Пример #1
0
        public new void CleanCheckpoints()
        {
            if (string.IsNullOrEmpty(SID) && (ID_Unsafe < 0 || AreaData.Areas.Count <= ID_Unsafe))
            {
                throw new Exception($"SaveData contains invalid AreaStats with no SID and out-of-range ID of {ID_Unsafe} / {AreaData.Areas.Count}");
            }

            AreaData area = AreaData.Get(ID);

            for (int i = 0; i < Modes.Length; i++)
            {
                AreaMode       areaMode       = (AreaMode)i;
                AreaModeStats  areaModeStats  = Modes[i];
                ModeProperties modeProperties = null;
                if (area.HasMode(areaMode))
                {
                    modeProperties = area.Mode[i];
                }

                HashSet <string> checkpoints = new HashSet <string>(areaModeStats.Checkpoints);
                areaModeStats.Checkpoints.Clear();

                if (modeProperties != null && modeProperties.Checkpoints != null)
                {
                    foreach (CheckpointData checkpointData in modeProperties.Checkpoints)
                    {
                        if (checkpoints.Contains(checkpointData.Level))
                        {
                            areaModeStats.Checkpoints.Add(checkpointData.Level);
                        }
                    }
                }
            }
        }
Пример #2
0
        public new void CleanCheckpoints()
        {
            AreaData area = AreaData.Get(ID);

            for (int i = 0; i < Modes.Length; i++)
            {
                AreaMode       areaMode       = (AreaMode)i;
                AreaModeStats  areaModeStats  = Modes[i];
                ModeProperties modeProperties = null;
                if (area.HasMode(areaMode))
                {
                    modeProperties = area.Mode[i];
                }

                HashSet <string> checkpoints = new HashSet <string>(areaModeStats.Checkpoints);
                areaModeStats.Checkpoints.Clear();

                if (modeProperties != null && modeProperties.Checkpoints != null)
                {
                    foreach (CheckpointData checkpointData in modeProperties.Checkpoints)
                    {
                        if (checkpoints.Contains(checkpointData.Level))
                        {
                            areaModeStats.Checkpoints.Add(checkpointData.Level);
                        }
                    }
                }
            }
        }
Пример #3
0
        private static HashSet <string> _GetCheckpoints(SaveData save, AreaKey area)
        {
            // TODO: Maybe switch back to using SaveData.GetCheckpoints in the future?

            if (Celeste.PlayMode == Celeste.PlayModes.Event)
            {
                return(new HashSet <string>());
            }

            HashSet <string> set;

            AreaData       areaData = AreaData.Areas[area.ID];
            ModeProperties mode     = areaData.Mode[(int)area.Mode];

            if (save.DebugMode || save.CheatMode)
            {
                set = new HashSet <string>();
                if (mode.Checkpoints != null)
                {
                    foreach (CheckpointData cp in mode.Checkpoints)
                    {
                        set.Add($"{(AreaData.Get(cp.GetArea()) ?? areaData).GetSID()}|{cp.Level}");
                    }
                }
                return(set);
            }

            AreaModeStats areaModeStats = save.Areas[area.ID].Modes[(int)area.Mode];

            set = areaModeStats.Checkpoints;

            // Perform the same "cleanup" as SaveData.GetCheckpoints, but copy the set when adding area SIDs.
            if (mode == null)
            {
                set.Clear();
                return(set);
            }

            set.RemoveWhere((string a) => !mode.Checkpoints.Any((CheckpointData b) => b.Level == a));
            AreaData[] subs = AreaData.Areas.Where(other =>
                                                   other.GetMeta()?.Parent == areaData.GetSID() &&
                                                   other.HasMode(area.Mode)
                                                   ).ToArray();
            return(new HashSet <string>(set.Select(s => {
                foreach (AreaData sub in subs)
                {
                    foreach (CheckpointData cp in sub.Mode[(int)area.Mode].Checkpoints)
                    {
                        if (cp.Level == s)
                        {
                            return $"{sub.GetSID()}|{s}";
                        }
                    }
                }
                return s;
            })));
        }
Пример #4
0
        public MapDataFixup(MapData map)
        {
            MapData  = map;
            AreaKey  = map.Area;
            AreaData = AreaData.Get(AreaKey);
            Mode     = AreaData.Mode[(int)AreaKey.Mode];

            foreach (EverestModule module in Everest._Modules)
            {
                module.PrepareMapDataProcessors(this);
            }
        }
