/// <summary>
        /// Mods the lighting of a new room being loaded.
        /// </summary>
        /// <param name="self">The level we are in</param>
        /// <param name="introType">How the player enters the level</param>
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            if (Settings.RoomLighting != -1)
            {
                float lightingTarget = 1 - Settings.RoomLighting / 10f;
                if (playerIntro == Player.IntroTypes.Transition)
                {
                    // we force the game into thinking this is not a dark room, so that it uses the BaseLightingAlpha + LightingAlphaAdd formula
                    // (this change is not permanent, exiting and re-entering will reset this flag)
                    self.DarkRoom = false;

                    // we mod BaseLightingAlpha temporarily so that it adds up with LightingAlphaAdd to the right value: the transition routine will smoothly switch to it
                    // (we are sure BaseLightingAlpha will not move for the entire session: it's only set when the map is loaded)
                    initialBaseLightingAlpha = self.BaseLightingAlpha;
                    self.BaseLightingAlpha   = lightingTarget - self.Session.LightingAlphaAdd;
                }
                else
                {
                    // just set the initial value
                    self.Lighting.Alpha = lightingTarget;
                }
            }
        }
Exemple #2
0
        private void OnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            BGModeToggle.BGMode    = BGModeToggle.Persist;
            BGModeToggle.OldBGMode = !BGModeToggle.Persist;
            orig(self, playerIntro, isFromLoader);

            if (self != null)
            {
                if (self.Tracker != null)
                {
                    if (self.Tracker.GetEntity <Player>() != null)
                    {
                        if (self.Entities.Any(entity => (entity is BGModeToggle)) || BGModeToggle.BGMode)
                        {
                            self.Tracker.GetEntity <Player>().Sprite.Position = new Vector2();
                            if (!BGModeToggle.Persist)
                            {
                                self.Tracker.GetEntity <Player>().Sprite.Position += new Vector2(0, +2);
                            }
                            BGModeToggle.Setup(self);
                            BGModeToggle.UpdateBG(self);
                        }
                    }
                }
            }
        }
Exemple #3
0
        [PatchLevelLoader] // Manually manipulate the method via MonoModRules
        public new void LoadLevel(Player.IntroTypes playerIntro, bool isFromLoader = false)
        {
            // Read player introType from metadata as player enter the C-Side
            if (Session.FirstLevel && Session.StartedFromBeginning && Session.JustStarted &&
                Session.Area.Mode == AreaMode.CSide &&
                AreaData.GetMode(Session.Area)?.GetMapMeta() is MapMeta mapMeta && (mapMeta.OverrideASideMeta ?? false) &&
                mapMeta.IntroType is Player.IntroTypes introType)
            {
                playerIntro = introType;
            }

            try {
                orig_LoadLevel(playerIntro, isFromLoader);
            } catch (Exception e) {
                Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed loading level {Session.Area}");
                e.LogDetailed();

                string message = Dialog.Get("postcard_levelloadfailed");
                if (e is ArgumentOutOfRangeException && e.StackTrace.Contains("get_DefaultSpawnPoint"))
                {
                    message = Dialog.Get("postcard_levelnospawn");
                }
                message = message
                          .Replace("((player))", SaveData.Instance.Name)
                          .Replace("((sid))", Session.Area.GetSID());

                Entity helperEntity = new Entity();
                helperEntity.Add(new Coroutine(ErrorRoutine(message)));
                Add(helperEntity);
                return;
            }
            Everest.Events.Level.LoadLevel(this, playerIntro, isFromLoader);
        }
