private EntityData generateBadelineEntityData(Level level, int badelineNumber) { EntityData entityData = ExtendedVariantsModule.GenerateBasicEntityData(level, badelineNumber); entityData.Values["canChangeMusic"] = false; return(entityData); }
private float determineDelayBetweenSnowballs() { if (ExtendedVariantsModule.ShouldIgnoreCustomDelaySettings()) { return(0.8f); } return(Settings.SnowballDelay / 10f); }
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(); } } }
public override void Update() { base.Update(); Player player = SceneAs <Level>().Tracker.GetEntity <Player>(); if (ExtendedVariantsModule.ShouldEntitiesAutoDestroy(player)) { RemoveSelf(); } }
public override void Update() { base.Update(); Player player = SceneAs <Level>().Tracker.GetEntity <Player>(); if (player != null && ExtendedVariantsModule.ShouldEntitiesAutoDestroy(player) && !triggeredLeave) { // we should destroy lava/ice Leave(); triggeredLeave = true; } }
public override void Update() { base.Update(); Level level = SceneAs <Level>(); Player player = level.Tracker.GetEntity <Player>(); if (ExtendedVariantsModule.ShouldEntitiesAutoDestroy(player)) { // during cutscenes, tell Oshiro to get outta here the same way as Badeline // (we can't use the "official" way of making him leave because that doesn't cancel his attack. // A Badeline vanish animation looks weird but nicer than a flat out disappearance imo) level.Displacement.AddBurst(Entity.Center, 0.5f, 24f, 96f, 0.4f, null, null); level.Particles.Emit(BadelineOldsite.P_Vanish, 12, Entity.Center, Vector2.One * 6f); Entity.RemoveSelf(); // make sure that the anxiety set by Oshiro went away (why doesn't that work in real life tho) Distort.Anxiety = 0; Distort.GameRate = 1; } }
public override void Update() { // if Badeline is waiting for a watchtower, stop updating it so that she doesn't go forward. if (!waitingForWatchtower) { base.Update(); Level level = SceneAs <Level>(); Player player = level.Tracker.GetEntity <Player>(); if (ExtendedVariantsModule.ShouldEntitiesAutoDestroy(player)) { // we are in a cutscene **but not the Badeline Intro one** // so we should just make the chasers disappear to prevent them from killing the player mid-cutscene level.Displacement.AddBurst(Center, 0.5f, 24f, 96f, 0.4f, null, null); level.Particles.Emit(P_Vanish, 12, Center, Vector2.One * 6f); if (!BadelineChasersEverywhere.UsingWatchtower) { // cutscene! RemoveSelf(); } else { // waiting for watchtower: just become invisible instead. waitingForWatchtower = true; Visible = false; } } } else if (!BadelineChasersEverywhere.UsingWatchtower) { // using the watchtower is over, make Badeline appear again! waitingForWatchtower = false; Visible = true; SceneAs <Level>().Displacement.AddBurst(Center, 0.5f, 24f, 96f, 0.4f, null, null); SceneAs <Level>().Particles.Emit(P_Vanish, 12, Center, Vector2.One * 6f); } }
public override void Update() { base.Update(); Player player = SceneAs <Level>().Tracker.GetEntity <Player>(); if (ExtendedVariantsModule.ShouldEntitiesAutoDestroy(player)) { // kill the boss no matter what RemoveSelf(); // and kill all the beams and shots as well, they could still kill the player! foreach (FinalBossShot shot in SceneAs <Level>().Tracker.GetEntities <FinalBossShot>()) { shot.Destroy(); } foreach (FinalBossBeam beam in SceneAs <Level>().Tracker.GetEntities <FinalBossBeam>()) { beam.Destroy(); } } }
private void injectBadelineBosses(Level level) { Player player = level.Tracker.GetEntity <Player>(); if (player != null) { for (int id = level.Tracker.CountEntities <FinalBoss>(); id < Settings.BadelineBossCount; id++) { // let's add a boss Vector2 bossPosition; if (id == 0 && !Settings.FirstBadelineSpawnRandom) { // the first Badeline should spawn at the opposite of the room bossPosition = computeBossPositionAtOppositeOfPlayer(level, player); } else { // all the others should spawn at random points bossPosition = computeBossPositionAtRandom(level, player); } // position not found, abort! if (bossPosition == Vector2.Zero) { break; } Vector2 penultimateNode = player.Position; Vector2 lastNode = bossPosition; Vector2[] nodes = new Vector2[Settings.BadelineBossNodeCount]; for (int i = 0; i < Settings.BadelineBossNodeCount - 1; i++) { // randomize all nodes, except the last one. nodes[i] = computeBossPositionAtRandom(level, player); penultimateNode = lastNode; lastNode = nodes[i]; // position not found, don't process other nodes! if (lastNode == Vector2.Zero) { break; } } // position not found, stop adding bosses! if (lastNode == Vector2.Zero) { break; } if (bossPosition != Vector2.Zero) { Vector2 bossPositionOffscreen = penultimateNode; Vector2 lastMoveDirection = (lastNode - penultimateNode); // extremely unlikely, but better safe than sorry if (lastMoveDirection == Vector2.Zero) { lastMoveDirection = Vector2.One; } while (bossPositionOffscreen.X + 30 > level.Bounds.Left - 50 && bossPositionOffscreen.X < level.Bounds.Right + 50 && bossPositionOffscreen.Y + 30 > level.Bounds.Top - 50 && bossPositionOffscreen.Y < level.Bounds.Bottom + 50) { // push the position until it is offscreen, using the same direction as the last move (or the player => boss direction if there is no node). bossPositionOffscreen += lastMoveDirection; } // the offscreen position has to be the last node. nodes[nodes.Length - 1] = bossPositionOffscreen; // build the boss EntityData bossData = ExtendedVariantsModule.GenerateBasicEntityData(level, 15 + id); // 0 to 9 are Badeline chasers, 10 to 14 are seekers. bossData.Position = bossPosition; bossData.Values["canChangeMusic"] = false; bossData.Values["cameraLockY"] = false; bossData.Values["patternIndex"] = Settings.BadelineAttackPattern == 0 ? patternRandomizer.Next(1, 16) : Settings.BadelineAttackPattern; bossData.Nodes = nodes; // add it to the level! level.Add(new AutoDestroyingBadelineBoss(bossData, Vector2.Zero)); } } level.Entities.UpdateLists(); } }
private float determineBadelineLag() { return(ExtendedVariantsModule.ShouldIgnoreCustomDelaySettings() || Settings.BadelineLag == 0 ? 1.55f : Settings.BadelineLag / 10f); }