Пример #5
0
    private void SetButtons(ModeProperties mp)
    {
        GameObject btnGo = Instantiate(_buttonPrefab);

        btnGo.transform.SetParent(_buttonsLayout.transform);
        btnGo.name = mp.mode.ToString();

        Button btn = btnGo.GetComponent <Button>();
        Image  img = btnGo.GetComponent <Image>();

        btn.onClick.AddListener(() => CurrentEditMode = mp.mode);
        img.sprite = mp.buttonSprite;
    }
Пример #6
0
 public static ModeProperties[] Convert(MapMetaModeProperties[] meta)
 {
     if (meta == null || meta.Length == 0)
     {
         return(null);
     }
     ModeProperties[] data = new ModeProperties[meta.Length];
     for (int i = 0; i < meta.Length; i++)
     {
         data[i] = meta[i]?.Convert();
     }
     return(data);
 }
Пример #7
0
        public MapDataFixup(MapData map)
        {
            MapData        = map;
            AreaKey        = map.Area;
            AreaData       = AreaData.Get(AreaKey);
            ParentAreaData = AreaDataExt.Get(AreaData.GetMeta()?.Parent) ?? AreaData;
            Mode           = AreaData.Mode[(int)AreaKey.Mode];
            ParentMode     = ParentAreaData.Mode.ElementAtOrDefault((int)AreaKey.Mode) ?? Mode;
            ParentMapData  = ParentMode?.MapData ?? map;

            foreach (EverestModule module in Everest._Modules)
            {
                module.PrepareMapDataProcessors(this);
            }
        }
Пример #8
0
        public void ApplyTo(AreaData area, AreaMode mode)
        {
            area.GetMeta().Modes[(int)mode] = this;
            ModeProperties props            = area.Mode[(int)mode];

            if (props != null)
            {
                props.AudioState  = AudioState?.Convert() ?? props.AudioState;
                props.Checkpoints = MapMeta.Convert(Checkpoints) ?? props.Checkpoints;
                props.IgnoreLevelAudioLayerData = IgnoreLevelAudioLayerData ?? props.IgnoreLevelAudioLayerData;
                props.Inventory = MapMeta.GetInventory(Inventory) ?? props.Inventory;
                props.Path      = Path ?? props.Path;
                props.PoemID    = PoemID ?? props.PoemID;
            }
            else
            {
                props = Convert();
            }
            area.Mode[(int)mode] = props;
        }
Пример #9
0
 private static int countTotalHeartGemsForMapsThatHaveHearts(LevelSetStats levelSetStats)
 {
     return(levelSetStats.AreasIncludingCeleste.Sum((AreaStats area) => {
         int totalHeartGems = 0;
         ModeProperties[] propertiesOfAllModes = AreaData.Get(area.GetSID())?.Mode ?? new ModeProperties[0];
         for (int i = 0; i < propertiesOfAllModes.Length && i < area.Modes.Length; i++)
         {
             if (area.Modes[i].HeartGem)
             {
                 // the crystal heart of this map/mode was collected, so check if it has one before counting it in.
                 ModeProperties modeProperties = propertiesOfAllModes[i];
                 if (modeProperties?.MapData != null && modeProperties.MapData.Area.Mode <= AreaMode.CSide)
                 {
                     totalHeartGems += (modeProperties.MapData.DetectedHeartGem ? 1 : 0);
                 }
             }
         }
         return totalHeartGems;
     }));
 }
Пример #10
0
        public new void Added(Scene scene)
        {
            orig_Added(scene);

            Session session = (scene as Level)?.Session;

            if (checkpoint != null || session == null || session.Area.GetLevelSet() == "Celeste")
            {
                return;
            }

            AreaKey        area = session.Area;
            ModeProperties mode = AreaData.Areas[area.ID].Mode[(int)area.Mode];

            if (mode.Checkpoints == null)
            {
                return;
            }

            HashSet <string> cps = SaveData.Instance.GetCheckpoints(area);
            CheckpointData   cp  = null;

            foreach (CheckpointData checkpointData2 in mode.Checkpoints)
            {
                bool flag2 = session.LevelFlags.Contains(checkpointData2.Level) && cps.Contains(checkpointData2.Level);
                if (flag2)
                {
                    cp = checkpointData2;
                }
            }

            string id = GetCheckpointPreviewName(area, cp?.Level);

            if (MTN.Checkpoints.Has(id))
            {
                checkpoint = MTN.Checkpoints[id];
            }
        }
