예제 #1
0
        private bool FixReturnFromAltSide(On.Celeste.OuiChapterPanel.orig_IsStart orig, OuiChapterPanel self, Overworld overworld, Overworld.StartMode start)
        {
            AreaData newArea = null;
            AreaData old;

            if (start == Overworld.StartMode.AreaComplete || start == Overworld.StartMode.AreaQuit)
            {
                AreaData area = AreaData.Get(SaveData.Instance.LastArea.ID);
                old = area;
                var meta = GetMetaForAreaData(area);
                if (meta?.AltSideData.IsAltSide ?? false)
                {
                    area = AreaData.Get(meta.AltSideData.For) ?? area;
                    if (area != null)
                    {
                        newArea = area;
                        SaveData.Instance.LastArea.ID = area.ID;
                        int returningSide = 0;                         //last unlocked mode
                        if (!area.Interlude_Safe && area.HasMode(AreaMode.BSide) && (SaveData.Instance.Areas_Safe[area.ID].Cassette || SaveData.Instance.DebugMode || SaveData.Instance.CheatMode))
                        {
                            returningSide++;
                        }
                        if (!area.Interlude_Safe && area.HasMode(AreaMode.CSide) && SaveData.Instance.UnlockedModes >= 3)
                        {
                            returningSide++;
                        }

                        var asideAltSideMeta = GetMetaForAreaData(area);
                        foreach (var mode in asideAltSideMeta.Sides)
                        {
                            if (!mode.OverrideVanillaSideData)
                            {
                                returningSide++;
                                if (mode.Map.Equals(area.GetSID()))
                                {
                                    break;
                                }
                            }
                        }

                        returningAltSide = returningSide;
                        SaveData.Instance.LastArea_Safe.ID = old.ID;
                    }
                }
            }
            var ret = orig(self, overworld, start);

            if (newArea != null)
            {
                //self.Data = newArea;
                SaveData.Instance.LastArea_Safe.ID = newArea.ID;
                shouldResetStats = false;
                resetMethod.Invoke(self, new object[] { });
                shouldResetStats = true;
            }
            returningAltSide = -1;

            return(ret);
        }
예제 #2
0
        private static int AreaComparison(AreaData a, AreaData b)
        {
            string aSet = a.GetLevelSet();
            string aSID = a.GetSID();
            string bSet = b.GetLevelSet();
            string bSID = b.GetSID();

            // Celeste appears before everything else.
            if (aSet == "Celeste" && bSet != "Celeste")
            {
                return(-1);
            }
            if (aSet != "Celeste" && bSet == "Celeste")
            {
                return(1);
            }

            // Uncategorized appears after everything else.
            if (string.IsNullOrEmpty(aSet) && !string.IsNullOrEmpty(bSet))
            {
                return(1);
            }
            if (!string.IsNullOrEmpty(aSet) && string.IsNullOrEmpty(bSet))
            {
                return(-1);
            }

            // Compare level sets alphabetically.
            if (aSet != bSet)
            {
                return(string.Compare(aSet, bSet));
            }

            int?     aOrder;
            AreaMode aSide;
            string   aName;

            ParseName(aSID, out aOrder, out aSide, out aName);

            int?     bOrder;
            AreaMode bSide;
            string   bName;

            ParseName(bSID, out bOrder, out bSide, out bName);

            if (aOrder != null && bOrder != null &&
                aOrder.Value != bOrder.Value)
            {
                return(aOrder.Value - bOrder.Value);
            }

            if (aSide != bSide)
            {
                return(aSide - bSide);
            }

            return(string.Compare(aName, bName));
        }
예제 #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 static RandoConfigFile Load(AreaData area)
        {
            String fullPath = "Config/" + area.GetSID() + ".rando";

            Logger.Log("randomizer", $"Loading config from {fullPath}");
            if (!Everest.Content.TryGet(fullPath, out ModAsset asset))
            {
                Logger.Log("randomizer", "...not found");
                return(null);
            }
            else
            {
                using (StreamReader reader = new StreamReader(asset.Stream)) {
                    try {
                        return(YamlHelper.Deserializer.Deserialize <RandoConfigFile>(reader));
                    } catch (YamlException e) {
                        throw new Exception($"Error parsing {area.GetSID()}.rando: {e.Message}");
                    }
                }
            }
        }
예제 #5
0
 private static int AreaComparison(AreaData a, AreaData b)
 {
     if (string.IsNullOrEmpty(a.GetLevelSet()) && !string.IsNullOrEmpty(b.GetLevelSet()))
     {
         return(1);
     }
     if (!string.IsNullOrEmpty(a.GetLevelSet()) && string.IsNullOrEmpty(b.GetLevelSet()))
     {
         return(-1);
     }
     return(string.Compare(a.GetSID(), b.GetSID()));
 }
예제 #6
0
        public static RandoConfigFile Load(AreaData area)
        {
            String fullpath = "Config/" + area.GetSID() + ".rando";

            if (!Everest.Content.TryGet(fullpath, out ModAsset asset))
            {
                return(null);
            }
            else
            {
                try {
                    using (StreamReader reader = new StreamReader(asset.Stream)) {
                        return(YamlHelper.Deserializer.Deserialize <RandoConfigFile>(reader));
                    }
                } catch (Exception e) {
                    Logger.Log("randomizer", $"Failed loading config for area {area}: {e.Message}");
                    return(null);
                }
            }
        }