Exemple #4
0
        public void OnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(level, playerIntro, isFromLoader);

            if (isFromLoader)
            {
                GhostManager?.RemoveSelf();
                GhostManager = null;
                GhostRecorder?.RemoveSelf();
                GhostRecorder = null;
                Run           = Guid.NewGuid();
            }

            Player player = level.Tracker.GetEntity <Player>();

            if (player == null)
            {
                level.Add(new Entity {
                    new Coroutine(WaitForPlayer(level))
                });
            }
            else
            {
                Step(level);
            }
        }
        private void OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            var settings = this.InRandomizerSettings;

            if (settings != null)
            {
                // set summit gems
                SaveData.Instance.SummitGems = new bool[6];
                if (settings.Length == MapLength.Short)
                {
                    SaveData.Instance.SummitGems[0] = true;
                    SaveData.Instance.SummitGems[1] = true;
                    SaveData.Instance.SummitGems[2] = true;
                }

                // set life berries
                if (isFromLoader && settings.Algorithm == LogicType.Endless)
                {
                    var glb = Entities.LifeBerry.GrabbedLifeBerries;
                    if (settings.EndlessLevel == 0)
                    {
                        glb.Carrying = settings.EndlessLives;
                    }
                    else if (glb.Carrying < settings.EndlessLives)
                    {
                        glb.Carrying++;
                    }
                }
            }
        }
Exemple #6
0
        [PatchLevelLoader] // Manually manipulate the method via MonoModRules
        public new void LoadLevel(Player.IntroTypes playerIntro, bool isFromLoader = false)
        {
            // These two fields are missing before Celeste 1.2.5.0.
            // This is only here because Leo bugged me enough to add this here.
            CassetteBlockTempo = 1f;
            CassetteBlockBeats = 2;

            try {
                orig_LoadLevel(playerIntro, isFromLoader);
            } catch (Exception e) {
                Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed loading level {Session.Area}");
                e.LogDetailed();

                string message = Dialog.Get("postcard_levelloadfailed");
                if (e is ArgumentOutOfRangeException && e.StackTrace.Contains("get_DefaultSpawnPoint"))
                {
                    message = Dialog.Get("postcard_levelnospawn");
                }
                message = message
                          .Replace("((player))", SaveData.Instance.Name)
                          .Replace("((sid))", Session.Area.GetSID());

                Entity helperEntity = new Entity();
                helperEntity.Add(new Coroutine(ErrorRoutine(message)));
                Add(helperEntity);
                return;
            }
            Everest.Events.Level.LoadLevel(this, playerIntro, isFromLoader);
        }
Exemple #7
0
        private void onLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            // strawberries weren't made golden yet, they were just spawned.
            strawberriesWereMadeGolden = false;

            orig(self, playerIntro, isFromLoader);
        }
Exemple #8
0
 private static void LevelLoadHandler(Level loadedLevel, Player.IntroTypes playerIntro, bool isFromLoader)
 {
     if (loadedLevel.Session.GetFlag("lightsDisabled"))
     {
         DisableAllLights(loadedLevel);
         On.Celeste.Level.TransitionTo += TransitionLightSources;
     }
 }
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            if (playerIntro != Player.IntroTypes.Transition)
            {
                applyWind(self);
            }
        }
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            // if we killed the slowdown earlier, stop now!
            killSeekerSlowdownToFixHeart = false;

            Level  level  = self;
            Player player = level.Tracker.GetEntity <Player>();

            if (player != null && Settings.AddSeekers != 0)
            {
                // make the seeker barriers temporarily collidable so that they are taken in account in Solid collide checks
                // and seekers can't spawn in them
                // (... yes, this is also what vanilla does in the seekers' Update method.)
                foreach (Entity entity in self.Tracker.GetEntities <SeekerBarrier>())
                {
                    entity.Collidable = true;
                }

                for (int seekerCount = 0; seekerCount < Settings.AddSeekers; seekerCount++)
                {
                    for (int i = 0; i < 100; i++)
                    {
                        // roll a seeker position in the room
                        int x = randomGenerator.Next(level.Bounds.Width) + level.Bounds.X;
                        int y = randomGenerator.Next(level.Bounds.Height) + level.Bounds.Y;

                        // should be at least 100 pixels from the player
                        double playerDistance = Math.Sqrt(Math.Pow(MathHelper.Distance(x, player.X), 2) + Math.Pow(MathHelper.Distance(y, player.Y), 2));

                        // also check if we are not spawning in a wall, that would be a shame
                        Rectangle collideRectangle = new Rectangle(x - 8, y - 8, 16, 16);
                        if (playerDistance > 100 && !level.CollideCheck <Solid>(collideRectangle) && !level.CollideCheck <Seeker>(collideRectangle))
                        {
                            // build a Seeker with a proper EntityID to make Speedrun Tool happy (this is useless in vanilla Celeste but the constructor call is intercepted by Speedrun Tool)
                            EntityData seekerData = ExtendedVariantsModule.GenerateBasicEntityData(level, 10 + seekerCount);
                            seekerData.Position = new Vector2(x, y);
                            Seeker seeker = new AutoDestroyingSeeker(seekerData, Vector2.Zero);
                            level.Add(seeker);
                            break;
                        }
                    }
                }

                foreach (Entity entity in self.Tracker.GetEntities <SeekerBarrier>())
                {
                    entity.Collidable = false;
                }

                if (playerIntro != Player.IntroTypes.Transition)
                {
                    level.Entities.UpdateLists();
                }
            }
        }