Пример #11
0
        public new HashSet <string> GetCheckpoints(AreaKey area)
        {
            HashSet <string> checkpoints = orig_GetCheckpoints(area);

            if (Celeste.PlayMode == Celeste.PlayModes.Event ||
                DebugMode || CheatMode)
            {
                return(checkpoints);
            }

            // Remove any checkpoints which don't exist in the level.
            ModeProperties mode = AreaData.Get(area).Mode[(int)area.Mode];

            if (mode == null)
            {
                checkpoints.Clear();
            }
            else
            {
                checkpoints.RemoveWhere(a => !mode.Checkpoints.Any(b => b.Level == a));
            }
            return(checkpoints);
        }
Пример #12
0
        private void ProcessLevels(BinaryPacker.Element levels)
        {
            AreaData       area = AreaData.Get(Area);
            ModeProperties mode = area.Mode[(int)Area.Mode];

            // Mod levels are... different.

            // levels.Children.Sort((a, b) => a.Attr("name").Replace("lvl_", "").CompareTo(b.Attr("name").Replace("lvl_", "")));

            int checkpoint = 0;
            List <CheckpointData> checkpointsAuto = null;

            if (mode.Checkpoints == null)
            {
                checkpointsAuto = new List <CheckpointData>();
            }

            int strawberry             = 0;
            int strawberryInCheckpoint = 0;

            if (levels.Children != null)
            {
                foreach (BinaryPacker.Element level in levels.Children)
                {
                    string[] levelTags = level.Attr("name").Split(':');
                    string   levelName = levelTags[0];
                    if (levelName.StartsWith("lvl_"))
                    {
                        levelName = levelName.Substring(4);
                    }
                    level.SetAttr("name", "lvl_" + levelName); // lvl_ was optional before Celeste 1.2.5.0 made it mandatory.

                    BinaryPacker.Element entities = level.Children.FirstOrDefault(el => el.Name == "entities");
                    BinaryPacker.Element triggers = level.Children.FirstOrDefault(el => el.Name == "triggers");

                    // Celeste 1.2.5.0 optimizes BinaryPacker, which causes some issues.
                    // Let's "unoptimize" entities and triggers.
                    if (entities == null)
                    {
                        level.Children.Add(entities = new BinaryPacker.Element {
                            Name = "entities"
                        });
                    }
                    if (entities.Children == null)
                    {
                        entities.Children = new List <BinaryPacker.Element>();
                    }

                    if (triggers == null)
                    {
                        level.Children.Add(triggers = new BinaryPacker.Element {
                            Name = "triggers"
                        });
                    }
                    if (triggers.Children == null)
                    {
                        triggers.Children = new List <BinaryPacker.Element>();
                    }

                    if (levelTags.Contains("checkpoint") || levelTags.Contains("cp"))
                    {
                        entities.Children.Add(new BinaryPacker.Element {
                            Name       = "checkpoint",
                            Attributes = new Dictionary <string, object>()
                            {
                                { "x", "0" },
                                { "y", "0" }
                            }
                        });
                    }

                    if (level.AttrBool("space"))
                    {
                        if (level.AttrBool("spaceSkipWrap") || levelTags.Contains("nospacewrap") || levelTags.Contains("nsw"))
                        {
                            entities.Children.Add(new BinaryPacker.Element {
                                Name = "everest/spaceControllerBlocker"
                            });
                        }
                        if (level.AttrBool("spaceSkipGravity") || levelTags.Contains("nospacegravity") || levelTags.Contains("nsg"))
                        {
                            entities.Children.Add(new BinaryPacker.Element {
                                Name = "everest/spaceController"
                            });
                            level.SetAttr("space", false);
                        }

                        if (!levelTags.Contains("nospacefix") && !levelTags.Contains("nsf") &&
                            !triggers.Children.Any(el => el.Name == "cameraTargetTrigger") &&
                            !entities.Children.Any(el => el.Name == "everest/spaceControllerBlocker"))
                        {
                            // Camera centers tile-perfectly on uneven heights.
                            int heightForCenter = level.AttrInt("height");
                            heightForCenter /= 8;
                            if (heightForCenter % 2 == 0)
                            {
                                heightForCenter--;
                            }
                            heightForCenter *= 8;

                            triggers.Children.Add(new BinaryPacker.Element {
                                Name       = "cameraTargetTrigger",
                                Attributes = new Dictionary <string, object>()
                                {
                                    { "x", 0f },
                                    { "y", 0f },
                                    { "width", level.AttrInt("width") },
                                    { "height", level.AttrInt("height") },
                                    { "yOnly", true },
                                    { "lerpStrength", 1f }
                                },
                                Children = new List <BinaryPacker.Element>()
                                {
                                    new BinaryPacker.Element {
                                        Attributes = new Dictionary <string, object>()
                                        {
                                            { "x", 160f },
                                            { "y", heightForCenter / 2f }
                                        }
                                    }
                                }
                            });
                        }
                    }

                    foreach (BinaryPacker.Element levelChild in level.Children)
                    {
                        switch (levelChild.Name)
                        {
                        case "entities":
                            foreach (BinaryPacker.Element entity in levelChild.Children)
                            {
                                switch (entity.Name)
                                {
                                case "checkpoint":
                                    if (checkpointsAuto != null)
                                    {
                                        CheckpointData c = new CheckpointData(
                                            levelName,
                                            (area.GetSID() + "_" + levelName).DialogKeyify(),
                                            MapMeta.GetInventory(entity.Attr("inventory")),
                                            entity.Attr("dreaming") == "" ? area.Dreaming : entity.AttrBool("dreaming"),
                                            null
                                            );
                                        int id = entity.AttrInt("checkpointID", -1);
                                        if (id == -1)
                                        {
                                            checkpointsAuto.Add(c);
                                        }
                                        else
                                        {
                                            while (checkpointsAuto.Count <= id)
                                            {
                                                checkpointsAuto.Add(null);
                                            }
                                            checkpointsAuto[id] = c;
                                        }
                                    }
                                    checkpoint++;
                                    strawberryInCheckpoint = 0;
                                    break;

                                case "cassette":
                                    if (area.CassetteCheckpointIndex == 0)
                                    {
                                        area.CassetteCheckpointIndex = checkpoint;
                                    }
                                    break;

                                case "strawberry":
                                    if (entity.AttrInt("checkpointID", -1) == -1)
                                    {
                                        entity.SetAttr("checkpointID", checkpoint);
                                    }
                                    if (entity.AttrInt("order", -1) == -1)
                                    {
                                        entity.SetAttr("order", strawberryInCheckpoint);
                                    }
                                    strawberry++;
                                    strawberryInCheckpoint++;
                                    break;
                                }
                            }
                            break;
                        }
                    }
                }
            }

            if (mode.Checkpoints == null)
            {
                mode.Checkpoints = checkpointsAuto.Where(c => c != null).ToArray();
            }
        }