예제 #7
0
파일: MapData.cs 프로젝트: leo60228/Everest
        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();
            }
        }
예제 #8
0
파일: AreaData.cs 프로젝트: jakobrs/Everest
 /// <summary>
 /// Get an AreaKey for this area.
 /// </summary>
 public static AreaKey ToKey(this AreaData self, AreaMode mode = AreaMode.Normal)
 => new AreaKey(self.ID, mode).SetSID(self.GetSID());
예제 #9
0
파일: AreaData.cs 프로젝트: jakobrs/Everest
        private static int AreaComparison(AreaData a, AreaData b)
        {
            string  aSet  = a.GetLevelSet();
            string  aSID  = a.GetSID();
            MapMeta aMeta = a.GetMeta();
            string  bSet  = b.GetLevelSet();
            string  bSID  = b.GetSID();
            MapMeta bMeta = b.GetMeta();

            // Celeste appears before everything else.
            if (aSet == "Celeste" && bSet != "Celeste")
            {
                return(-1);
            }
            if (aSet != "Celeste" && bSet == "Celeste")
            {
                return(1);
            }

            // Uncategorized appears after everything else.
            if (string.IsNullOrEmpty(aSet) && !string.IsNullOrEmpty(bSet))
            {
                return(1);
            }
            if (!string.IsNullOrEmpty(aSet) && string.IsNullOrEmpty(bSet))
            {
                return(-1);
            }

            // Compare level sets alphabetically.
            if (aSet != bSet)
            {
                return(string.Compare(aSet, bSet));
            }

            // Put "parented" levels at the end.
            if (!string.IsNullOrEmpty(aMeta?.Parent) && string.IsNullOrEmpty(bMeta?.Parent))
            {
                return(1);
            }
            if (string.IsNullOrEmpty(aMeta?.Parent) && !string.IsNullOrEmpty(bMeta?.Parent))
            {
                return(-1);
            }

            int?     aOrder;
            AreaMode aSide;
            string   aName;

            ParseName(aSID, out aOrder, out aSide, out aName);

            int?     bOrder;
            AreaMode bSide;
            string   bName;

            ParseName(bSID, out bOrder, out bSide, out bName);

            // put the "unordered" levels at the end. (Farewell is one of them.)
            if (aOrder != null && bOrder == null)
            {
                return(-1);
            }

            if (aOrder == null && bOrder != null)
            {
                return(1);
            }

            // order the rest by order, then by name, then by side
            if (aOrder != null && bOrder != null && aOrder.Value != bOrder.Value)
            {
                return(aOrder.Value - bOrder.Value);
            }

            if (aName != bName)
            {
                return(string.Compare(aName, bName));
            }

            if (aSide != bSide)
            {
                return(aSide - bSide);
            }

            // everything is the same: this is the same level
            return(0);
        }
예제 #10
0
파일: AreaData.cs 프로젝트: jakobrs/Everest
        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();
        }
예제 #11
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));
                    }
                }
            }
        }
        public static List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet)
        {
            List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>();
            int rowCount = 0;
            OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet);

            pages.Add(currentPage);

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allMapsDone = true;

            bool allLevelsDone       = true;
            bool allSpeedBerriesDone = true;

            string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0";

            foreach (AreaStats item in SaveData.Instance.Areas_Safe)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (!areaData.Interlude_Safe)
                {
                    if (LobbyHelper.IsHeartSide(areaData.GetSID()))
                    {
                        if (allMapsDone || item.TotalTimePlayed > 0)
                        {
                            // add a separator, like the one between regular maps and Farewell
                            currentPage.table.AddRow();
                        }
                        else
                        {
                            // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now.
                            continue;
                        }
                    }

                    string strawberryText = null;
                    if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0)
                    {
                        strawberryText = item.TotalStrawberries.ToString();
                        if (item.Modes[0].Completed)
                        {
                            strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries;
                        }
                    }
                    else
                    {
                        strawberryText = "-";
                    }

                    Row row = currentPage.table.AddRow()
                              .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor))
                              .Add(null)
                              .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                              .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor));

                    if (item.TotalTimePlayed > 0)
                    {
                        row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }


                    AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                    if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) &&
                        levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) &&
                        stats.Modes[0].Strawberries.Contains(berryID))
                    {
                        // silver berry was obtained!
                        row.Add(new IconCell("CollabUtils2/silver_strawberry"));
                    }
                    else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level)))
                    {
                        // golden berry was obtained!
                        row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                    }
                    else if (item.Modes[0].SingleRunCompleted)
                    {
                        row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                        sumOfBestDeaths += item.Modes[0].BestDeaths;
                    }
                    else
                    {
                        // the player didn't ever do a single run.
                        row.Add(new IconCell("dot"));
                        allLevelsDone = false;
                    }

                    if (item.TotalTimePlayed > 0)
                    {
                        row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (CollabModule.Instance.Settings.BestTimeToDisplayInJournal == CollabSettings.BestTimeInJournal.SpeedBerry)
                    {
                        if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) &&
                            CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB))
                        {
                            row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB)));
                            row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB)));
                            sumOfBestTimes += speedBerryPB;
                        }
                        else
                        {
                            row.Add(new IconCell("dot")).Add(null);
                            allSpeedBerriesDone = false;
                        }
                    }
                    else
                    {
                        if (item.Modes[0].BestTime > 0f)
                        {
                            row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null);
                            sumOfBestTimes += item.Modes[0].BestTime;
                        }
                        else
                        {
                            row.Add(new IconCell("dot")).Add(null);
                            allSpeedBerriesDone = false;
                        }
                    }

                    totalStrawberries += item.TotalStrawberries;
                    totalDeaths       += item.Modes[0].Deaths;
                    totalTime         += item.TotalTimePlayed;

                    if (!item.Modes[0].HeartGem)
                    {
                        allMapsDone = false;
                    }

                    rowCount++;
                    if (rowCount > 11)
                    {
                        // split the next zones into another page.
                        rowCount    = 0;
                        currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet);
                        pages.Add(currentPage);
                    }
                }
            }

            if (currentPage.table.Rows > 1)
            {
                currentPage.table.AddRow();
                Row totalsRow = currentPage.table.AddRow()
                                .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null)
                                .Add(null)
                                .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                                .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null);

                for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
                {
                    totalsRow.Add(null);
                }
                totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor));
                currentPage.table.AddRow();
            }

            return(pages);
        }
