private void GameSelectionPanel_RefreshContent(On.GameSelectionPanel.orig_RefreshContent orig, GameSelectionPanel self) { if (!run) { // Set GameConfig SetGameConfig(); // Need to AT LEAST alter the following: // GameSelectionPanel.Display --> NullReference // Lift.Show() --> IndexOutOfBounds // GameSelectionPanel.Display --> NullReference var d = new DynData <GameSelectionPanel>(self); mod.Log("Before set:"); mod.Log("GameSelectionPanel.maxHeroCount: " + d.Get <int>("maxHeroCount")); mod.Log("GameSelectionPanel.competitorsTable: " + d.Get <AgeTransform>("competitorsTable")); mod.Log("GameSelectionPanel.competitorSlots: " + d.Get <List <CompetitorSlot> >("competitorSlots")); d.Set <int>("maxHeroCount", maxHeroShipCountWrapper.Value); mod.Log("After set:"); mod.Log("GameSelectionPanel.maxHeroCount: " + d.Get <int>("maxHeroCount")); run = true; } orig(self); }
public override void Added(Scene scene) { base.Added(scene); Remove(baseData.Get <TileGrid>("tiles")); Remove(Get <TileInterceptor>()); TileGrid tiles; if (baseData.Get <Modes>("mode") == Modes.Wall) { Level level = SceneAs <Level>(); Rectangle tileBounds = level.Session.MapData.TileBounds; VirtualMap <char> solidsData = level.SolidsData; int x = (int)X / 8 - tileBounds.Left; int y = (int)Y / 8 - tileBounds.Top; tiles = GFX.FGAutotiler.GenerateOverlay(tileMap, x, y, solidsData).TileGrid; } else { tiles = GFX.FGAutotiler.GenerateMap(tileMap, default(Autotiler.Behaviour)).TileGrid; } baseData["tiles"] = tiles; Add(baseData.Get <TileGrid>("tiles")); Add(new TileInterceptor(baseData.Get <TileGrid>("tiles"), false)); }
private void GameSelectionPanel_Display(On.GameSelectionPanel.orig_Display orig, GameSelectionPanel self, bool isMultiplayer, string mpSaveKey, int slotCount, GameSelectionPanel.GameSelectionFinishedHandler onGameSelectionFinished) { // Should happen often enough (before we get to the PodScreen) in order to work out. if (!run) { // Set GameConfig SetGameConfig(); // Need to AT LEAST alter the following: // GameSelectionPanel.Display --> NullReference // Lift.Show() --> IndexOutOfBounds // GameSelectionPanel.Display --> NullReference var d = new DynData <GameSelectionPanel>(self); mod.Log("Before set:"); mod.Log("GameSelectionPanel.maxHeroCount: " + d.Get <int>("maxHeroCount")); mod.Log("GameSelectionPanel.competitorsTable: " + d.Get <AgeTransform>("competitorsTable")); mod.Log("GameSelectionPanel.competitorSlots: " + d.Get <List <CompetitorSlot> >("competitorSlots")); d.Set <int>("maxHeroCount", maxHeroShipCountWrapper.Value); mod.Log("After set:"); mod.Log("GameSelectionPanel.maxHeroCount: " + d.Get <int>("maxHeroCount")); run = true; } orig(self, isMultiplayer, mpSaveKey, slotCount, onGameSelectionFinished); }
public override void Added(Scene scene) { // turn off createdFromLevel to prevent vanilla from spawning ReflectionTentacles. DynData <ReflectionTentacles> self = new DynData <ReflectionTentacles>(this); bool createdFromLevel = self.Get <bool>("createdFromLevel"); self["createdFromLevel"] = false; // run vanilla code. base.Added(scene); // restore the createdFromLevel value. self["createdFromLevel"] = createdFromLevel; // add tentacles like vanilla would, but make them ForegroundReflectionTentacles. if (createdFromLevel) { for (int i = 1; i < 4; i++) { ForegroundReflectionTentacles reflectionTentacles = new ForegroundReflectionTentacles(); reflectionTentacles.Create(self.Get <float>("fearDistance"), self.Get <int>("slideUntilIndex"), i, Nodes); scene.Add(reflectionTentacles); } } // bring all tentacles to the foreground. Depth = -1000000 + self.Get <int>("layer"); }
private void FakeoutHeart(ILContext il) { var cursor = new ILCursor(il); if (!cursor.TryGotoNext(MoveType.After, instr => instr.MatchCallvirt <Player>("get_Dead"))) { throw new Exception("Could not find patching point"); } cursor.EmitDelegate <Func <bool> >(() => { if (!this.InRandomizer) { return(false); } var level = Engine.Scene as Level ?? throw new Exception("what"); var dyn = new DynData <LevelData>(level.Session.LevelData); if (dyn.Get <string>("CustomWarp") == null) { return(false); } var targetLevel = level.Session.MapData.Get(dyn.Get <string>("CustomWarp")); var player = level.Tracker.GetEntity <Player>(); level.Add(new Entity { new Coroutine(this.FakeoutWarp(level, player, targetLevel)) }); return(true); }); cursor.Emit(OpCodes.Or); }
private static void Player_DashBegin(On.Celeste.Player.orig_DashBegin orig, Player self) { orig(self); if (HasDreamTunnelDash) { dreamTunnelDashAttacking = true; dreamTunnelDashTimer = self.GetData().Get <float>("dashAttackTimer"); // Ensures the player enters the dream tunnel dash state if dashing into a fast moving block // Because of how it works, it removes dashdir leniency :( DynData <Player> playerData = self.GetData(); Vector2 lastAim = Input.GetAimVector(self.Facing); Vector2 dir = lastAim.Sign(); if (!self.CollideCheck <Solid, DreamBlock>() && self.CollideCheck <Solid, DreamBlock>(self.Position + dir)) { self.Speed = self.DashDir = lastAim; self.MoveHExact((int)dir.X, playerData.Get <Collision>("onCollideH")); self.MoveVExact((int)dir.Y, playerData.Get <Collision>("onCollideV")); } if (NextDashFeather) { FeatherMode = true; NextDashFeather = false; } } HasDreamTunnelDash = false; }
private static void modDeathSound(ILContext il) { ILCursor cursor = new ILCursor(il); FieldReference refToThis = HookHelper.FindReferenceToThisInCoroutine(cursor); if (cursor.TryGotoNext(MoveType.After, instr => instr.MatchLdstr("event:/new_content/char/madeline/death_golden"))) { Logger.Log("CollabUtils2/StrawberryHooks", $"Modding golden death sound at {cursor.Index} in IL for PlayerDeadBody.DeathRoutine"); // we want to replace the vanilla death sound with the silver berry one if carrying a silver berry. cursor.Emit(OpCodes.Ldarg_0); cursor.Emit(OpCodes.Ldfld, refToThis); cursor.EmitDelegate <Func <string, PlayerDeadBody, string> >((orig, self) => { DynData <PlayerDeadBody> data = new DynData <PlayerDeadBody>(self); bool hasSilver = data.Get <bool>("hasSilver"); bool hasSpeedBerry = data.Get <bool>("hasSpeedBerry"); if (hasSilver && hasSpeedBerry) { return("event:/SC2020_silverTimedBerry_death"); } if (hasSilver) { return("event:/SC2020_silverBerry_death"); } if (hasSpeedBerry) { return("event:/SC2020_timedBerry_death"); } return(orig); }); } }
private static bool onSwitchActivate(On.Celeste.Switch.orig_Activate orig, Switch self) { if (self.Entity.GetType().ToString() == "Celeste.Mod.OutbackHelper.MovingTouchSwitch") { DynData <TouchSwitch> selfData = new DynData <TouchSwitch>((TouchSwitch)self.Entity); if (selfData.Data.ContainsKey("flag")) { DynData <Switch> selfSwitch = new DynData <Switch>(self); string flag = selfData.Get <string>("flag"); Level level = self.Entity.SceneAs <Level>(); // do what the regular Switch.Activate() method does if (self.Finished || self.Activated) { return(false); } selfSwitch["Activated"] = true; if (self.OnActivate != null) { self.OnActivate(); } // use the same logic as flag touch switches to determine if the group is complete. return(FlagTouchSwitch.HandleCollectedFlagTouchSwitch(flag, inverted: false, selfData.Get <bool>("persistent"), level, selfData.Get <int>("id"), selfData.Get <List <FlagTouchSwitch> >("allTouchSwitchesInRoom"), selfData.Get <List <TouchSwitch> >("allMovingFlagTouchSwitchesInRoom"), () => { })); } } // if we are here, it means we aren't dealing with a flag moving touch switch. // so, we want regular behavior! return(orig(self)); }
public IEnumerator RefillRoutine(Player player) { Celeste.Freeze(0.025f); baseData.Get <Sprite>("sprite").Visible = baseData.Get <Sprite>("flash").Visible = false; bool oneUse = baseData.Get <bool>("oneUse"); if (!oneUse) { baseData.Get <Image>("outline").Visible = true; } yield return(0.05); player.StateMachine.State = 0; float angle = player.Speed.Angle(); Level level = baseData.Get <Level>("level"); level.ParticlesFG.Emit(P_Shatter, 5, Position, Vector2.One * 4f, angle - (float)Math.PI / 2f); level.ParticlesFG.Emit(P_Shatter, 5, Position, Vector2.One * 4f, angle + (float)Math.PI / 2f); SlashFx.Burst(Position, angle); if (oneUse) { RemoveSelf(); } }
public FancyFinalBossMovingBlock(EntityData data, Vector2 offset) : base(data.NodesWithPosition(offset), data.Width, data.Height, data.Int("nodeIndex", 0)) { baseData = new DynData <FinalBossMovingBlock>(this); Remove(baseData.Get <TileGrid>("sprite")); Remove(baseData.Get <TileGrid>("highlight")); Remove(Get <TileInterceptor>()); badLightOcclude = Get <LightOcclude>(); tileMap = GenerateTileMap(data.Attr("tileData", "")); tileMapHighlighted = GenerateTileMap(data.Attr("tileDataHighlight", "")); int newSeed = Calc.Random.Next(); Calc.PushRandom(newSeed); baseData["sprite"] = GFX.FGAutotiler.GenerateMap(tileMap, default(Autotiler.Behaviour)).TileGrid; Add(baseData.Get <TileGrid>("sprite")); Calc.PopRandom(); Calc.PushRandom(newSeed); TileGrid highlight = GFX.FGAutotiler.GenerateMap(tileMapHighlighted, default(Autotiler.Behaviour)).TileGrid; highlight.Alpha = 0f; baseData["highlight"] = highlight; Add(baseData.Get <TileGrid>("highlight")); Calc.PopRandom(); Add(new TileInterceptor(baseData.Get <TileGrid>("sprite"), false)); highlightCollider = GenerateBetterColliderGrid(tileMapHighlighted, 8, 8); collider = GenerateBetterColliderGrid(tileMap, 8, 8); AddLightOcclude(this, collider); Collider = collider; }
public override void Added(Scene scene) { // if the gate was already opened on that save or in that session, open the door right away by setting the flag. (scene as Level).Session.SetFlag("opened_heartgem_door_" + Requires, (scene as Level).Session.GetFlag("opened_mini_heart_door_" + entityID) || CollabModule.Instance.SaveData.OpenedMiniHeartDoors.Contains(GetDoorSaveDataID(scene))); base.Added(scene); DynData <HeartGemDoor> self = new DynData <HeartGemDoor>(this); TopSolid = self.Get <Solid>("TopSolid"); BottomSolid = self.Get <Solid>("BotSolid"); // resize the gate: it shouldn't take the whole screen height. TopSolid.Collider.Height = height; BottomSolid.Collider.Height = height; TopSolid.Top = Y - height; BottomSolid.Bottom = Y + height; if (Opened) { // place the blocks correctly in an open position. float openDistance = self.Get <float>("openDistance"); TopSolid.Collider.Height -= openDistance; BottomSolid.Collider.Height -= openDistance; BottomSolid.Top += openDistance; } }
public override void Render() { SamplerState before = null; Matrix beforeMatrix = default; if (cleanSampling || respectScreenShake) { Draw.SpriteBatch.End(); before = spriteBatchData.Get <SamplerState>("samplerState"); beforeMatrix = spriteBatchData.Get <Matrix>("transformMatrix"); Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, spriteBatchData.Get <BlendState>("blendState"), cleanSampling ? SamplerState.PointClamp : before, spriteBatchData.Get <DepthStencilState>("depthStencilState"), spriteBatchData.Get <RasterizerState>("rasterizerState"), spriteBatchData.Get <Effect>("customEffect"), beforeMatrix * (respectScreenShake ? Matrix.CreateTranslation(new Vector3(-level.ShakeVector.X, -level.ShakeVector.Y, 0) * 6) : Matrix.Identity)); } base.Render(); if (cleanSampling) { Draw.SpriteBatch.End(); Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, spriteBatchData.Get <BlendState>("blendState"), before, spriteBatchData.Get <DepthStencilState>("depthStencilState"), spriteBatchData.Get <RasterizerState>("rasterizerState"), spriteBatchData.Get <Effect>("customEffect"), beforeMatrix); } }
private static void ModHitbox(On.Monocle.Entity.orig_DebugRender orig, Entity self, Camera camera) { if (!CelesteTasModule.Settings.ShowHitboxes) { orig(self, camera); return; } orig(self, camera); if (self is FinalBossBeam finalBossBeam) { DynData <FinalBossBeam> dynData = finalBossBeam.GetDynDataInstance(); if (dynData.Get <float>("chargeTimer") <= 0f && dynData.Get <float>("activeTimer") > 0f) { FinalBoss boss = dynData.Get <FinalBoss>("boss"); float angle = dynData.Get <float>("angle"); Vector2 vector = boss.BeamOrigin + Calc.AngleToVector(angle, 12f); Vector2 vector2 = boss.BeamOrigin + Calc.AngleToVector(angle, 2000f); Vector2 value = (vector2 - vector).Perpendicular().SafeNormalize(2f); Player player = boss.Scene.CollideFirst <Player>(vector + value, vector2 + value); Draw.Line(vector + value, vector2 + value, Color.Aqua); Draw.Line(vector - value, vector2 - value, Color.Aqua); Draw.Line(vector, vector2, Color.Aqua); } } }
public FancyFallingBlock(EntityData data, Vector2 offset) : base(data.Position + offset, '3', data.Width, data.Height, data.Bool("finalBoss", false), data.Bool("behind", false), data.Bool("climbFall", true)) { baseData = new DynData <FallingBlock>(this); Remove(baseData.Get <TileGrid>("tiles")); Remove(Get <TileInterceptor>()); badLightOcclude = Get <LightOcclude>(); int newSeed = Calc.Random.Next(); Calc.PushRandom(newSeed); tileMap = GenerateTileMap(data.Attr("tileData", "")); Autotiler.Generated generated = GFX.FGAutotiler.GenerateMap(tileMap, default(Autotiler.Behaviour)); baseData["tiles"] = generated.TileGrid; Add(baseData.Get <TileGrid>("tiles")); Add(animatedTiles = generated.SpriteOverlay); Calc.PopRandom(); if (data.Bool("finalBoss", false)) { VirtualMap <char> tileMapHighlighted = GenerateTileMap(data.Attr("tileDataHighlight", "")); Calc.PushRandom(newSeed); TileGrid highlight = GFX.FGAutotiler.GenerateMap(tileMapHighlighted, default(Autotiler.Behaviour)).TileGrid; highlight.Alpha = 0f; baseData["highlight"] = highlight; Add(baseData.Get <TileGrid>("highlight")); Calc.PopRandom(); } ColliderList colliders = GenerateBetterColliderGrid(tileMap, 8, 8); AddLightOcclude(this, colliders); Collider = colliders; Add(new TileInterceptor(baseData.Get <TileGrid>("tiles"), false)); }
private static bool PlayerUseRefill(On.Celeste.Player.orig_UseRefill orig, Player self, bool twoDashes) { DynData <Player> selfData = new DynData <Player>(self); if (selfData.Data.ContainsKey("DJMapHelper_RefillColor") && selfData["DJMapHelper_RefillColor"] != null) { bool result; switch (selfData.Get <RefillColor>("DJMapHelper_RefillColor")) { case RefillColor.Red: result = PlayerUseRedRefill(self); break; case RefillColor.Blue: result = PlayerUseBlueRefill(self); break; case RefillColor.Black: result = PlayerUseBlackRefill(self); break; default: throw new ArgumentOutOfRangeException(nameof(color), selfData.Get <RefillColor>("DJMapHelper_RefillColor"), null); } selfData["DJMapHelper_RefillColor"] = null; return(result); } else { return(orig(self, twoDashes)); } }
private void GameSelectionPanel_Display(On.GameSelectionPanel.orig_Display orig, GameSelectionPanel self, bool isMultiplayer, string mpSaveKey, int slotCount, GameSelectionPanel.GameSelectionFinishedHandler onGameSelectionFinished) { Log("Attempting to Display!"); IDatabase <ShipConfig> db = Databases.GetDatabase <ShipConfig>(false); Log("Database loaded!"); if (db.GetValue(this.GetConfig().Name) != null) { // It already exists within the database, so we can continue Log("The shipconfig with name: " + this.GetConfig().Name + " already exists inside the database!"); } else { db.Add(this.GetConfig()); } DynData <GameSelectionPanel> paneldyn = new DynData <GameSelectionPanel>(self); paneldyn.Set <IDatabase <ShipConfig> >("shipConfigDB", db); Log("Database set!"); orig(self, isMultiplayer, mpSaveKey, slotCount, onGameSelectionFinished); Log("Available Ships:"); foreach (string s in paneldyn.Get <List <Amplitude.StaticString> >("availableShips")) { Log(s); } Log("DB:"); foreach (ShipConfig s in paneldyn.Get <IDatabase <ShipConfig> >("shipConfigDB")) { Log("Name: " + s.Name + " localized: " + s.GetLocalizedName() + " desc: " + s.GetLocalizedDescription() + " abscissa val: " + s.AbscissaValue); } }
public DreamBlockDebris Init(Vector2 pos, DreamBlockDummy block = null) { orig_Init(pos, '1'); Block = block ?? new DreamBlockDummy(this); Image image = baseData.Get <Image>("image"); Remove(image); Sprite sprite = new Sprite(GFX.Game, "objects/CommunalHelper/dreamMoveBlock/"); float speed = Calc.Random.NextFloat(0.3f) + 0.1f; sprite.AddLoop("active", "debris", speed); sprite.AddLoop("disabled", "disabledDebris", speed); sprite.CenterOrigin(); sprite.Color = image.Color; sprite.Rotation = image.Rotation; sprite.Scale = image.Scale; sprite.FlipX = image.FlipX; sprite.FlipY = image.FlipY; Add(sprite); sprite.Play(Block.PlayerHasDreamDash ? "active" : "disabled", randomizeFrame: true); baseData["image"] = this.sprite = sprite; if (Calc.Random.Next(4) == 0) { activePointColor = Calc.Random.Choose(activeParticleColors); disabledPointColor = Color.LightGray * (0.5f + Calc.Random.Choose(0, 1, 1, 2, 2, 2) / 2f * 0.5f); pointOffset = new Vector2(Calc.Random.Next(-2, 2), Calc.Random.Next(-2, 2)); } return(this); }
public override void Update() { base.Update(); // if we are supposed to push the player and the player is hitting us... Player p; if (pushPlayer && (p = CollideFirst <Player>()) != null) { DynData <Player> playerData = new DynData <Player>(p); if (AllowLeftToRight) { // player is moving right, not on the ground, not climbing, not blocked => push them to the right if (p.Speed.X >= 0f && !playerData.Get <bool>("onGround") && (p.StateMachine.State != 1 || playerData.Get <int>("lastClimbMove") == -1) && !((bool)playerJumpthruBoostBlockedCheck.Invoke(p, new object[0]))) { p.MoveH(40f * Engine.DeltaTime); } } else { // player is moving left, not on the ground, not climbing, not blocked => push them to the left if (p.Speed.X <= 0f && !playerData.Get <bool>("onGround") && (p.StateMachine.State != 1 || playerData.Get <int>("lastClimbMove") == -1) && !((bool)playerJumpthruBoostBlockedCheck.Invoke(p, new object[0]))) { p.MoveH(-40f * Engine.DeltaTime); } } } }
protected DashStateRefill(EntityData data, Vector2 offset) : base(data, offset) { baseData = new DynData <Refill>(this); respawnTime = data.Float("respawnTime", 2.5f); // default is 2.5 sec. Get <PlayerCollider>().OnCollide = OnPlayer; if (TryCreateCustomSprite(out Sprite sprite)) { Remove(baseData.Get <Sprite>("sprite")); Add(sprite); baseData["sprite"] = sprite; } if (TryCreateCustomOutline(out Sprite outline)) { Remove(baseData.Get <Sprite>("outline")); Add(outline); baseData["outline"] = outline; } if (TryCreateCustomFlash(out Sprite flash)) { Remove(baseData.Get <Sprite>("flash")); Add(flash); baseData["flash"] = flash; } }
internal static void Break(On.Celeste.CrumbleWallOnRumble.orig_Break orig, CrumbleWallOnRumble self) { if (self is FancyCrumbleWallOnRumble) { if (self.Collidable && self.Scene != null) { FancyCrumbleWallOnRumble block = (self as FancyCrumbleWallOnRumble); Audio.Play("event:/new_content/game/10_farewell/quake_rockbreak", block.Position); block.Collidable = false; for (int x = 0; x < block.Width / 8f; x++) { for (int y = 0; y < block.Height / 8f; y++) { if (!IsEmpty(block.tileMap[x, y]) && !block.Scene.CollideCheck <Solid>(new Rectangle((int)block.X + x * 8, (int)block.Y + y * 8, 8, 8))) { block.Scene.Add(Engine.Pooler.Create <Debris>().Init(block.Position + new Vector2(4 + x * 8, 4 + y * 8), block.tileMap[x, y], true).BlastFrom(block.TopCenter)); } } } DynData <CrumbleWallOnRumble> blockData = new DynData <CrumbleWallOnRumble>(block); if (blockData.Get <bool>("permanent")) { block.SceneAs <Level>().Session.DoNotLoad.Add(blockData.Get <EntityID>("id")); } block.RemoveSelf(); } } else { orig(self); } }
protected void ReplaceSprite(Sprite newSprite) { Sprite oldSprite = BoosterData.Get <Sprite>("sprite"); Remove(oldSprite); BoosterData["sprite"] = newSprite; Add(newSprite); }
public static void CollectedPieces(DynData <HeartGem> heartData) { heartData.Target.Visible = true; heartData.Target.Active = true; heartData.Target.Collidable = true; heartData.Get <BloomPoint>("bloom").Visible = heartData.Get <VertexLight>("light").Visible = true; heartData.Target.SceneAs <Level>().Session.SetFlag(GotShardFlag(heartData)); }
public ReskinnableCrushBlock(EntityData data, Vector2 offset) : base(data, offset) { DynData <CrushBlock> self = new DynData <CrushBlock>(this); bool giant = self.Get <bool>("giant"); Sprite face = self.Get <Sprite>("face"); // rebuild the face in code with the sprites in our custom directory. face.Reset(GFX.Game, spriteDirectory + "/"); if (giant) { /* * <Loop id="idle" path="giant_block" frames="0" delay="0.08"/> * <Anim id="hurt" path="giant_block" frames="8-12" delay="0.08" goto="idle"/> * <Anim id="hit" path="giant_block" frames="0-5" delay="0.08"/> * <Loop id="right" path="giant_block" frames="6,7" delay="0.08"/> */ face.AddLoop("idle", "giant_block", 0.08f, 0); face.Add("hurt", "giant_block", 0.08f, "idle", 8, 9, 10, 11, 12); face.Add("hit", "giant_block", 0.08f, 0, 1, 2, 3, 4, 5); face.AddLoop("right", "giant_block", 0.08f, 6, 7); } else { /* * <Loop id="idle" path="idle_face" delay="0.08"/> * <Anim id="hurt" path="hurt" frames="3-12" delay="0.08" goto="idle"/> * <Anim id="hit" path="hit" delay="0.08"/> * <Loop id="left" path="hit_left" delay="0.08"/> * <Loop id="right" path="hit_right" delay="0.08"/> * <Loop id="up" path="hit_up" delay="0.08"/> * <Loop id="down" path="hit_down" delay="0.08"/> */ face.AddLoop("idle", "idle_face", 0.08f); face.Add("hurt", "hurt", 0.08f, "idle", 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); face.Add("hit", "hit", 0.08f); face.AddLoop("left", "hit_left", 0.08f); face.AddLoop("right", "hit_right", 0.08f); face.AddLoop("up", "hit_up", 0.08f); face.AddLoop("down", "hit_down", 0.08f); } face.CenterOrigin(); face.Play("idle"); // customize the fill color. self["fill"] = Calc.HexToColor(data.Attr("fillColor", "62222b")); crushParticleColor = new ParticleType(P_Crushing) { Color = Calc.HexToColor(data.Attr("crushParticleColor1", "ff66e2")), Color2 = Calc.HexToColor(data.Attr("crushParticleColor2", "68fcff")) }; activateParticleColor = new ParticleType(P_Activate) { Color = Calc.HexToColor(data.Attr("activateParticleColor1", "5fcde4")), Color2 = Calc.HexToColor(data.Attr("activateParticleColor2", "ffffff")) }; }
public static Entity Load(Level level, LevelData levelData, Vector2 offset, EntityData entityData) { string flag = entityData.Attr("flag"); if (level.Session.GetFlag(flag) || level.Session.GetFlag(flag + "_switch" + entityData.ID)) { // moving touch switches can't be persistent, but we can very much spawn a flag touch switch instead! Vector2[] nodes = entityData.Nodes; Vector2 origPosition = entityData.Position; entityData.Position = nodes[nodes.Length - 1]; FlagTouchSwitch flagTouchSwitch = new FlagTouchSwitch(entityData, offset); entityData.Position = origPosition; return(flagTouchSwitch); } else { // build the moving touch switch TouchSwitch movingTouchSwitch = (TouchSwitch)Activator.CreateInstance(movingTouchSwitchType, new object[] { entityData.NodesOffset(offset), entityData, offset }); // save its attributes as DynData DynData <TouchSwitch> switchData = new DynData <TouchSwitch>(movingTouchSwitch); switchData["flag"] = entityData.Attr("flag"); switchData["id"] = entityData.ID; switchData["persistent"] = entityData.Bool("persistent", false); switchData["movingColor"] = Calc.HexToColor(entityData.Attr("movingColor", "FF8080")); // these attributes actually exist in TouchSwitch and as such, they work! switchData["inactiveColor"] = Calc.HexToColor(entityData.Attr("inactiveColor", "5FCDE4")); switchData["activeColor"] = Calc.HexToColor(entityData.Attr("activeColor", "FFFFFF")); switchData["finishColor"] = Calc.HexToColor(entityData.Attr("finishColor", "F141DF")); switchData["P_RecoloredFire"] = new ParticleType(TouchSwitch.P_Fire) { Color = switchData.Get <Color>("finishColor") }; // set up the icon string iconAttribute = entityData.Attr("icon", "vanilla"); Sprite icon = new Sprite(GFX.Game, iconAttribute == "vanilla" ? "objects/touchswitch/icon" : $"objects/MaxHelpingHand/flagTouchSwitch/{iconAttribute}/icon"); icon.Add("idle", "", 0f, default(int)); icon.Add("spin", "", 0.1f, new Chooser <string>("spin", 1f), 0, 1, 2, 3, 4, 5); icon.Play("spin"); icon.Color = switchData.Get <Color>("inactiveColor"); icon.CenterOrigin(); movingTouchSwitch.Remove(movingTouchSwitch.Get <Sprite>()); movingTouchSwitch.Add(icon); movingTouchSwitchIcon.SetValue(movingTouchSwitch, icon); switchData["icon"] = icon; // collect the list of flag touch switches in the room as soon as the entity is awake, like regular flag touch switches. movingTouchSwitch.Add(new TouchSwitchListAttacher(entityData.Attr("flag"))); return(movingTouchSwitch); } }
public override void Added(Scene scene) { base.Added(scene); // fix the bloom to match golden alpha DynData <Strawberry> self = new DynData <Strawberry>(this); self.Get <BloomPoint>("bloom").Alpha = 0.5f; if (CollabMapDataProcessor.SilverBerries.ContainsKey(levelSet)) { int missingBerries = 0; int totalBerries = CollabMapDataProcessor.SilverBerries[levelSet].Count; foreach (KeyValuePair <string, EntityID> requiredSilver in CollabMapDataProcessor.SilverBerries[levelSet]) { // check if the silver was collected. AreaStats stats = SaveData.Instance.GetAreaStatsFor(AreaData.Get(requiredSilver.Key).ToKey()); if (!stats.Modes[0].Strawberries.Contains(requiredSilver.Value)) { // this berry wasn't collected! missingBerries++; } } if (missingBerries != 0) { // some berries are missing, spawn the hologram instead of the berry. HoloRainbowBerry hologram = new HoloRainbowBerry(Position, totalBerries - missingBerries, totalBerries); scene.Add(hologram); RemoveSelf(); } else { // all berries are here! check if we should play the unlock cutscene. if (!CollabModule.Instance.SaveData.CombinedRainbowBerries.Contains((scene as Level).Session.Area.GetSID())) { // spawn the hologram for the animation... HoloRainbowBerry hologram = new HoloRainbowBerry(Position, totalBerries, totalBerries); hologram.Tag = Tags.FrozenUpdate; scene.Add(hologram); // make rainbow berry invisible for now... Visible = false; Collidable = false; self.Get <BloomPoint>("bloom").Visible = (self.Get <VertexLight>("light").Visible = false); // now we wait for the player to enter the trigger. filling the HologramForCutscene field will tell the trigger to create the cutscene. HologramForCutscene = hologram; CutsceneTotalBerries = totalBerries; } } } }
public FancyIntroCrusher(EntityData data, Vector2 offset) : base(data, offset) { baseData = new DynData <IntroCrusher>(this); Remove(baseData.Get <TileGrid>("tilegrid")); tileMap = GenerateTileMap(data.Attr("tileData", "")); Collider = GenerateBetterColliderGrid(tileMap, 8, 8); baseData["tilegrid"] = GFX.FGAutotiler.GenerateMap(tileMap, default(Autotiler.Behaviour)).TileGrid; Add(baseData.Get <TileGrid>("tilegrid")); }
public override void Added(Scene scene) { base.Added(scene); if (notCoreMode) { // revert the activation of fire mode bumper. DynData <Bumper> thisBumper = new DynData <Bumper>(this); thisBumper["fireMode"] = false; thisBumper.Get <Sprite>("spriteEvil").Visible = false; thisBumper.Get <Sprite>("sprite").Visible = true; } }
private static void hookSeekerBarrierParticles(On.Celeste.SeekerBarrier.orig_Update orig, SeekerBarrier self) { float particleDirection = controllerOnScreen?.particleDirection ?? 0f; if (self is CustomSeekerBarrier customBarrier) { particleDirection = customBarrier.particleDirection; } // no need to account for screen transitions: particles are frozen during them. if (particleDirection == 0f) { // default settings: do nothing orig(self); return; } // save all particles DynData <SeekerBarrier> selfData = new DynData <SeekerBarrier>(self); List <Vector2> particles = new List <Vector2>(selfData.Get <List <Vector2> >("particles")); float[] speeds = selfData.Get <float[]>("speeds"); // run vanilla code orig(self); // move particles again ourselves, except on the direction we want. for (int i = 0; i < particles.Count; i++) { // compute new position Vector2 newPosition = particles[i] + Vector2.UnitY.Rotate((float)(particleDirection * Math.PI / 180)) * speeds[i % speeds.Length] * Engine.DeltaTime; // make sure it stays in bounds while (newPosition.X < 0) { newPosition.X += self.Width; } while (newPosition.Y < 0) { newPosition.Y += self.Height; } newPosition.X %= self.Width; newPosition.Y %= self.Height; // replace the particle position particles[i] = newPosition; } // replace them. selfData["particles"] = particles; }
public MoreCustomNPC(EntityData data, Vector2 offset, EntityID id) : base(data, offset, id) { hasDialogue = !string.IsNullOrEmpty(data.Attr("dialogId")); DynData <CustomNPC> npcData = new DynData <CustomNPC>(this); string spriteName = data.Attr("spriteName", ""); if (!string.IsNullOrEmpty(spriteName)) { // replace the NPC texture with a sprite. npcData["textures"] = null; sprite = GFX.SpriteBank.Create(spriteName); sprite.Scale = npcData.Get <Vector2>("scale"); Add(sprite); } string frames = data.Attr("frames", ""); if (!string.IsNullOrEmpty(frames)) { // "textures" currently contains all frames, but we only want some. List <MTexture> npcTextures = npcData.Get <List <MTexture> >("textures"); List <MTexture> allTextures = new List <MTexture>(npcTextures); // clear the texture list, then only add back the textures we want! npcTextures = new List <MTexture>(); npcData["textures"] = npcTextures; foreach (int frame in Calc.ReadCSVIntWithTricks(frames)) { npcTextures.Add(allTextures[frame]); } } if (data.Nodes.Length >= 2) { // the nodes define a "talker zone", with one being the top left, and the other the bottom right. // we're adding 8 to bottom and right because nodes are rendered as 8x8 rectangles in Ahorn, // and we want to take the bottom/right of those rectangles. Vector2[] nodesOffset = data.NodesOffset(offset); float top = Math.Min(nodesOffset[0].Y, nodesOffset[1].Y); float bottom = Math.Max(nodesOffset[0].Y, nodesOffset[1].Y) + 8; float left = Math.Min(nodesOffset[0].X, nodesOffset[1].X); float right = Math.Max(nodesOffset[0].X, nodesOffset[1].X) + 8; talkerZone = new Rectangle((int)(left - Position.X), (int)(top - Position.Y), (int)(right - left), (int)(bottom - top)); } }
private Session onSessionRestart(On.Celeste.Session.orig_Restart orig, Session self, string intoLevel) { Session session = orig(self, intoLevel); if (Settings.AllStrawberriesAreGoldens && Engine.Scene is LevelExit levelExit) { DynData <LevelExit> exitData = new DynData <LevelExit>(levelExit); if (exitData.Get <LevelExit.Mode>("mode") == LevelExit.Mode.GoldenBerryRestart && exitData.Data.ContainsKey("playerInventoryToRestore")) { session.Inventory = exitData.Get <PlayerInventory>("playerInventoryToRestore"); } } return(session); }