Пример #13
0
        public static new void Load()
        {
            orig_Load();

            // assign SIDs and CheckpointData.Area for vanilla maps.
            foreach (AreaData area in Areas)
            {
                area.SetSID("Celeste/" + area.Mode[0].Path);

                for (int modeId = 0; modeId < area.Mode.Length; modeId++)
                {
                    ModeProperties mode = area.Mode[modeId];
                    if (mode?.Checkpoints == null)
                    {
                        continue;
                    }

                    foreach (CheckpointData checkpoint in mode.Checkpoints)
                    {
                        checkpoint.SetArea(area.ToKey((AreaMode)modeId));
                    }
                }
            }

            // Separate array as we sort it afterwards.
            List <AreaData> modAreas = new List <AreaData>();

            lock (Everest.Content.Map) {
                foreach (ModAsset asset in Everest.Content.Map.Values.Where(asset => asset.Type == typeof(AssetTypeMap)))
                {
                    string path = asset.PathVirtual.Substring(5);

                    AreaData area = new AreaData();

                    // Default values.

                    area.SetSID(path);
                    area.Name = path;
                    area.Icon = "areas/" + path.ToLowerInvariant();
                    if (!GFX.Gui.Has(area.Icon))
                    {
                        area.Icon = "areas/null";
                    }

                    area.Interlude    = false;
                    area.CanFullClear = true;

                    area.TitleBaseColor   = Calc.HexToColor("6c7c81");
                    area.TitleAccentColor = Calc.HexToColor("2f344b");
                    area.TitleTextColor   = Color.White;

                    area.IntroType = Player.IntroTypes.WakeUp;

                    area.Dreaming   = false;
                    area.ColorGrade = null;

                    area.Mode = new ModeProperties[] {
                        new ModeProperties {
                            Inventory  = PlayerInventory.Default,
                            AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main)
                        }
                    };

                    area.Wipe = (Scene scene, bool wipeIn, Action onComplete)
                                => new AngledWipe(scene, wipeIn, onComplete);

                    area.DarknessAlpha = 0.05f;
                    area.BloomBase     = 0f;
                    area.BloomStrength = 1f;

                    area.Jumpthru = "wood";

                    area.CassseteNoteColor = Calc.HexToColor("33a9ee");
                    area.CassetteSong      = SFX.cas_01_forsaken_city;

                    // Custom values can be set via the MapMeta.
                    MapMeta meta = new MapMeta();
                    meta.ApplyTo(area);
                    MapMeta metaLoaded = asset.GetMeta <MapMeta>();
                    if (metaLoaded != null)
                    {
                        area.SetMeta(null);
                        metaLoaded.ApplyTo(area);
                        meta = metaLoaded;
                    }

                    if (string.IsNullOrEmpty(area.Mode[0].Path))
                    {
                        area.Mode[0].Path = asset.PathVirtual.Substring(5);
                    }

                    // Some of the game's code checks for [1] / [2] explicitly.
                    // Let's just provide null modes to fill any gaps.
                    meta.Modes = meta.Modes ?? new MapMetaModeProperties[3];
                    if (meta.Modes.Length < 3)
                    {
                        MapMetaModeProperties[] larger = new MapMetaModeProperties[3];
                        for (int i = 0; i < meta.Modes.Length; i++)
                        {
                            larger[i] = meta.Modes[i];
                        }
                        meta.Modes = larger;
                    }
                    if (area.Mode.Length < 3)
                    {
                        ModeProperties[] larger = new ModeProperties[3];
                        for (int i = 0; i < area.Mode.Length; i++)
                        {
                            larger[i] = area.Mode[i];
                        }
                        area.Mode = larger;
                    }

                    // Celeste levelset always appears first.
                    if (area.GetLevelSet() == "Celeste")
                    {
                        Areas.Add(area);
                    }
                    else
                    {
                        modAreas.Add(area);
                    }

                    // Some special handling.
                    area.OnLevelBegin = (level) => {
                        MapMeta levelMeta = AreaData.Get(level.Session).GetMeta();
                        MapMetaModeProperties levelMetaMode = level.Session.MapData.GetMeta();

                        if (levelMetaMode?.SeekerSlowdown ?? false)
                        {
                            level.Add(new SeekerEffectsController());
                        }
                    };
                }
            }


            // Merge modAreas into Areas.
            Areas.AddRange(modAreas);

            // Find duplicates and remove any earlier copies.
            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area       = Areas[i];
                int      otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID());
                if (otherIndex < i)
                {
                    Areas[otherIndex] = area;
                    Areas.RemoveAt(i);
                    i--;
                }
            }

            // Sort areas.
            Areas.Sort(AreaComparison);

            // Remove AreaDatas which are now a mode of another AreaData.
            // This can happen late as the map data (.bin) can contain additional metadata.
            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area       = Areas[i];
                string   path       = area.Mode[0].Path;
                int      otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == path));
                if (otherIndex != -1 && otherIndex != i)
                {
                    Areas.RemoveAt(i);
                    i--;
                    continue;
                }

                int?     order;
                AreaMode side;
                string   name;
                ParseName(path, out order, out side, out name);

                // Also check for .bins possibly belonging to A side .bins by their path and lack of existing modes.
                for (int ii = 0; ii < Areas.Count; ii++)
                {
                    AreaData other = Areas[ii];
                    int?     otherOrder;
                    AreaMode otherSide;
                    string   otherName;
                    ParseName(other.Mode[0].Path, out otherOrder, out otherSide, out otherName);

                    if (area.GetLevelSet() == other.GetLevelSet() && order == otherOrder && name == otherName && side != otherSide &&
                        !other.HasMode(side))
                    {
                        if (other.Mode[(int)side] == null)
                        {
                            other.Mode[(int)side] = new ModeProperties {
                                Inventory  = PlayerInventory.Default,
                                AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main)
                            }
                        }
                        ;
                        other.Mode[(int)side].Path = path;
                        Areas.RemoveAt(i);
                        i--;
                        break;
                    }
                }
            }

            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area = Areas[i];
                area.ID = i;

                // Clean up non-existing modes.
                int modei = 0;
                for (; modei < area.Mode.Length; modei++)
                {
                    ModeProperties mode = area.Mode[modei];
                    if (mode == null || string.IsNullOrEmpty(mode.Path))
                    {
                        break;
                    }
                }
                Array.Resize(ref area.Mode, modei);

                Logger.Log("AreaData", $"{i}: {area.GetSID()} - {area.Mode.Length} sides");

                // Update old MapData areas and load any new areas.

                // Add the A side MapData or update its area key.
                if (area.Mode[0].MapData != null)
                {
                    area.Mode[0].MapData.Area = area.ToKey();
                }
                else
                {
                    area.Mode[0].MapData = new MapData(area.ToKey());
                }

                if (area.IsInterludeUnsafe())
                {
                    continue;
                }

                // A and (some) B sides have PoemIDs. Can be overridden via empty PoemID.
                if (area.Mode[0].PoemID == null)
                {
                    area.Mode[0].PoemID = area.GetSID().DialogKeyify() + "_A";
                }
                if (area.Mode.Length > 1 &&
                    area.Mode[1] != null &&
                    area.Mode[1].PoemID == null)
                {
                    area.Mode[1].PoemID = area.GetSID().DialogKeyify() + "_B";
                }

                // Update all other existing mode's area keys.
                for (int mode = 1; mode < area.Mode.Length; mode++)
                {
                    if (area.Mode[mode] == null)
                    {
                        continue;
                    }
                    if (area.Mode[mode].MapData != null)
                    {
                        area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode);
                    }
                    else
                    {
                        area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode));
                    }
                }
            }

            // Load custom mountains
            // This needs to be done after areas are loaded because it depends on the MapMeta
            MTNExt.LoadMod();
            MTNExt.LoadModData();
        }
