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.AttrInt("checkpointID", -1) == -1)
                  {
                      entity.SetAttr("checkpointID", Checkpoint);
                  }
                  if (entity.AttrInt("order", -1) == -1)
                  {
                      entity.SetAttr("order", StrawberryInCheckpoint);
                  }
                  Strawberry++;
                  StrawberryInCheckpoint++;
              } }
        };
Esempio n. 2
0
        private void ProcessLevels(BinaryPacker.Element levels)
        {
            AreaData       area = AreaData.Get(Area);
            ModeProperties mode = area.Mode[(int)Area.Mode];

            // Mod levels are... different.

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

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

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

            int strawberry             = 0;
            int strawberryInCheckpoint = 0;

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

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

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

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

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

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

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

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

                    foreach (BinaryPacker.Element levelChild in level.Children)
                    {
                        switch (levelChild.Name)
                        {
                        case "entities":
                            foreach (BinaryPacker.Element entity in levelChild.Children)
                            {
                                switch (entity.Name)
                                {
                                case "checkpoint":
                                    if (checkpointsAuto != null)
                                    {
                                        MapMeta        modeMeta = area.GetModeMeta(Area.Mode);
                                        CheckpointData c        = new CheckpointData(
                                            levelName,
                                            (area.GetSID() + "_" + levelName).DialogKeyify(),
                                            MapMeta.GetInventory(entity.Attr("inventory")),
                                            entity.Attr("dreaming") == "" ? modeMeta.Dreaming ?? area.Dreaming : entity.AttrBool("dreaming"),
                                            null
                                            );
                                        if (entity.Attr("coreMode") == "")
                                        {
                                            c.CoreMode = modeMeta.CoreMode ?? area.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;
                                    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();
            }
        }
Esempio n. 3
0
        private void ProcessLevels(BinaryPacker.Element levels)
        {
            AreaData       area = AreaData.Get(Area);
            ModeProperties mode = area.Mode[(int)Area.Mode];

            // Mod levels are... different.

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

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

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

            int strawberry             = 0;
            int strawberryInCheckpoint = 0;

            foreach (BinaryPacker.Element level in levels.Children)
            {
                string[] levelTags = level.Attr("name").Split(':');
                level.Attributes["name"] = levelTags[0];

                string levelName = level.Attr("name").Replace("lvl_", "");

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

                if (levelTags.Contains("checkpoint") || levelTags.Contains("cp"))
                {
                    entities.Children.Add(new BinaryPacker.Element {
                        Name       = "checkpoint",
                        Attributes =
                        {
                            { "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.Attributes["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 = (int)level.Attributes["height"];
                        heightForCenter /= 8;
                        if (heightForCenter % 2 == 0)
                        {
                            heightForCenter--;
                        }
                        heightForCenter *= 8;

                        triggers.Children.Add(new BinaryPacker.Element {
                            Name       = "cameraTargetTrigger",
                            Attributes =
                            {
                                { "x",                                    0f },
                                { "y",                                    0f },
                                { "width",        level.Attributes["width"]  },
                                { "height",       level.Attributes["height"] },
                                { "yOnly",        true                       },
                                { "lerpStrength",                         1f }
                            },
                            Children =
                            {
                                new BinaryPacker.Element {
                                    Attributes =
                                    {
                                        { "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.Attributes["checkpointID"] = checkpoint;
                                }
                                if (entity.AttrInt("order", -1) == -1)
                                {
                                    entity.Attributes["order"] = strawberryInCheckpoint;
                                }
                                strawberry++;
                                strawberryInCheckpoint++;
                                break;
                            }
                        }
                        break;
                    }
                }
            }

            if (mode.Checkpoints == null)
            {
                mode.Checkpoints = checkpointsAuto.Where(c => c != null).ToArray();
            }
        }