예제 #13
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            var  lastarea = AreaData.Areas[AreaData.Areas.Count - 1];
            var  newID    = AreaData.Areas.Count;
            bool secondVerseSameAsTheFirst = lastarea.GetSID().StartsWith("randomizer/");

            if (secondVerseSameAsTheFirst)
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, true, false) :
                                    settings.Dashes == NumDashes.One ? PlayerInventory.Default :
                                    PlayerInventory.CH6End,
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };
            var dyn = new DynData <AreaData>(newArea);

            dyn.Set <RandoSettings>("RandoSettings", settings.Copy());

            newArea.SetSID($"randomizer/{newArea.Name}");
            if (secondVerseSameAsTheFirst)
            {
                AreaData.Areas[AreaData.Areas.Count - 1] = newArea;
                // invalidate the MapEditor area key cache, as it will erroniously see a cache hit
                typeof(Editor.MapEditor).GetField("area", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, AreaKey.None);
            }
            else
            {
                // avert race condition
                RandoModule.AreaHandoff = newArea;
                while (RandoModule.AreaHandoff != null)
                {
                    Thread.Sleep(10);
                }
            }

            var key = new AreaKey(newArea.ID);

            var r = new RandoLogic(settings, key);

            newArea.Mode[0].MapData    = r.MakeMap();
            newArea.Wipe               = r.PickWipe();
            newArea.CompleteScreenName = r.PickCompleteScreen();
            newArea.CassetteSong       = r.PickCassetteAudio();
            newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
            r.RandomizeDialog();

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }
예제 #14
0
        public OuiJournalCollabProgressInOverworld(OuiJournal journal)
            : base(journal)
        {
            bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn();

            PageTexture = "page";
            table       = new Table()
                          .AddColumn(new TextCell(Dialog.Clean("journal_progress"), new Vector2(0f, 0.5f), 1f, Color.Black * 0.7f, 420f))
                          .AddColumn(new EmptyCell(20f))
                          .AddColumn(new EmptyCell(64f))
                          .AddColumn(new IconCell("strawberry", 150f))
                          .AddColumn(new IconCell("skullblue", 100f))
                          .AddColumn(new IconCell("CollabUtils2MinDeaths/SpringCollab2020/1-Beginner", 100f));

            if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
            {
                table.AddColumn(new IconCell("max480/DashCountMod/dashes", 80f));
            }

            table
            .AddColumn(new IconCell("time", 220f))
            .AddColumn(new IconCell("CollabUtils2/speed_berry_pbs_heading", 220f))
            .AddColumn(new EmptyCell(30f));

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            int  sumOfBestDashes   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allLevelsDone               = true;
            bool allSpeedBerriesDone         = true;
            bool allMapsCompletedInSingleRun = true;

            foreach (AreaStats item in SaveData.Instance.Areas_Safe)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (areaData.GetLevelSet() == SaveData.Instance.LevelSet)
                {
                    string        lobbyMapLevelSetName = LobbyHelper.GetLobbyLevelSet(areaData.GetSID());
                    LevelSetStats lobbyMapLevelSet     = null;
                    if (lobbyMapLevelSetName != null)
                    {
                        lobbyMapLevelSet = SaveData.Instance.GetLevelSetStatsFor(lobbyMapLevelSetName);
                    }

                    int  lobbyStrawberries                = item.TotalStrawberries;
                    int  lobbyTotalStrawberries           = areaData.Mode[0].TotalStrawberries;
                    int  lobbyDeaths                      = item.Modes[0].Deaths;
                    int  lobbySumOfBestDeaths             = 0;
                    int  lobbySumOfBestDashes             = OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() ? OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item) : 0;
                    long lobbyTotalTime                   = item.TotalTimePlayed;
                    long lobbySumOfBestTimes              = 0;
                    bool lobbyLevelsDone                  = true;
                    int  lobbySpeedBerryLevel             = 1;
                    bool lobbySilverBerriesObtained       = true;
                    bool lobbyAllMapsCompletedInSingleRun = true;

                    foreach (AreaStats lobbyMap in lobbyMapLevelSet?.Areas ?? new List <AreaStats>())
                    {
                        AreaData lobbyAreaData = AreaData.Get(lobbyMap.ID_Safe);
                        lobbyStrawberries                += lobbyMap.TotalStrawberries;
                        lobbyTotalStrawberries           += lobbyAreaData.Mode[0].TotalStrawberries;
                        lobbyDeaths                      += lobbyMap.Modes[0].Deaths;
                        lobbyTotalTime                   += lobbyMap.TotalTimePlayed;
                        lobbyAllMapsCompletedInSingleRun &= lobbyMap.Modes[0].SingleRunCompleted;

                        if (displaySpeedBerryColumn)
                        {
                            if (CollabMapDataProcessor.SpeedBerries.TryGetValue(lobbyMap.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo mapSpeedBerryInfo) &&
                                CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(lobbyMap.GetSID(), out long mapSpeedBerryPB))
                            {
                                lobbySpeedBerryLevel = Math.Max(getRankLevel(mapSpeedBerryInfo, mapSpeedBerryPB), lobbySpeedBerryLevel);
                                lobbySumOfBestTimes += mapSpeedBerryPB;
                            }
                            else
                            {
                                lobbySpeedBerryLevel = 4;
                            }
                        }
                        else
                        {
                            if (lobbyMap.Modes[0].BestTime > 0f)
                            {
                                lobbySumOfBestTimes += lobbyMap.Modes[0].BestTime;
                            }
                            else
                            {
                                lobbySpeedBerryLevel = 4;
                            }
                        }

                        bool goldenBerryNotObtained = !lobbyMap.Modes[0].Strawberries.Any(berry => lobbyAreaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level));
                        bool silverBerryNotObtained = !CollabMapDataProcessor.SilverBerries.TryGetValue(lobbyMap.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) ||
                                                      !levelSetBerries.TryGetValue(lobbyMap.GetSID(), out EntityID berryID) ||
                                                      !lobbyMap.Modes[0].Strawberries.Contains(berryID);

                        if (goldenBerryNotObtained && silverBerryNotObtained)
                        {
                            lobbySilverBerriesObtained = false;
                            lobbySumOfBestDeaths      += lobbyMap.Modes[0].BestDeaths;
                        }

                        lobbySumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(lobbyMap);

                        if (!lobbyMap.Modes[0].HeartGem)
                        {
                            lobbyLevelsDone = false;
                        }
                    }

                    string strawberryText = null;
                    if (lobbyStrawberries > 0 || lobbyTotalStrawberries > 0)
                    {
                        strawberryText = lobbyStrawberries.ToString();
                        if (lobbyLevelsDone)
                        {
                            strawberryText = strawberryText + "/" + lobbyTotalStrawberries;
                        }
                    }
                    else
                    {
                        strawberryText = "-";
                    }

                    string heartTexturePath = lobbyMapLevelSetName ?? areaData.GetSID();
                    string heartTexture     = MTN.Journal.Has("CollabUtils2Hearts/" + heartTexturePath) ? "CollabUtils2Hearts/" + heartTexturePath : "heartgem0";

                    string areaName = Dialog.Clean(areaData.Name);
                    if (Dialog.Has(areaData.Name + "_journal"))
                    {
                        areaName = Dialog.Clean(areaData.Name + "_journal");
                    }

                    Row row = table.AddRow()
                              .Add(new TextCell(areaName, new Vector2(1f, 0.5f), 0.6f, TextColor))
                              .Add(null)
                              .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                              .Add(new TextCell(strawberryText, TextJustify, 0.5f, TextColor));

                    if (lobbyTotalTime > 0)
                    {
                        row.Add(new TextCell(Dialog.Deaths(lobbyDeaths), TextJustify, 0.5f, TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (lobbyLevelsDone)
                    {
                        AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                        if (lobbyMapLevelSet == null)
                        {
                            row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), TextJustify, 0.5f, TextColor));
                            sumOfBestDeaths += item.Modes[0].BestDeaths;
                        }
                        else if (lobbySilverBerriesObtained)
                        {
                            row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                        }
                        else if (lobbyAllMapsCompletedInSingleRun)
                        {
                            row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDeaths), TextJustify, 0.5f, TextColor));
                        }
                        else
                        {
                            row.Add(new IconCell("dot"));
                        }
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                        allLevelsDone = false;
                    }


                    if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                    {
                        if (lobbyMapLevelSet == null)
                        {
                            if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted)
                            {
                                row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)),
                                                     TextJustify, 0.5f, TextColor));
                                sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item);
                            }
                            else
                            {
                                row.Add(new IconCell("dot"));
                            }
                        }
                        else if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || lobbyAllMapsCompletedInSingleRun)
                        {
                            row.Add(new TextCell(Dialog.Deaths(lobbySumOfBestDashes), TextJustify, 0.5f, TextColor));
                        }
                        else
                        {
                            row.Add(new IconCell("dot"));
                        }
                    }

                    if (lobbyTotalTime > 0)
                    {
                        row.Add(new TextCell(Dialog.Time(lobbyTotalTime), TextJustify, 0.5f, TextColor));
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }

                    if (lobbyMapLevelSet == null)
                    {
                        row.Add(new TextCell("-", TextJustify, 0.5f, TextColor)).Add(null);
                    }
                    else if (lobbySpeedBerryLevel < 4)
                    {
                        if (displaySpeedBerryColumn)
                        {
                            row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, getRankColor(lobbySpeedBerryLevel)));
                            row.Add(new IconCell(getRankIcon(lobbySpeedBerryLevel)));
                            sumOfBestTimes += lobbySumOfBestTimes;
                        }
                        else
                        {
                            row.Add(new TextCell(Dialog.Time(lobbySumOfBestTimes), TextJustify, 0.5f, TextColor)).Add(null);
                            sumOfBestTimes += lobbySumOfBestTimes;
                        }
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }

                    totalStrawberries           += lobbyStrawberries;
                    totalDeaths                 += lobbyDeaths;
                    sumOfBestDeaths             += lobbySumOfBestDeaths;
                    sumOfBestDashes             += lobbySumOfBestDashes;
                    totalTime                   += lobbyTotalTime;
                    allMapsCompletedInSingleRun &= lobbyAllMapsCompletedInSingleRun;

                    if (!lobbyLevelsDone)
                    {
                        allLevelsDone = false;
                    }
                }
            }

            table.AddRow();
            Row totalsRow = table.AddRow()
                            .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, TextColor)).Add(null)
                            .Add(null)
                            .Add(new TextCell(totalStrawberries.ToString(), TextJustify, 0.6f, TextColor))
                            .Add(new TextCell(Dialog.Deaths(totalDeaths), TextJustify, 0.6f, TextColor))
                            .Add(new TextCell(allLevelsDone && allMapsCompletedInSingleRun ? Dialog.Deaths(sumOfBestDeaths) : "-", TextJustify, 0.6f, TextColor));

            if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
            {
                totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || (allLevelsDone && allMapsCompletedInSingleRun) ?
                                           Dialog.Deaths(sumOfBestDashes) : "-", TextJustify, 0.6f, TextColor));
            }

            totalsRow
            .Add(new TextCell(Dialog.Time(totalTime), TextJustify, 0.6f, TextColor))
            .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", TextJustify, 0.6f, TextColor)).Add(null);

            for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
            {
                totalsRow.Add(null);
            }
            totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), TextJustify, 0.6f, TextColor));
            table.AddRow();
        }