Exemple #11
0
        private void onLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            Player player = self.Tracker.GetEntity <Player>();

            if (Settings.FriendlyBadelineFollower && player != null && self.Tracker.CountEntities <FriendlyBaddy>() == 0)
            {
                self.Add(new FriendlyBaddy(player.Center + new Vector2(-16f * (int)player.Facing, -16f)));
            }
        }
Exemple #12
0
        public void OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            if (isFromLoader)
            {
                Ghosts.Clear();
                GhostRecorder?.RemoveSelf();
                GhostRecorder = null;
            }

            Step(level);
        }
Exemple #13
0
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            // failsafe: kill the glitch effect.
            Glitch.Value = 0;

            if (Settings.BadelineBossesEverywhere && playerIntro != Player.IntroTypes.Transition)
            {
                injectBadelineBosses(self);
            }
        }
Exemple #14
0
        public static void TeleportTo(Scene scene, Player player, string room, Player.IntroTypes introType = Player.IntroTypes.Transition, Vector2?nearestSpawn = null)
        {
            Level level = scene as Level;

            if (level != null)
            {
                level.OnEndOfFrame += () =>
                {
                    level.TeleportTo(player, room, introType, nearestSpawn);
                };
            }
        }
Exemple #15
0
            private static void OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
            {
                DateTime now = DateTime.UtcNow;

                if (DiscordPresence.startTimestamp == 0)
                {
                    DiscordPresence.startTimestamp = DateTimeToDiscordTime(DateTime.UtcNow);
                }
                DiscordPresence.endTimestamp = 0;

                UpdateText(CoreModule.Settings.DiscordTextInGame, CoreModule.Settings.DiscordSubtextInGame, level.Session);
            }
Exemple #16
0
        /// <summary>
        /// Checks how many lives should be given to the player when a level is loaded.
        /// </summary>
        /// <param name="level">Level object.</param>
        /// <param name="playerIntro">Intro type (how the player entered the level).</param>
        /// <param name="isFromLoader">Whether level entry is from after file select or not.</param>
        private void OnLevelLoad(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            // Check if chapter name is in the dictionary, return if otherwise
            enabled = chapters.TryGetValue(level.Session.MapData.Filename, out Chapter c);
            display.SetEnabled(enabled);
            if (!enabled)
            {
                return;
            }

            // Check if should restart chapter, do so if needed
            if (shouldRestart)
            {
                display.SetDisplayText(infiniteLives ? "inf" : lifeCount.ToString());
                Action restartChapter = delegate { Engine.Scene = new LevelExit(LevelExit.Mode.Restart, level.Session); };
                if (isFromLoader)
                {
                    level.DoScreenWipe(false, restartChapter);
                }
                else
                {
                    restartChapter();
                }
                shouldRestart = false;
                return;
            }

            // Check if player has not visited this level before, return if otherwise
            if (!level.NewLevel)
            {
                display.SetDisplayText(infiniteLives ? "inf" : lifeCount.ToString());
                return;
            }

            int?newLives = c.GetLives(level.Session.LevelData.Name);

            // Do not try to change life count if non-existent new life count
            if (!newLives.HasValue)
            {
                return;
            }

            lifeCount     = Math.Max(lifeCount, newLives.Value); // Don't punish player for being too good
            infiniteLives = newLives.Value < 0;
            SaveSessionData();
            display.SetDisplayText(infiniteLives ? "inf" : lifeCount.ToString());

            Log($"OnLevelLoad: map={level.Session.MapData.Filename}, level: {level.Session.LevelData.Name}, lifeCount={lifeCount}, infiniteLives={infiniteLives}");
        }