Пример #14
0
        // Mods can't access patch_ classes directly.
        // We thus expose any new members through extensions.

        public static MapMeta GetMapMeta(this ModeProperties self)
        => ((patch_ModeProperties)self).MapMeta;
Пример #15
0
        public static new void Load()
        {
            orig_Load();

            foreach (AreaData area in Areas)
            {
                area.SetSID("Celeste/" + area.Mode[0].Path);
            }

            // Separate array as we sort it afterwards.
            List <AreaData> modAreas = new List <AreaData>();

            foreach (ModAsset asset in Everest.Content.ListMaps)
            {
                string  path = asset.PathMapped.Substring(5);
                MapMeta meta = asset.GetMeta <MapMeta>();

                AreaData area = new AreaData();

                // Default values.

                area.SetSID(path);

                area.Name = path;
                area.Icon = "areas/" + path.ToLowerInvariant();
                if (!GFX.Gui.Has(area.Icon))
                {
                    area.Icon = "areas/null";
                }

                area.TitleBaseColor   = Calc.HexToColor("6c7c81");
                area.TitleAccentColor = Calc.HexToColor("2f344b");
                area.TitleTextColor   = Color.White;

                area.IntroType = Player.IntroTypes.WakeUp;

                area.Dreaming   = false;
                area.ColorGrade = null;

                area.Mode = new ModeProperties[] {
                    new ModeProperties {
                        Path       = asset.PathMapped.Substring(5),
                        Inventory  = PlayerInventory.Default,
                        AudioState = new AudioState(Sfxs.music_city, Sfxs.env_amb_00_main)
                    }
                };

                area.Wipe = (Scene scene, bool wipeIn, Action onComplete)
                            => new AngledWipe(scene, wipeIn, onComplete);

                area.DarknessAlpha = 0.05f;
                area.BloomBase     = 0f;
                area.BloomStrength = 1f;

                area.Jumpthru = "wood";

                area.CassseteNoteColor = Calc.HexToColor("33a9ee");
                area.CassetteSong      = Sfxs.cas_01_forsaken_city;

                // Custom values.
                if (meta != null)
                {
                    if (!string.IsNullOrEmpty(meta.Name))
                    {
                        area.Name = meta.Name;
                    }

                    if (!string.IsNullOrEmpty(meta.SID))
                    {
                        area.SetSID(meta.SID);
                    }

                    if (!string.IsNullOrEmpty(meta.Icon) && GFX.Gui.Has(meta.Icon))
                    {
                        area.Icon = meta.Icon;
                    }

                    area.Interlude = meta.Interlude;
                    if (!string.IsNullOrEmpty(meta.CompleteScreenName))
                    {
                        area.CompleteScreenName = meta.CompleteScreenName;
                    }

                    area.CassetteCheckpointIndex = meta.CassetteCheckpointIndex;

                    if (!string.IsNullOrEmpty(meta.TitleBaseColor))
                    {
                        area.TitleBaseColor = Calc.HexToColor(meta.TitleBaseColor);
                    }
                    if (!string.IsNullOrEmpty(meta.TitleAccentColor))
                    {
                        area.TitleAccentColor = Calc.HexToColor(meta.TitleAccentColor);
                    }
                    if (!string.IsNullOrEmpty(meta.TitleTextColor))
                    {
                        area.TitleTextColor = Calc.HexToColor(meta.TitleTextColor);
                    }

                    area.IntroType = meta.IntroType;

                    area.Dreaming = meta.Dreaming;
                    if (!string.IsNullOrEmpty(meta.ColorGrade))
                    {
                        area.ColorGrade = meta.ColorGrade;
                    }

                    area.Mode = MapMeta.Convert(meta.Modes) ?? area.Mode;

                    if (!string.IsNullOrEmpty(meta.Wipe))
                    {
                        Type            type = Assembly.GetEntryAssembly().GetType(meta.Wipe);
                        ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) });
                        if (type != null && ctor != null)
                        {
                            area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete });
                        }
                    }

                    area.DarknessAlpha = meta.DarknessAlpha;
                    area.BloomBase     = meta.BloomBase;
                    area.BloomStrength = meta.BloomStrength;

                    if (!string.IsNullOrEmpty(meta.Jumpthru))
                    {
                        area.Jumpthru = meta.Jumpthru;
                    }

                    if (!string.IsNullOrEmpty(meta.CassetteNoteColor))
                    {
                        area.CassseteNoteColor = Calc.HexToColor(meta.CassetteNoteColor);
                    }
                    if (!string.IsNullOrEmpty(meta.CassetteSong))
                    {
                        area.CassetteSong = meta.CassetteSong;
                    }

                    area.MountainIdle   = meta.Mountain?.Idle?.Convert() ?? area.MountainIdle;
                    area.MountainSelect = meta.Mountain?.Select?.Convert() ?? area.MountainSelect;
                    area.MountainZoom   = meta.Mountain?.Zoom?.Convert() ?? area.MountainZoom;
                    area.MountainCursor = meta.Mountain?.Cursor?.ToVector3() ?? area.MountainCursor;
                    area.MountainState  = meta.Mountain?.State ?? area.MountainState;

                    area.SetCompleteScreenMeta(meta.CompleteScreen);
                }

                // Some of the game's code checks for [1] / [2] explicitly.
                // Let's just provide null modes to fill any gaps.
                if (area.Mode.Length < 3)
                {
                    ModeProperties[] larger = new ModeProperties[3];
                    for (int i = 0; i < area.Mode.Length; i++)
                    {
                        larger[i] = area.Mode[i];
                    }
                    area.Mode = larger;
                }

                // Celeste levelset always appears first.
                if (area.GetLevelSet() == "Celeste")
                {
                    Areas.Add(area);
                }
                else
                {
                    modAreas.Add(area);
                }
            }


            // Sort and merge modAreas into Areas. Makes for easier levelset handling.
            Areas.Sort(AreaComparison);
            modAreas.Sort(AreaComparison);
            Areas.AddRange(modAreas);

            // Find duplicates and remove the earlier copy.
            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area       = Areas[i];
                int      otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID());
                if (otherIndex < i)
                {
                    Areas[otherIndex] = area;
                    Areas.RemoveAt(i);
                    i--;
                }
            }

            // Remove AreaDatas which are now a mode of another AreaData.
            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area       = Areas[i];
                int      otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == area.Mode[0].Path));
                if (otherIndex != -1 && otherIndex != i)
                {
                    Areas.RemoveAt(i);
                    i--;
                }
            }

            // Update old MapData areas and load any new areas.
            for (int i = 0; i < Areas.Count; i++)
            {
                AreaData area = Areas[i];
                area.ID = i;
                if (area.Mode[0].MapData != null)
                {
                    area.Mode[0].MapData.Area = area.ToKey();
                }
                else
                {
                    area.Mode[0].MapData = new MapData(area.ToKey());
                }
                if (area.Interlude)
                {
                    continue;
                }
                for (int mode = 1; mode < area.Mode.Length; mode++)
                {
                    if (area.Mode[mode] == null)
                    {
                        continue;
                    }
                    if (area.Mode[mode].MapData != null)
                    {
                        area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode);
                    }
                    else
                    {
                        area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode));
                    }
                }
            }
        }