예제 #15
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            var lastarea = AreaData.Areas[AreaData.Areas.Count - 1];
            var newID    = AreaData.Areas.Count;

            if (lastarea.GetLevelSet() == "randomizer")
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, true, false) :
                                    settings.Dashes == NumDashes.One ? PlayerInventory.Default :
                                    PlayerInventory.CH6End,
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };

            newArea.SetSID($"randomizer/{newArea.Name}");
            if (lastarea.GetSID().StartsWith("randomizer/"))
            {
                AreaData.Areas[AreaData.Areas.Count - 1] = newArea;
            }
            else
            {
                // avert race condition
                RandoModule.AreaHandoff = newArea;
                while (RandoModule.AreaHandoff != null)
                {
                    Thread.Sleep(10);
                }
            }

            var key = new AreaKey(newArea.ID);

            var r = new RandoLogic(settings, key);

            newArea.Wipe = r.PickWipe();
            newArea.CompleteScreenName = r.PickCompleteScreen();
            newArea.CassetteSong       = r.PickCassetteAudio();
            newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
            newArea.Mode[0].MapData    = r.MakeMap();
            r.RandomizeDialog();

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }
예제 #16
0
        public override Dictionary <string, Action <BinaryPacker.Element> > Init()
        => new Dictionary <string, Action <BinaryPacker.Element> >()
        {
            { "root", root => {
                  foreach (BinaryPacker.Element el in root.Children)
                  {
                      Context.Run(el.Name, el);
                  }
              } },

            { "Style", style => {
                  // Celeste 1.2.5.0 optimizes BinaryPacker, which causes some issues.
                  // Let's "unoptimize" Style and its Backgrounds and Foregrounds.
                  if (style.Children == null)
                  {
                      style.Children = new List <BinaryPacker.Element>();
                  }
                  foreach (BinaryPacker.Element el in style.Children)
                  {
                      if ((el.Name == "Backgrounds" ||
                           el.Name == "Foregrounds") &&
                          el.Children == null)
                      {
                          el.Children = new List <BinaryPacker.Element>();
                      }
                  }
              } },

            { "levels", levels => {
                  if (levels.Children != null)
                  {
                      foreach (BinaryPacker.Element level in levels.Children)
                      {
                          Context.Run("level", level);

                          if (level.Children != null)
                          {
                              foreach (BinaryPacker.Element levelChild in level.Children)
                              {
                                  Context.Run(levelChild.Name, levelChild);
                              }
                          }
                      }
                  }
              } },

            { "level", level => {
                  // lvl_ was optional before Celeste 1.2.5.0 made it mandatory.
                  // Certain level "tags" are supported as very early mods used them.
                  LevelTags = level.Attr("name").Split(':');
                  LevelName = LevelTags[0];
                  if (LevelName.StartsWith("lvl_"))
                  {
                      LevelName = LevelName.Substring(4);
                  }
                  level.SetAttr("name", "lvl_" + LevelName);

                  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 (null instead of empty lists),
                  // which causes some issues where the game still expects an empty list.
                  // 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 }
                                        }
                                    }
                                }
                            });
                      }
                  }
              } },

            { "entities", levelChild => {
                  // check if the room has a checkpoint first.
                  foreach (BinaryPacker.Element entity in levelChild.Children)
                  {
                      if (entity.Name == "checkpoint")
                      {
                          if (CheckpointsAuto != null)
                          {
                              MapMeta        modeMeta = AreaData.GetModeMeta(AreaKey.Mode);
                              CheckpointData c        = new CheckpointData(
                                  LevelName,
                                  (AreaData.GetSID() + "_" + LevelName).DialogKeyify(),
                                  MapMeta.GetInventory(entity.Attr("inventory")),
                                  entity.Attr("dreaming") == "" ? modeMeta.Dreaming ?? AreaData.Dreaming : entity.AttrBool("dreaming"),
                                  null
                                  );
                              if (entity.Attr("coreMode") == "")
                              {
                                  c.CoreMode = modeMeta.CoreMode ?? AreaData.CoreMode;
                              }
                              else
                              {
                                  entity.AttrIf("coreMode", v => c.CoreMode = (Session.CoreModes)Enum.Parse(typeof(Session.CoreModes), v, true));
                              }

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

                  // then, auto-assign strawberries and cassettes to checkpoints.
                  foreach (BinaryPacker.Element entity in levelChild.Children)
                  {
                      Context.Run("entity:" + entity.Name, entity);
                  }
              } },

            { "entity:cassette", entity => {
                  if (AreaData.CassetteCheckpointIndex < 0)
                  {
                      AreaData.CassetteCheckpointIndex = Checkpoint;
                  }
              } },

            { "entity:strawberry", entity => {
                  if (!entity.AttrBool("moon", false))
                  {
                      if (entity.AttrInt("checkpointID", -1) == -1)
                      {
                          entity.SetAttr("checkpointID", Checkpoint);
                      }
                      if (entity.AttrInt("order", -1) == -1)
                      {
                          entity.SetAttr("order", StrawberryInCheckpoint);
                      }
                      Strawberry++;
                      StrawberryInCheckpoint++;
                  }
              } }
        };
예제 #17
0
        public static List <OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal journal, string levelSet, bool showOnlyDiscovered)
        {
            bool displaySpeedBerryColumn = shouldDisplaySpeedBerryColumn(levelSet);

            List <OuiJournalCollabProgressInLobby> pages = new List <OuiJournalCollabProgressInLobby>();
            int rowCount = 0;

            int  totalStrawberries = 0;
            int  totalDeaths       = 0;
            int  sumOfBestDeaths   = 0;
            int  sumOfBestDashes   = 0;
            long totalTime         = 0;
            long sumOfBestTimes    = 0;

            bool allMapsDone = true;

            bool allLevelsDone       = true;
            bool allSpeedBerriesDone = true;

            string heartTexture = MTN.Journal.Has("CollabUtils2Hearts/" + levelSet) ? "CollabUtils2Hearts/" + levelSet : "heartgem0";

            int mapsPerPage = 12;
            int mapAmount   = SaveData.Instance.Areas_Safe.Where(item => !AreaData.Get(item.ID_Safe).Interlude_Safe &&
                                                                 (!showOnlyDiscovered || item.TotalTimePlayed > 0)).Count();

            // we want to display the map icons if they're not actually all the same. ^^'
            bool displayIcons = AreaData.Areas
                                .Where(area => !area.Interlude_Safe)
                                .Select(area => area.Icon)
                                .Distinct()
                                .Count() > 1;

            OuiJournalCollabProgressInLobby currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons);

            pages.Add(currentPage);

            if (mapAmount >= mapsPerPage)
            {
                // we want the last page to contain at least 2 maps.
                while (mapAmount % mapsPerPage < 2)
                {
                    mapsPerPage--;
                }
            }

            List <AreaStats> sortedMaps = new List <AreaStats>(SaveData.Instance.Areas_Safe)
                                          .Where(map => !AreaData.Get(map).Interlude_Safe)
                                          .ToList();

            // sort maps by icon name if all of their icons start with [number]-...
            // because then we know the ordering is intentional (and not accidental like easy > hard > medium)
            Regex startsWithNumber = new Regex(".*/[0-9]+-.*");

            if (sortedMaps.Select(map => AreaData.Get(map).Icon ?? "").All(icon => startsWithNumber.IsMatch(icon)))
            {
                sortedMaps.Sort((a, b) => {
                    AreaData adata = AreaData.Get(a);
                    AreaData bdata = AreaData.Get(b);

                    bool aHeartSide = LobbyHelper.IsHeartSide(a.GetSID());
                    bool bHeartSide = LobbyHelper.IsHeartSide(b.GetSID());

                    // heart sides should appear last.
                    if (aHeartSide && !bHeartSide)
                    {
                        return(1);
                    }
                    if (!aHeartSide && bHeartSide)
                    {
                        return(-1);
                    }

                    // sort by icon name, then by map bin name.
                    return(adata.Icon == bdata.Icon ? adata.Name.CompareTo(bdata.Name) : adata.Icon.CompareTo(bdata.Icon));
                });
            }

            foreach (AreaStats item in sortedMaps)
            {
                AreaData areaData = AreaData.Get(item.ID_Safe);
                if (LobbyHelper.IsHeartSide(areaData.GetSID()))
                {
                    if (allMapsDone || item.TotalTimePlayed > 0)
                    {
                        // add a separator, like the one between regular maps and Farewell
                        currentPage.table.AddRow();
                    }
                    else
                    {
                        // all maps weren't complete yet, and the heart side was never accessed: hide the heart side for now.
                        continue;
                    }
                }

                if (showOnlyDiscovered && item.TotalTimePlayed <= 0)
                {
                    // skip the map, because it was not discovered yet.
                    // since it wasn't discovered, we can already say all maps weren't done though.
                    allMapsDone         = false;
                    allLevelsDone       = false;
                    allSpeedBerriesDone = false;
                    continue;
                }

                string strawberryText = null;
                if (areaData.Mode[0].TotalStrawberries > 0 || item.TotalStrawberries > 0)
                {
                    strawberryText = item.TotalStrawberries.ToString();
                    if (item.Modes[0].Completed)
                    {
                        strawberryText = strawberryText + "/" + areaData.Mode[0].TotalStrawberries;
                    }
                }
                else
                {
                    strawberryText = "-";
                }

                Row row = currentPage.table.AddRow()
                          .Add(new TextCell(Dialog.Clean(areaData.Name), new Vector2(1f, 0.5f), 0.6f, currentPage.TextColor));

                if (displayIcons)
                {
                    row.Add(null).Add(new IconCellFromGui(GFX.Gui.Has(areaData.Icon) ? areaData.Icon : "areas/null", 60f, 50f));
                }

                row.Add(null)
                .Add(new IconCell(item.Modes[0].HeartGem ? heartTexture : "dot"))
                .Add(new TextCell(strawberryText, currentPage.TextJustify, 0.5f, currentPage.TextColor));

                if (item.TotalTimePlayed > 0)
                {
                    row.Add(new TextCell(Dialog.Deaths(item.Modes[0].Deaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                }
                else
                {
                    row.Add(new IconCell("dot"));
                }

                AreaStats stats = SaveData.Instance.GetAreaStatsFor(areaData.ToKey());
                if (CollabMapDataProcessor.SilverBerries.TryGetValue(areaData.GetLevelSet(), out Dictionary <string, EntityID> levelSetBerries) &&
                    levelSetBerries.TryGetValue(areaData.GetSID(), out EntityID berryID) &&
                    stats.Modes[0].Strawberries.Contains(berryID))
                {
                    // silver berry was obtained!
                    row.Add(new IconCell("CollabUtils2/silver_strawberry"));
                }
                else if (stats.Modes[0].Strawberries.Any(berry => areaData.Mode[0].MapData.Goldenberries.Any(golden => golden.ID == berry.ID && golden.Level.Name == berry.Level)))
                {
                    // golden berry was obtained!
                    row.Add(new IconCell("CollabUtils2/golden_strawberry"));
                }
                else if (item.Modes[0].SingleRunCompleted)
                {
                    row.Add(new TextCell(Dialog.Deaths(item.Modes[0].BestDeaths), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                    sumOfBestDeaths += item.Modes[0].BestDeaths;
                }
                else
                {
                    // the player didn't ever do a single run.
                    row.Add(new IconCell("dot"));
                    allLevelsDone = false;
                }

                if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                {
                    if ((OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() && item.TotalTimePlayed > 0) || item.Modes[0].SingleRunCompleted)
                    {
                        row.Add(new TextCell(Dialog.Deaths(OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item)),
                                             currentPage.TextJustify, 0.5f, currentPage.TextColor));
                        sumOfBestDashes += OuiJournalCollabProgressDashCountMod.GetLevelDashesForJournalProgress(item);
                    }
                    else
                    {
                        row.Add(new IconCell("dot"));
                    }
                }

                if (item.TotalTimePlayed > 0)
                {
                    row.Add(new TextCell(Dialog.Time(item.TotalTimePlayed), currentPage.TextJustify, 0.5f, currentPage.TextColor));
                }
                else
                {
                    row.Add(new IconCell("dot"));
                }

                if (displaySpeedBerryColumn)
                {
                    if (CollabMapDataProcessor.SpeedBerries.TryGetValue(item.GetSID(), out CollabMapDataProcessor.SpeedBerryInfo speedBerryInfo) &&
                        CollabModule.Instance.SaveData.SpeedBerryPBs.TryGetValue(item.GetSID(), out long speedBerryPB))
                    {
                        row.Add(new TextCell(Dialog.Time(speedBerryPB), currentPage.TextJustify, 0.5f, getRankColor(speedBerryInfo, speedBerryPB)));
                        row.Add(new IconCell(getRankIcon(speedBerryInfo, speedBerryPB)));
                        sumOfBestTimes += speedBerryPB;
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }
                }
                else
                {
                    if (item.Modes[0].BestTime > 0f)
                    {
                        row.Add(new TextCell(Dialog.Time(item.Modes[0].BestTime), currentPage.TextJustify, 0.5f, currentPage.TextColor)).Add(null);
                        sumOfBestTimes += item.Modes[0].BestTime;
                    }
                    else
                    {
                        row.Add(new IconCell("dot")).Add(null);
                        allSpeedBerriesDone = false;
                    }
                }

                totalStrawberries += item.TotalStrawberries;
                totalDeaths       += item.Modes[0].Deaths;
                totalTime         += item.TotalTimePlayed;

                if (!item.Modes[0].HeartGem)
                {
                    allMapsDone = false;
                }

                rowCount++;
                if (rowCount >= mapsPerPage)
                {
                    // split the next zones into another page.
                    rowCount    = 0;
                    currentPage = new OuiJournalCollabProgressInLobby(journal, levelSet, displayIcons);
                    pages.Add(currentPage);
                }
            }

            if (currentPage.table.Rows > 1)
            {
                currentPage.table.AddRow();
                Row totalsRow = currentPage.table.AddRow()
                                .Add(new TextCell(Dialog.Clean("journal_totals"), new Vector2(1f, 0.5f), 0.7f, currentPage.TextColor)).Add(null);

                if (displayIcons)
                {
                    totalsRow.Add(null).Add(null);
                }

                totalsRow.Add(null)
                .Add(new TextCell(totalStrawberries.ToString(), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(Dialog.Deaths(totalDeaths), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(allLevelsDone ? Dialog.Deaths(sumOfBestDeaths) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor));

                if (OuiJournalCollabProgressDashCountMod.IsDashCountEnabled())
                {
                    totalsRow.Add(new TextCell(OuiJournalCollabProgressDashCountMod.DisplaysTotalDashes() || allLevelsDone ? Dialog.Deaths(sumOfBestDashes) : "-",
                                               currentPage.TextJustify, 0.6f, currentPage.TextColor));
                }

                totalsRow
                .Add(new TextCell(Dialog.Time(totalTime), currentPage.TextJustify, 0.6f, currentPage.TextColor))
                .Add(new TextCell(allSpeedBerriesDone ? Dialog.Time(sumOfBestTimes) : "-", currentPage.TextJustify, 0.6f, currentPage.TextColor)).Add(null);

                for (int l = 1; l < SaveData.Instance.UnlockedModes; l++)
                {
                    totalsRow.Add(null);
                }
                totalsRow.Add(new TextCell(Dialog.Time(SaveData.Instance.Time), currentPage.TextJustify, 0.6f, currentPage.TextColor));
                currentPage.table.AddRow();
            }

            return(pages);
        }
예제 #18
0
        public static AreaKey GenerateMap(RandoSettings settings)
        {
            StringWriter builder = new StringWriter();

            builder.Write("Generating map with settings:\n");
            YamlHelper.Serializer.Serialize(builder, settings);
            Logger.Log("randomizer", builder.ToString());
            var newID = AreaData.Areas.Count;

            if (AreaData.Areas.Last().GetSID().StartsWith("randomizer/"))
            {
                newID--;
            }

            var newArea = new AreaData {
                IntroType = Player.IntroTypes.WakeUp,
                Interlude = false,
                Dreaming  = false,
                ID        = newID,
                Name      = $"{settings.Seed}_{settings.EndlessLevel}_{settings.Hash}",
                Mode      = new ModeProperties[3] {
                    new ModeProperties {
                        Inventory = settings.Dashes == NumDashes.Zero ? new PlayerInventory(0, true, false, false) :
                                    settings.Dashes == NumDashes.One ?  new PlayerInventory(1, true, false, false) :
                                    new PlayerInventory(2, true, false, false),
                    }, null, null
                },
                Icon                = AreaData.Areas[0].Icon,
                MountainIdle        = AreaData.Areas[0].MountainIdle,
                MountainZoom        = AreaData.Areas[0].MountainZoom,
                MountainState       = AreaData.Areas[0].MountainState,
                MountainCursor      = new Vector3(0, 100000, 0), // ???
                MountainSelect      = AreaData.Areas[0].MountainSelect,
                MountainCursorScale = AreaData.Areas[0].MountainCursorScale,
            };

            newArea.SetMeta(new Meta.MapMeta {
                Modes = new Meta.MapMetaModeProperties[] {
                    new Meta.MapMetaModeProperties {
                        HeartIsEnd = true,
                        //SeekerSlowdown = true,  // this doesn't do anything
                    },
                    null, null
                }
            });
            newArea.OnLevelBegin = (level) => {
                level.Add(new SeekerEffectsController());
            };
            var dyn = new DynData <AreaData>(newArea);

            dyn.Set <RandoSettings>("RandoSettings", settings.Copy());

            newArea.SetSID($"randomizer/{newArea.Name}");

            // avert race condition
            RandoModule.AreaHandoff = newArea;
            while (RandoModule.AreaHandoff != null)
            {
                Thread.Sleep(10);
            }

            // invalidate the MapEditor area key cache, as it will erroniously see a cache hit
            typeof(Editor.MapEditor).GetField("area", BindingFlags.NonPublic | BindingFlags.Static).SetValue(null, AreaKey.None);

            var key = new AreaKey(newArea.ID);

            int tryNum = 0;

            while (true)
            {
                try {
                    var r = new RandoLogic(settings, key, tryNum);

                    newArea.Mode[0].MapData    = r.MakeMap();
                    newArea.Wipe               = r.PickWipe();
                    newArea.CompleteScreenName = r.PickCompleteScreen();
                    newArea.CassetteSong       = r.PickCassetteAudio();
                    newArea.Mode[0].AudioState = new AudioState(r.PickMusicAudio(), r.PickAmbienceAudio());
                    if (settings.RandomColors)
                    {
                        newArea.BloomBase     = (float)Math.Pow(r.Random.NextFloat(), 5) * r.Random.NextFloat();
                        newArea.DarknessAlpha = r.Random.NextFloat() * (float)Math.Pow(r.Random.NextFloat(), 0.5) * (float)Math.Pow(r.Random.NextFloat(), 2) * 0.35f;
                        newArea.ColorGrade    = r.PickColorGrade();
                    }
                    r.RandomizeDialog();
                    break;
                } catch (RetryException) {
                    tryNum++;
                    if (tryNum >= 500)
                    {
                        throw new GenerationError("Cannot create map with these settings");
                    }
                    Logger.Log("randomizer", $"Too many retries ({tryNum}), starting again");
                }
            }

            Logger.Log("randomizer", $"new area {newArea.GetSID()}");

            return(key);
        }