Exemple #17
0
        public void OnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(level, playerIntro, isFromLoader);

            if (isFromLoader)
            {
                GhostManager?.RemoveSelf();
                GhostManager = null;
                GhostRecorder?.RemoveSelf();
                GhostRecorder = null;
                Run           = Guid.NewGuid();
            }

            Step(level);
        }
Exemple #18
0
        private void OnLoadLevel(Level level, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            var settings = this.InRandomizerSettings;

            if (settings != null)
            {
                // set summit gems
                SaveData.Instance.SummitGems = new bool[6];
                if (settings.Length == MapLength.Short)
                {
                    SaveData.Instance.SummitGems[0] = true;
                    SaveData.Instance.SummitGems[1] = true;
                    SaveData.Instance.SummitGems[2] = true;
                }
            }
        }
Exemple #19
0
        private void onLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            if (!self.Session?.LevelData?.Underwater ?? false)
            {
                // inject a controller that will spawn/despawn water depending on the extended variant setting.
                self.Add(new UnderwaterSwitchController(Settings));

                // when transitioning, don't update lists. this messes with sandwich lava, and the hook below will take care of updating lists.
                if (playerIntro != Player.IntroTypes.Transition)
                {
                    self.Entities.UpdateLists();
                }
            }
        }
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            if (playerIntro != Player.IntroTypes.Transition)
            {
                // always reset the jump count when the player enters a new level (respawn, new map, etc... everything but a transition)
                RefillJumpBuffer();
            }

            if (!self.Entities.Any(entity => entity is JumpIndicator))
            {
                // add the entity showing the jump count
                self.Add(new JumpIndicator());
                self.Entities.UpdateLists();
            }
        }
        private static void LevelOnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self,
                                             Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);
            if (!isFromLoader && playerIntro != Player.IntroTypes.Respawn)
            {
                return;
            }
            // entities with Tags.Global will not be removed after level reloading, so we need to remove them manually
            self.Entities.Remove(self.Entities.FindAll <CollectablePointer>());
            var entities = self.Session.MapData.Levels.SelectMany(data => data.Entities);

            foreach (EntityData entityData in entities)
            {
                TryAddPointer(self, entityData);
            }
        }
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader) {
            orig(self, playerIntro, isFromLoader);

            if (playerIntro != Player.IntroTypes.Transition) {
                injectTheoCrystal(self);

            } else if ((Settings.AllowLeavingTheoBehind || Settings.AllowThrowingTheoOffscreen) &&
                !(self.Tracker.GetEntity<Player>()?.Holding?.Entity is TheoCrystal)) {

                // player is transitioning into a new room, but doesn't have Theo with them.
                injectTheoCrystalAfterTransition(self);

            } else if (self.Tracker.CountEntities<ExtendedVariantTheoCrystal>() == 0) {
                // player is transitioning into a new room, and no Theo crystal is in the room: we should add one if the variant is enabled.
                injectTheoCrystalAfterTransition(self);
            }
        }
 private void OnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level level, Player.IntroTypes playerIntro, bool isFromLoader)
 {
     orig(level, playerIntro, isFromLoader);
     Debug.WriteLine($"OnLoadLevel called, intro type {playerIntro}, isFromLoader {isFromLoader}");
     if (isFromLoader)
     {
         List <Monocle.Entity> e = level.Tracker.GetEntities <CounterEntity>();
         if (e.Count == 1)
         {
             Debug.WriteLine("counter already exists");
         }
         else
         {
             Debug.WriteLine("Creating counter entity");
             level.Add(new CounterEntity(level));
         }
     }
 }