Пример #16
0
        [PatchMapDataLoader] // Manually manipulate the method via MonoModRules
        private void Load()
        {
            // reset those fields to prevent them from stacking up when reloading the map.
            DetectedStrawberries  = 0;
            DetectedHeartGem      = false;
            DetectedRemixNotes    = false;
            Goldenberries         = new List <EntityData>();
            DashlessGoldenberries = new List <EntityData>();
            DetectedCassette      = false;
            DetectedStrawberriesIncludingUntracked = 0;

            try {
                orig_Load();

                foreach (LevelData level in Levels)
                {
                    foreach (EntityData entity in level.Entities)
                    {
                        if (entity.Name == "memorialTextController") // aka "dashless golden"
                        {
                            DashlessGoldenberries.Add(entity);
                        }
                    }
                }

                AreaData       area       = AreaData.Get(Area);
                AreaData       parentArea = AreaDataExt.Get(area.GetMeta()?.Parent);
                ModeProperties parentMode = parentArea?.Mode?.ElementAtOrDefault((int)Area.Mode);
                if (parentMode != null)
                {
                    MapData parentMapData = parentMode.MapData;
                    if (parentMapData == null)
                    {
                        Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed auto-assigning data from {Area} to its unloaded parent");
                        return;
                    }

                    parentMapData.Strawberries.AddRange(Strawberries);

                    // Recount everything berry-related for the parent map data, just like in orig_Load.
                    parentMode.TotalStrawberries        = 0;
                    parentMode.StartStrawberries        = 0;
                    parentMode.StrawberriesByCheckpoint = new EntityData[10, 25];

                    for (int i = 0; parentMode.Checkpoints != null && i < parentMode.Checkpoints.Length; i++)
                    {
                        if (parentMode.Checkpoints[i] != null)
                        {
                            parentMode.Checkpoints[i].Strawberries = 0;
                        }
                    }

                    foreach (EntityData entity in parentMapData.Strawberries)
                    {
                        if (!entity.Bool("moon"))
                        {
                            int checkpointID = entity.Int("checkpointIDParented", entity.Int("checkpointID"));
                            int order        = entity.Int("order");

                            if (_GrowAndGet(ref parentMode.StrawberriesByCheckpoint, checkpointID, order) == null)
                            {
                                parentMode.StrawberriesByCheckpoint[checkpointID, order] = entity;
                            }

                            if (checkpointID == 0)
                            {
                                parentMode.StartStrawberries++;
                            }
                            else if (parentMode.Checkpoints != null)
                            {
                                parentMode.Checkpoints[checkpointID - 1].Strawberries++;
                            }

                            parentMode.TotalStrawberries++;
                        }
                    }
                }
            } catch (Exception e) {
                Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed loading MapData {Area}");
                e.LogDetailed();
            }
        }
Пример #17
0
 public static void SetMapMeta(this ModeProperties self, MapMeta value)
 => ((patch_ModeProperties)self).MapMeta = value;