Exemple #24
0
        private void onLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            if (!self.Session?.LevelData?.Underwater ?? false)
            {
                // inject a controller that will spawn/despawn water depending on the extended variant setting.
                self.Add(new UnderwaterSwitchController(Settings));

                // when transitioning, don't update lists right away, but on the end of the frame.
                if (playerIntro != Player.IntroTypes.Transition)
                {
                    self.Entities.UpdateLists();
                }
                else
                {
                    self.OnEndOfFrame += () => self.Entities.UpdateLists();
                }
            }
        }
Exemple #25
0
        private static void LevelOnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self,
                                             Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            // 切换章节清理历史记录
            if (RoomHistory.Count > 0 && RoomHistory[0].Area != self.Session.Area)
            {
                Reset();
            }

            // 非初始房间
            if (HistoryIndex != -1)
            {
                return;
            }

            // 进入章节的第一个房间
            RoomHistory.Add(self.Session.DeepClone());
            HistoryIndex = 0;
        }
Exemple #26
0
        [PatchLevelLoader] // Manually manipulate the method via MonoModRules
        public new void LoadLevel(Player.IntroTypes playerIntro, bool isFromLoader = false)
        {
            try {
                orig_LoadLevel(playerIntro, isFromLoader);
            } catch (Exception e) {
                Mod.Logger.Log(LogLevel.Warn, "misc", $"Failed loading level {Session.Area}");
                e.LogDetailed();

                string message = Dialog.Get("postcard_levelloadfailed");
                if (e is ArgumentOutOfRangeException && e.StackTrace.Contains("get_DefaultSpawnPoint"))
                {
                    message = Dialog.Get("postcard_levelnospawn");
                }
                message = message
                          .Replace("((player))", SaveData.Instance.Name)
                          .Replace("((sid))", Session.Area.GetSID());

                Entity helperEntity = new Entity();
                helperEntity.Add(new Coroutine(ErrorRoutine(message)));
                Add(helperEntity);
                return;
            }
            Everest.Events.Level.LoadLevel(this, playerIntro, isFromLoader);
        }
        /// <summary>
        /// Wraps the LoadLevel method in order to add Badeline chasers when needed.
        /// </summary>
        /// <param name="orig">The base method</param>
        /// <param name="self">The level entity</param>
        /// <param name="playerIntro">The type of player intro</param>
        /// <param name="isFromLoader">unused</param>
        private void modLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            // this method takes care of every situation except transitions, we let this one to TransitionRoutine
            if (Settings.BadelineChasersEverywhere && playerIntro != Player.IntroTypes.Transition)
            {
                // set this to avoid the player being instakilled during the level intro animation
                Player player = self.Tracker.GetEntity <Player>();
                if (player != null)
                {
                    player.JustRespawned = true;
                }
            }

            if (playerIntro != Player.IntroTypes.Transition)
            {
                if ((Settings.BadelineChasersEverywhere || Settings.AffectExistingChasers))
                {
                    injectBadelineChasers(self);
                }
                updateLastChaserLag(self);
            }
        }
        private static void onLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            orig(self, playerIntro, isFromLoader);

            DynData <Session> sessionData = new DynData <Session>(self.Session);

            if (sessionData.Data.ContainsKey("pauseTimerUntilAction") && sessionData.Get <bool>("pauseTimerUntilAction"))
            {
                sessionData["pauseTimerUntilAction"] = false;
                self.TimerStopped        = true;
                unpauseTimerOnNextAction = true;
            }
        }
Exemple #29
0
 public extern void orig_LoadLevel(Player.IntroTypes playerIntro, bool isFromLoader = false);
        private void Level_LoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level self, Player.IntroTypes playerIntro, bool isFromLoader)
        {
            if (playerIntro != Player.IntroTypes.Respawn)
            {
                deaths.Clear();
            }

            orig(self, playerIntro, isFromLoader);
        }