private static void Stage2_MakeStartRoom(ref char[][] map, MapMeta meta, List <RoomMeta> rooms) { Random r = new Random((int)DateTime.Now.Ticks); int x = r.Next(0, meta.MapSizeX); int y = r.Next(0, meta.MapSizeY); for (int i = 0; i < meta.StartRoomSize; i++) { for (int j = 0; j < meta.StartRoomSize; j++) { int pan = meta.StartRoomSize / 2; if (x + (i - pan) >= 0 && x + (i - pan) < meta.MapSizeX && y + (j - pan) >= 0 && y + (j - pan) < meta.MapSizeY) { map[x + (i - pan)][y + (j - pan)] = (char)TileType.FLOOR; if ((i - pan == 0) && (j - pan == 0)) { map[x + (i - pan)][y + (j - pan)] = (char)TileType.FLOOR_START; rooms.Add(new RoomMeta { X = x + (i - pan), Y = y + (j - pan), Width = meta.StartRoomSize, Height = meta.StartRoomSize, Connections = 0 }); } } } } }
public override void Update(Scene scene) { AreaData area = -1 < Area && Area < (AreaData.Areas?.Count ?? 0) ? AreaData.Get(Area) : null; MapMeta meta = area?.GetMeta(); bool wasFreeCam = inFreeCameraDebugMode; if (meta?.Mountain?.ShowCore ?? false) { Area = 9; orig_Update(scene); Area = area.ID; } else { orig_Update(scene); } Overworld overworld = scene as Overworld; if (!wasFreeCam && inFreeCameraDebugMode && ( ((overworld.Current ?? overworld.Next) is patch_OuiFileNaming naming && naming.UseKeyboardInput) || ((overworld.Current ?? overworld.Next) is OuiModOptionString stringInput && stringInput.UseKeyboardInput))) { // we turned on free cam mode (by pressing Space) while on an text entry screen using keyboard input... we should turn it back off. inFreeCameraDebugMode = false; } }
private IEnumerator Routine() { if (ErrorMessage != null) { string message = ErrorMessage; ErrorMessage = null; return(ErrorRoutine(message)); } if (AreaData.Get(session) == null) { string message = Dialog.Get("postcard_levelgone") .Replace("((player))", SaveData.Instance.Name) .Replace("((sid))", session.Area.GetSID()) ; return(ErrorRoutine(message)); } AreaData areaData = AreaData.Get(session); MapMeta areaMeta = areaData.GetMeta(); if (areaMeta != null && areaData.GetLevelSet() != "Celeste" && Dialog.Has(areaData.Name + "_postcard") && session.StartedFromBeginning && !fromSaveData && session.Area.Mode == AreaMode.Normal && (!SaveData.Instance.Areas[session.Area.ID].Modes[0].Completed || SaveData.Instance.DebugMode)) { return(EnterWithPostcardRoutine(Dialog.Get(areaData.Name + "_postcard"), areaMeta.PostcardSoundID)); } return(orig_Routine()); }
public static IEnumerator getMetaFile(BmobFile metaFile, Action <List <GameObject> > after) { MapMeta meta = null; yield return(TapOnUtils.downloadFile( metaFile.url, wr => { meta = JsonUtility.FromJson <MapMeta>(wr.downloadHandler.text); })); List <GameObject> objects = new List <GameObject>(); List <IEnumerator> tasks = new List <IEnumerator>(); foreach (MapMeta.PropInfo propInfo in meta.Props) { if (propInfo.type == MapMeta.PropType.Text) { GameObject instance = GameObject.Instantiate(Globals.instance.templetes[0]); instance.tag = "word"; instance.SetActive(false); TextMesh tm = instance.GetComponentInChildren <TextMesh>(); tm.text = propInfo.text; instance.transform.position = new Vector3(propInfo.Position[0], propInfo.Position[1], propInfo.Position[2]); instance.transform.rotation = new Quaternion(propInfo.Rotation[0], propInfo.Rotation[1], propInfo.Rotation[2], propInfo.Rotation[3]); instance.transform.localScale = new Vector3(propInfo.Scale[0], propInfo.Scale[1], propInfo.Scale[2]); objects.Add(instance); } if (propInfo.type == MapMeta.PropType.Texture) { GameObject instance = GameObject.Instantiate(Globals.instance.templetes[1]); instance.tag = "texture"; instance.SetActive(false); Renderer rd = instance.GetComponentInChildren <Renderer>(); instance.transform.position = new Vector3(propInfo.Position[0], propInfo.Position[1], propInfo.Position[2]); instance.transform.rotation = new Quaternion(propInfo.Rotation[0], propInfo.Rotation[1], propInfo.Rotation[2], propInfo.Rotation[3]); instance.transform.localScale = new Vector3(propInfo.Scale[0], propInfo.Scale[1], propInfo.Scale[2]); objects.Add(instance); IEnumerator downloadTexture = TapOnUtils.downloadFile( propInfo.infoUrl, wr_dt => { Texture2D texture = new Texture2D(10, 10); texture.LoadImage(wr_dt.downloadHandler.data); rd.material.mainTexture = texture; }); tasks.Add(downloadTexture); } } foreach (IEnumerator t in tasks) { yield return(t); } if (after != null) { after(objects); } }
private static void Stage3_MakeRooms(ref char[][] map, MapMeta meta, List <RoomMeta> rooms) { // TODO: Try to remove overlapping for (int k = 0; k < meta.MaxRoomNumber;) { RetryRoomPositioning: int x = RandomNumber.Between(0, meta.MapSizeX); int y = RandomNumber.Between(0, meta.MapSizeY); Point pt = new Point(x, y); for (int i = 0; i < rooms.Count; i++) { if ((pt - new Point(rooms[i].X, rooms[i].Y)).LengthSquared < meta.MinRoomDistance) { //Console.WriteLine("Failed to make room, dist=" + (pt - new Point(rooms[i].X, rooms[i].Y)).LengthSquared // + ", but meta.MinRoomDistance is " + meta.MinRoomDistance); goto RetryRoomPositioning; } } int w = RandomNumber.Between(meta.MinRoomSize, meta.MaxRoomSize); int h = RandomNumber.Between(meta.MinRoomSize, meta.MaxRoomSize); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int panW = w / 2; int panH = h / 2; if (x + (i - panW) >= 0 && x + (i - panW) < meta.MapSizeX && y + (j - panH) >= 0 && y + (j - panH) < meta.MapSizeY) { if (map[x + (i - panW)][y + (j - panH)] != (char)TileType.FLOOR_START) { map[x + (i - panW)][y + (j - panH)] = (char)TileType.FLOOR; } if ((i - panW == 0) && (j - panH == 0)) { rooms.Add(new RoomMeta { X = x + (i - panW), Y = y + (j - panH), Width = w, Height = h, Connections = 0 }); } } } } k++; } }
private static void Stage8_InsertComponent(ref char[][] map, MapMeta meta, List <RoomMeta> rooms, List <string> componentsPath) { // Insert pre built map from filePath, but not near rooms[0] (starting area) foreach (string filePath in componentsPath) { string[] dataRaw = File.ReadAllLines(filePath); var totalRow = dataRaw.Length; if (totalRow < 2) { continue; } var chance = Convert.ToInt32(dataRaw[0]); // get component size int width = dataRaw[1].Length; int height = totalRow - 1; // choose random coordinates RetryComponentPositioning: int x = RandomNumber.Between(0, meta.MapSizeX - width - 1); int y = RandomNumber.Between(0, meta.MapSizeY - height - 1); Point pt = new Point(x, y); if ((pt - new Point(rooms[0].X, rooms[0].Y)).LengthSquared < meta.MinRoomDistance || (pt - new Point(rooms[0].X - width, rooms[0].Y - height)).LengthSquared < meta.MinRoomDistance) { goto RetryComponentPositioning; } // try roll if (RandomNumber.Between(0, (int)(ChancePool / Convert.ToDouble(dataRaw[0]))) == 0) { Console.WriteLine("Inserting component : " + filePath + ", size=" + width + "x" + height); // insert for (int i = 0; i < height; i++) { string line = dataRaw[i + 1]; for (int j = 0; j < width; j++) { map[x + j][y + i] = (char)(line[j] - '0'); } } } // TODO: try connect if isolated return; } }
private static char[][] Stage1_MapInit(MapMeta meta) { char[][] map = new char[meta.MapSizeX][]; for (int i = 0; i < meta.MapSizeX; i++) { map[i] = new char[meta.MapSizeY]; } return(map); }
public void OnLoad(MapEventArgs e) { e.Map.FileName = Path.GetFileNameWithoutExtension(e.FilePath); string filePath = Directory.GetParent(e.FilePath).ToString(); m_mapMeta = m_dataStore.Load <MapMeta>($"{filePath}\\{e.Map.FileName}.MapMeta"); // no file was found, create new map if (m_mapMeta == null) { m_mapMeta = new MapMeta(); } }
private static bool resolveModel(MapMeta meta, string modelName, out ModAsset matchingAsset, out string path) { if (Everest.Content.TryGet(Path.Combine(meta.Mountain.MountainModelDirectory, modelName + ".obj"), out matchingAsset) && matchingAsset.Type == typeof(AssetTypeObjModelExport)) { path = Path.Combine(meta.Mountain.MountainModelDirectory, modelName + ".export").Replace("\\", "/"); return(true); } else if (Everest.Content.TryGet(Path.Combine(meta.Mountain.MountainModelDirectory, modelName), out matchingAsset) && matchingAsset.Type == typeof(ObjModel)) { path = Path.Combine(meta.Mountain.MountainModelDirectory, modelName + ".obj").Replace("\\", "/"); return(true); } path = null; return(false); }
public override void Update(Scene scene) { AreaData area = -1 < Area && Area < (AreaData.Areas?.Count ?? 0) ? AreaData.Get(Area) : null; MapMeta meta = area?.GetMeta(); if (meta?.Mountain?.ShowCore ?? false) { Area = 9; orig_Update(scene); Area = area.ID; } else { orig_Update(scene); } }
private static void Stage7_MakePrefab( ref char[][] map, MapMeta meta, List <Corridors> corridors, List <Prefabs> prefabs) { // If tile is Floor, have chance to make something bool[][] visited = new bool[meta.MapSizeX][]; for (int i = 0; i < meta.MapSizeX; i++) { visited[i] = new bool[meta.MapSizeY]; } for (int i = 0; i < corridors.Count; i++) { Stack <Point> stack = corridors[i].CorridorData; for (int j = 0; j < stack.Count; j++) { Point pt = stack.Pop(); // Not sure why this sentence hit if (pt.X + 1 > meta.MapSizeX || pt.X < 0 || pt.Y + 1 > meta.MapSizeY || pt.Y < 0) { continue; } if (map[(int)pt.X][(int)pt.Y] == (char)TileType.FLOOR && visited[(int)pt.X][(int)pt.Y] == false) { for (int k = 0; k < prefabs.Count; k++) { if (RandomNumber.Between(0, (int)(ChancePool / (double)prefabs[k].SpawnChance)) == 0) { map[(int)pt.X][(int)pt.Y] = (char)prefabs[k].Tile; } visited[(int)pt.X][(int)pt.Y] = true; } } } } }
private static void Stage6_ConnectionValidate( ref char[][] map, MapMeta meta, List <RoomMeta> rooms, List <Corridors> corridors) { bool[] connected = new bool[rooms.Count]; LinkedList <int> list = new LinkedList <int>(); for (int i = 1; i < connected.Length; i++) { if (connected[i] == false) { Aux_RoomValidationUpdate(ref connected, list, corridors); Stage4_ConnectRooms(ref map, meta, rooms, corridors, 0, i); connected[i] = true; } } }
private static void Stage5_MakeCorridorBranch( ref char[][] map, MapMeta meta, List <RoomMeta> rooms, List <Corridors> corridors) { // TODO: Beautify random corridors // Make branches of exist corridors for (int i = 0; i < corridors.Count; i++) { Stack <Point> stack = corridors[i].CorridorData; if (corridors.Count > meta.MaxCorridorBranchCount) { break; } for (int j = 0; stack.Count > 0; j++) { Point coord = stack.Pop(); if (coord.X < 0 || coord.X + 1 > meta.MapSizeX || coord.Y < 0 || coord.Y + 1 > meta.MapSizeY) { continue; } if (corridors[i].IsBranch == false && RandomNumber.Between(0, meta.RouteBranchFactor) == 0) { Aux_MakeRandomCorridor(ref map, meta, coord, corridors); } else if (corridors[i].IsBranch == true && RandomNumber.Between(0, meta.RouteAdditionalBranchFactor) == 0) { Aux_MakeRandomCorridor(ref map, meta, coord, corridors); } } } }
private void ProcessMeta(BinaryPacker.Element meta) { AreaData area = AreaData.Get(Area); AreaMode mode = Area.Mode; if (mode == AreaMode.Normal) { new MapMeta(meta).ApplyTo(area); Area = area.ToKey(); // Backup A-Side's Metadata. Only back up useful data. area.SetASideAreaDataBackup(new AreaData { IntroType = area.IntroType, ColorGrade = area.ColorGrade, DarknessAlpha = area.DarknessAlpha, BloomBase = area.BloomBase, BloomStrength = area.BloomStrength, CoreMode = area.CoreMode, Dreaming = area.Dreaming }); } BinaryPacker.Element modeMeta = meta.Children?.FirstOrDefault(el => el.Name == "mode"); if (modeMeta == null) { return; } new MapMetaModeProperties(modeMeta).ApplyTo(area, mode); // Metadata for B-Side and C-Side are parsed and stored. if (mode != AreaMode.Normal) { MapMeta mapMeta = new MapMeta(meta) { Modes = area.GetMeta().Modes }; area.Mode[(int)mode].SetMapMeta(mapMeta); } }
public static void SetMapMeta(this ModeProperties self, MapMeta value) => ((patch_ModeProperties)self).MapMeta = value;
private static void Stage4_ConnectRooms( ref char[][] map, MapMeta meta, List <RoomMeta> rooms, List <Corridors> corridors, int overrideRoomStart, int overrideRoomTarget) { bool completeFlag = false; bool overrideFlag = false; if (overrideRoomStart != 0 || overrideRoomTarget != 0) { overrideFlag = true; } if (meta.MaxRoomNumber != rooms.Count) { meta.MaxRoomNumber = rooms.Count; } while (completeFlag == false) { Stack <Point> stack = new Stack <Point>(); int targetRoom = RandomNumber.Between(1, meta.MaxRoomNumber - 1); int startRoom = 0; int maxConn = RandomNumber.Between(1, meta.MaxRoomConnection); for (int i = 0; i < rooms.Count; i++) { if (rooms[i].Connections < maxConn) { startRoom = i; break; } // there is no more room that 0 connection if (i == rooms.Count - 1) { completeFlag = true; } } if (overrideFlag) { startRoom = overrideRoomStart; targetRoom = overrideRoomTarget; completeFlag = true; } // starting position stack.Push(new Point { X = rooms[startRoom].X, Y = rooms[startRoom].Y }); Point start = stack.Pop(); int curX = (int)start.X; int curY = (int)start.Y; double dist = double.MaxValue; int cs = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); int headTo = 0; while (dist > 1.412) { // Readjust point that out of bound double distN = (new Point(curX, curY + 1) - new Point(rooms[targetRoom].X, rooms[targetRoom].Y)).LengthSquared; double distS = (new Point(curX, curY - 1) - new Point(rooms[targetRoom].X, rooms[targetRoom].Y)).LengthSquared; double distW = (new Point(curX - 1, curY) - new Point(rooms[targetRoom].X, rooms[targetRoom].Y)).LengthSquared; double distE = (new Point(curX + 1, curY) - new Point(rooms[targetRoom].X, rooms[targetRoom].Y)).LengthSquared; double min = Math.Min(distE, Math.Min(distW, Math.Min(distN, distS))); // Corridor twister here (set on meta) - 0, 4 = 25% int rb = RandomNumber.Between(0, meta.RouteTwistFactor); if (rb == 0) { int rc = RandomNumber.Between(0, 4); if (rc == N) { min = distN; } else if (rc == S) { min = distS; } else if (rc == W) { min = distW; } else if (rc == E) { min = distE; } } dist = min; // Boundary check if (curX > meta.MapSizeX) { curX--; continue; } else if (curY > meta.MapSizeY) { curY--; continue; } else if (curX - 1 < 0) { curX++; continue; } else if (curY - 1 < 0) { curY++; continue; } // Push cursor if (min == distN) { stack.Push(new Point { X = curX, Y = ++curY }); headTo = N; } if (min == distS) { stack.Push(new Point { X = curX, Y = --curY }); headTo = S; } if (min == distW) { stack.Push(new Point { X = --curX, Y = curY }); headTo = W; } if (min == distE) { stack.Push(new Point { X = ++curX, Y = curY }); headTo = E; } // Make corridor straight int straight = RandomNumber.Between(0, meta.RouteDirectFactor); if (straight == 0) { cs = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); switch (headTo) { case N: for (int i = 0; i < cs; i++) { stack.Push(new Point { X = curX, Y = ++curY }); } break; case S: for (int i = 0; i < cs; i++) { stack.Push(new Point { X = curX, Y = --curY }); } break; case W: for (int i = 0; i < cs; i++) { stack.Push(new Point { X = --curX, Y = curY }); } break; case E: for (int i = 0; i < cs; i++) { stack.Push(new Point { X = ++curX, Y = curY }); } break; default: break; } } } // Save corridors data Stack <Point> tmp = new Stack <Point>(); tmp = CopyStack.Clone(stack); Corridors crd = new Corridors { CorridorData = tmp, IsBranch = false, CorridorStartAt = startRoom, CorridorEndAt = targetRoom, }; corridors.Add(crd); // Draw path while (stack.Count > 0) { Point pt = stack.Pop(); if (pt.X < meta.MapSizeX && pt.X >= 0 && pt.Y < meta.MapSizeY && pt.Y >= 0) { if (map[(int)pt.X][(int)pt.Y] != (char)TileType.FLOOR_START) { map[(int)pt.X][(int)pt.Y] = (char)TileType.FLOOR; } } } // Add connections rooms[startRoom].Connections++; rooms[targetRoom].Connections++; } }
private static bool hasCustomSettings(MapMeta meta) { return(!string.IsNullOrEmpty(meta.Mountain.MountainModelDirectory) || !string.IsNullOrEmpty(meta.Mountain.MountainTextureDirectory) || meta.Mountain.FogColors != null || !string.IsNullOrEmpty(meta.Mountain.StarFogColor) || meta.Mountain.StarStreamColors != null || (meta.Mountain.StarBeltColors1 != null && meta.Mountain.StarBeltColors2 != null)); }
private static int AreaComparison(AreaData a, AreaData b) { string aSet = a.GetLevelSet(); string aSID = a.GetSID(); MapMeta aMeta = a.GetMeta(); string bSet = b.GetLevelSet(); string bSID = b.GetSID(); MapMeta bMeta = b.GetMeta(); // Celeste appears before everything else. if (aSet == "Celeste" && bSet != "Celeste") { return(-1); } if (aSet != "Celeste" && bSet == "Celeste") { return(1); } // Uncategorized appears after everything else. if (string.IsNullOrEmpty(aSet) && !string.IsNullOrEmpty(bSet)) { return(1); } if (!string.IsNullOrEmpty(aSet) && string.IsNullOrEmpty(bSet)) { return(-1); } // Compare level sets alphabetically. if (aSet != bSet) { return(string.Compare(aSet, bSet)); } // Put "parented" levels at the end. if (!string.IsNullOrEmpty(aMeta?.Parent) && string.IsNullOrEmpty(bMeta?.Parent)) { return(1); } if (string.IsNullOrEmpty(aMeta?.Parent) && !string.IsNullOrEmpty(bMeta?.Parent)) { return(-1); } int? aOrder; AreaMode aSide; string aName; ParseName(aSID, out aOrder, out aSide, out aName); int? bOrder; AreaMode bSide; string bName; ParseName(bSID, out bOrder, out bSide, out bName); // put the "unordered" levels at the end. (Farewell is one of them.) if (aOrder != null && bOrder == null) { return(-1); } if (aOrder == null && bOrder != null) { return(1); } // order the rest by order, then by name, then by side if (aOrder != null && bOrder != null && aOrder.Value != bOrder.Value) { return(aOrder.Value - bOrder.Value); } if (aName != bName) { return(string.Compare(aName, bName)); } if (aSide != bSide) { return(aSide - bSide); } // everything is the same: this is the same level return(0); }
private void ProcessLevels(BinaryPacker.Element levels) { AreaData area = AreaData.Get(Area); ModeProperties mode = area.Mode[(int)Area.Mode]; // Mod levels are... different. // levels.Children.Sort((a, b) => a.Attr("name").Replace("lvl_", "").CompareTo(b.Attr("name").Replace("lvl_", ""))); int checkpoint = 0; List <CheckpointData> checkpointsAuto = null; if (mode.Checkpoints == null) { checkpointsAuto = new List <CheckpointData>(); } int strawberry = 0; int strawberryInCheckpoint = 0; if (levels.Children != null) { foreach (BinaryPacker.Element level in levels.Children) { string[] levelTags = level.Attr("name").Split(':'); string levelName = levelTags[0]; if (levelName.StartsWith("lvl_")) { levelName = levelName.Substring(4); } level.SetAttr("name", "lvl_" + levelName); // lvl_ was optional before Celeste 1.2.5.0 made it mandatory. BinaryPacker.Element entities = level.Children.FirstOrDefault(el => el.Name == "entities"); BinaryPacker.Element triggers = level.Children.FirstOrDefault(el => el.Name == "triggers"); // Celeste 1.2.5.0 optimizes BinaryPacker, which causes some issues. // Let's "unoptimize" entities and triggers. if (entities == null) { level.Children.Add(entities = new BinaryPacker.Element { Name = "entities" }); } if (entities.Children == null) { entities.Children = new List <BinaryPacker.Element>(); } if (triggers == null) { level.Children.Add(triggers = new BinaryPacker.Element { Name = "triggers" }); } if (triggers.Children == null) { triggers.Children = new List <BinaryPacker.Element>(); } if (levelTags.Contains("checkpoint") || levelTags.Contains("cp")) { entities.Children.Add(new BinaryPacker.Element { Name = "checkpoint", Attributes = new Dictionary <string, object>() { { "x", "0" }, { "y", "0" } } }); } if (level.AttrBool("space")) { if (level.AttrBool("spaceSkipWrap") || levelTags.Contains("nospacewrap") || levelTags.Contains("nsw")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceControllerBlocker" }); } if (level.AttrBool("spaceSkipGravity") || levelTags.Contains("nospacegravity") || levelTags.Contains("nsg")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceController" }); level.SetAttr("space", false); } if (!levelTags.Contains("nospacefix") && !levelTags.Contains("nsf") && !triggers.Children.Any(el => el.Name == "cameraTargetTrigger") && !entities.Children.Any(el => el.Name == "everest/spaceControllerBlocker")) { // Camera centers tile-perfectly on uneven heights. int heightForCenter = level.AttrInt("height"); heightForCenter /= 8; if (heightForCenter % 2 == 0) { heightForCenter--; } heightForCenter *= 8; triggers.Children.Add(new BinaryPacker.Element { Name = "cameraTargetTrigger", Attributes = new Dictionary <string, object>() { { "x", 0f }, { "y", 0f }, { "width", level.AttrInt("width") }, { "height", level.AttrInt("height") }, { "yOnly", true }, { "lerpStrength", 1f } }, Children = new List <BinaryPacker.Element>() { new BinaryPacker.Element { Attributes = new Dictionary <string, object>() { { "x", 160f }, { "y", heightForCenter / 2f } } } } }); } } foreach (BinaryPacker.Element levelChild in level.Children) { switch (levelChild.Name) { case "entities": foreach (BinaryPacker.Element entity in levelChild.Children) { switch (entity.Name) { case "checkpoint": if (checkpointsAuto != null) { CheckpointData c = new CheckpointData( levelName, (area.GetSID() + "_" + levelName).DialogKeyify(), MapMeta.GetInventory(entity.Attr("inventory")), entity.Attr("dreaming") == "" ? area.Dreaming : entity.AttrBool("dreaming"), null ); int id = entity.AttrInt("checkpointID", -1); if (id == -1) { checkpointsAuto.Add(c); } else { while (checkpointsAuto.Count <= id) { checkpointsAuto.Add(null); } checkpointsAuto[id] = c; } } checkpoint++; strawberryInCheckpoint = 0; break; case "cassette": if (area.CassetteCheckpointIndex == 0) { area.CassetteCheckpointIndex = checkpoint; } break; case "strawberry": if (entity.AttrInt("checkpointID", -1) == -1) { entity.SetAttr("checkpointID", checkpoint); } if (entity.AttrInt("order", -1) == -1) { entity.SetAttr("order", strawberryInCheckpoint); } strawberry++; strawberryInCheckpoint++; break; } } break; } } } } if (mode.Checkpoints == null) { mode.Checkpoints = checkpointsAuto.Where(c => c != null).ToArray(); } }
public void ctor(Session session, Vector2?startPosition = default(Vector2?)) { if (CoreModule.Settings.LazyLoading) { VirtualContentExt.UnloadOverworld(); } // Vanilla TileToIndex mappings. SurfaceIndex.TileToIndex = new Dictionary <char, int> { { '1', 3 }, { '3', 4 }, { '4', 7 }, { '5', 8 }, { '6', 8 }, { '7', 8 }, { '8', 8 }, { '9', 13 }, { 'a', 8 }, { 'b', 23 }, { 'c', 8 }, { 'd', 8 }, { 'e', 8 }, { 'f', 8 }, { 'g', 8 }, { 'h', 33 }, { 'i', 4 }, { 'j', 8 }, { 'k', 3 }, { 'l', 33 }, { 'm', 3 } }; AreaData area = AreaData.Get(session); MapMeta meta = area.GetMeta(); string path; path = meta?.BackgroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "BackgroundTiles.xml"); } GFX.BGAutotiler = new Autotiler(path); path = meta?.ForegroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "ForegroundTiles.xml"); } GFX.FGAutotiler = new Autotiler(path); path = meta?.AnimatedTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "AnimatedTiles.xml"); } GFX.AnimatedTilesBank = new AnimatedTilesBank(); XmlElement animatedData = Calc.LoadContentXML(path)["Data"]; foreach (XmlElement el in animatedData) { if (el != null) { GFX.AnimatedTilesBank.Add( el.Attr("name"), el.AttrFloat("delay", 0f), el.AttrVector2("posX", "posY", Vector2.Zero), el.AttrVector2("origX", "origY", Vector2.Zero), GFX.Game.GetAtlasSubtextures(el.Attr("path")) ); } } path = meta?.Sprites; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Sprites.xml"); } GFX.SpriteBank = new SpriteBank(GFX.Game, path); path = meta?.Portraits; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Portraits.xml"); } GFX.PortraitsSpriteBank = new SpriteBank(GFX.Portraits, path); orig_ctor(session, startPosition); }
public static new void Load() { orig_Load(); foreach (AreaData area in Areas) { area.SetSID("Celeste/" + area.Mode[0].Path); } // Separate array as we sort it afterwards. List <AreaData> modAreas = new List <AreaData>(); foreach (ModAsset asset in Everest.Content.ListMaps) { string path = asset.PathMapped.Substring(5); MapMeta meta = asset.GetMeta <MapMeta>(); AreaData area = new AreaData(); // Default values. area.SetSID(path); area.Name = path; area.Icon = "areas/" + path.ToLowerInvariant(); if (!GFX.Gui.Has(area.Icon)) { area.Icon = "areas/null"; } area.TitleBaseColor = Calc.HexToColor("6c7c81"); area.TitleAccentColor = Calc.HexToColor("2f344b"); area.TitleTextColor = Color.White; area.IntroType = Player.IntroTypes.WakeUp; area.Dreaming = false; area.ColorGrade = null; area.Mode = new ModeProperties[] { new ModeProperties { Path = asset.PathMapped.Substring(5), Inventory = PlayerInventory.Default, AudioState = new AudioState(Sfxs.music_city, Sfxs.env_amb_00_main) } }; area.Wipe = (Scene scene, bool wipeIn, Action onComplete) => new AngledWipe(scene, wipeIn, onComplete); area.DarknessAlpha = 0.05f; area.BloomBase = 0f; area.BloomStrength = 1f; area.Jumpthru = "wood"; area.CassseteNoteColor = Calc.HexToColor("33a9ee"); area.CassetteSong = Sfxs.cas_01_forsaken_city; // Custom values. if (meta != null) { if (!string.IsNullOrEmpty(meta.Name)) { area.Name = meta.Name; } if (!string.IsNullOrEmpty(meta.SID)) { area.SetSID(meta.SID); } if (!string.IsNullOrEmpty(meta.Icon) && GFX.Gui.Has(meta.Icon)) { area.Icon = meta.Icon; } area.Interlude = meta.Interlude; if (!string.IsNullOrEmpty(meta.CompleteScreenName)) { area.CompleteScreenName = meta.CompleteScreenName; } area.CassetteCheckpointIndex = meta.CassetteCheckpointIndex; if (!string.IsNullOrEmpty(meta.TitleBaseColor)) { area.TitleBaseColor = Calc.HexToColor(meta.TitleBaseColor); } if (!string.IsNullOrEmpty(meta.TitleAccentColor)) { area.TitleAccentColor = Calc.HexToColor(meta.TitleAccentColor); } if (!string.IsNullOrEmpty(meta.TitleTextColor)) { area.TitleTextColor = Calc.HexToColor(meta.TitleTextColor); } area.IntroType = meta.IntroType; area.Dreaming = meta.Dreaming; if (!string.IsNullOrEmpty(meta.ColorGrade)) { area.ColorGrade = meta.ColorGrade; } area.Mode = MapMeta.Convert(meta.Modes) ?? area.Mode; if (!string.IsNullOrEmpty(meta.Wipe)) { Type type = Assembly.GetEntryAssembly().GetType(meta.Wipe); ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) }); if (type != null && ctor != null) { area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete }); } } area.DarknessAlpha = meta.DarknessAlpha; area.BloomBase = meta.BloomBase; area.BloomStrength = meta.BloomStrength; if (!string.IsNullOrEmpty(meta.Jumpthru)) { area.Jumpthru = meta.Jumpthru; } if (!string.IsNullOrEmpty(meta.CassetteNoteColor)) { area.CassseteNoteColor = Calc.HexToColor(meta.CassetteNoteColor); } if (!string.IsNullOrEmpty(meta.CassetteSong)) { area.CassetteSong = meta.CassetteSong; } area.MountainIdle = meta.Mountain?.Idle?.Convert() ?? area.MountainIdle; area.MountainSelect = meta.Mountain?.Select?.Convert() ?? area.MountainSelect; area.MountainZoom = meta.Mountain?.Zoom?.Convert() ?? area.MountainZoom; area.MountainCursor = meta.Mountain?.Cursor?.ToVector3() ?? area.MountainCursor; area.MountainState = meta.Mountain?.State ?? area.MountainState; area.SetCompleteScreenMeta(meta.CompleteScreen); } // Some of the game's code checks for [1] / [2] explicitly. // Let's just provide null modes to fill any gaps. if (area.Mode.Length < 3) { ModeProperties[] larger = new ModeProperties[3]; for (int i = 0; i < area.Mode.Length; i++) { larger[i] = area.Mode[i]; } area.Mode = larger; } // Celeste levelset always appears first. if (area.GetLevelSet() == "Celeste") { Areas.Add(area); } else { modAreas.Add(area); } } // Sort and merge modAreas into Areas. Makes for easier levelset handling. Areas.Sort(AreaComparison); modAreas.Sort(AreaComparison); Areas.AddRange(modAreas); // Find duplicates and remove the earlier copy. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID()); if (otherIndex < i) { Areas[otherIndex] = area; Areas.RemoveAt(i); i--; } } // Remove AreaDatas which are now a mode of another AreaData. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == area.Mode[0].Path)); if (otherIndex != -1 && otherIndex != i) { Areas.RemoveAt(i); i--; } } // Update old MapData areas and load any new areas. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; area.ID = i; if (area.Mode[0].MapData != null) { area.Mode[0].MapData.Area = area.ToKey(); } else { area.Mode[0].MapData = new MapData(area.ToKey()); } if (area.Interlude) { continue; } for (int mode = 1; mode < area.Mode.Length; mode++) { if (area.Mode[mode] == null) { continue; } if (area.Mode[mode].MapData != null) { area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode); } else { area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode)); } } } }
public void ctor(Session session, Vector2?startPosition = default(Vector2?)) { if (LastLoadingThread != null && LastLoadingThread.TryGetTarget(out Thread lastThread) && (lastThread?.IsAlive ?? false)) { lastThread?.Abort(); } if (CoreModule.Settings.LazyLoading) { MainThreadHelper.Do(() => VirtualContentExt.UnloadOverworld()); } // Vanilla TileToIndex mappings. SurfaceIndex.TileToIndex = new Dictionary <char, int> { { '1', 3 }, { '3', 4 }, { '4', 7 }, { '5', 8 }, { '6', 8 }, { '7', 8 }, { '8', 8 }, { '9', 13 }, { 'a', 8 }, { 'b', 23 }, { 'c', 8 }, { 'd', 8 }, { 'e', 8 }, { 'f', 8 }, { 'g', 8 }, { 'G', 8 }, // Reflection alt - unassigned in vanilla. { 'h', 33 }, { 'i', 4 }, { 'j', 8 }, { 'k', 3 }, { 'l', 25 }, { 'm', 44 }, { 'n', 40 }, { 'o', 43 } }; AreaData area = AreaData.Get(session); MapMeta meta = area.GetMeta(); string path; path = meta?.BackgroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "BackgroundTiles.xml"); } GFX.BGAutotiler = new Autotiler(path); path = meta?.ForegroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "ForegroundTiles.xml"); } GFX.FGAutotiler = new Autotiler(path); path = meta?.AnimatedTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "AnimatedTiles.xml"); } GFX.AnimatedTilesBank = new AnimatedTilesBank(); XmlElement animatedData = Calc.LoadContentXML(path)["Data"]; foreach (XmlElement el in animatedData) { if (el != null) { GFX.AnimatedTilesBank.Add( el.Attr("name"), el.AttrFloat("delay", 0f), el.AttrVector2("posX", "posY", Vector2.Zero), el.AttrVector2("origX", "origY", Vector2.Zero), GFX.Game.GetAtlasSubtextures(el.Attr("path")) ); } } GFX.SpriteBank = new SpriteBank(GFX.Game, Path.Combine("Graphics", "Sprites.xml")); path = meta?.Sprites; if (!string.IsNullOrEmpty(path)) { SpriteBank bankOrig = GFX.SpriteBank; SpriteBank bankMod = new SpriteBank(GFX.Game, path); foreach (KeyValuePair <string, SpriteData> kvpBank in bankMod.SpriteData) { string key = kvpBank.Key; SpriteData valueMod = kvpBank.Value; if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig)) { IDictionary animsOrig = valueOrig.Sprite.GetAnimations(); IDictionary animsMod = valueMod.Sprite.GetAnimations(); foreach (DictionaryEntry kvpAnim in animsMod) { animsOrig[kvpAnim.Key] = kvpAnim.Value; } valueOrig.Sources.AddRange(valueMod.Sources); // replay the starting animation to be sure it is referring to the new sprite. valueOrig.Sprite.Stop(); if (valueMod.Sprite.CurrentAnimationID != "") { valueOrig.Sprite.Play(valueMod.Sprite.CurrentAnimationID); } } else { bankOrig.SpriteData[key] = valueMod; } } } // This is done exactly once in the vanilla GFX.LoadData method. PlayerSprite.ClearFramesMetadata(); PlayerSprite.CreateFramesMetadata("player"); PlayerSprite.CreateFramesMetadata("player_no_backpack"); PlayerSprite.CreateFramesMetadata("badeline"); PlayerSprite.CreateFramesMetadata("player_badeline"); PlayerSprite.CreateFramesMetadata("player_playback"); path = meta?.Portraits; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Portraits.xml"); } GFX.PortraitsSpriteBank = new SpriteBank(GFX.Portraits, path); orig_ctor(session, startPosition); LastLoadingThread = patch_RunThread.Current; }
private static void Aux_MakeRandomCorridor(ref char[][] map, MapMeta meta, Point coord, List <Corridors> corridors) { int headTo = RandomNumber.Between(0, 4); int direction = RandomNumber.Between(0, 3); // +/- , + , - int lengthPositive = 0, lengthNegative = 0; Stack <Point> path = new Stack <Point>(); Point baseCoord = new Point(coord.X, coord.Y); if (direction == 0) { lengthPositive = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); lengthNegative = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); } else if (direction == 1) { lengthPositive = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); } else { lengthNegative = RandomNumber.Between(meta.MinCorridorStraight, meta.MaxCorridorStraight); } for (int i = 0; i < lengthPositive; i++) { switch (headTo) { case N: case S: coord.Y++; break; case W: case E: coord.X++; break; default: break; } if (coord.X + 1 > meta.MapSizeX) { coord.X--; continue; } else if (coord.Y + 1 > meta.MapSizeY) { coord.Y--; continue; } if (map[(int)coord.X][(int)coord.Y] != (char)TileType.FLOOR_START) { map[(int)coord.X][(int)coord.Y] = (char)TileType.FLOOR; } path.Push(coord); } coord = baseCoord; for (int i = 0; i < lengthNegative; i++) { switch (headTo) { case N: case S: coord.Y--; break; case W: case E: coord.X--; break; default: break; } if (coord.X - 1 < 0) { coord.X++; continue; } else if (coord.Y - 1 < 0) { coord.Y++; continue; } if (map[(int)coord.X][(int)coord.Y] != (char)TileType.FLOOR_START) { map[(int)coord.X][(int)coord.Y] = (char)TileType.FLOOR; } path.Push(coord); } Corridors crd = new Corridors { CorridorData = path, IsBranch = true, CorridorStartAt = -1, CorridorEndAt = -1, }; corridors.Add(crd); }
public static new void Load() { orig_Load(); // assign SIDs and CheckpointData.Area for vanilla maps. foreach (AreaData area in Areas) { area.SetSID("Celeste/" + area.Mode[0].Path); for (int modeId = 0; modeId < area.Mode.Length; modeId++) { ModeProperties mode = area.Mode[modeId]; if (mode?.Checkpoints == null) { continue; } foreach (CheckpointData checkpoint in mode.Checkpoints) { checkpoint.SetArea(area.ToKey((AreaMode)modeId)); } } } // Separate array as we sort it afterwards. List <AreaData> modAreas = new List <AreaData>(); lock (Everest.Content.Map) { foreach (ModAsset asset in Everest.Content.Map.Values.Where(asset => asset.Type == typeof(AssetTypeMap))) { string path = asset.PathVirtual.Substring(5); AreaData area = new AreaData(); // Default values. area.SetSID(path); area.Name = path; area.Icon = "areas/" + path.ToLowerInvariant(); if (!GFX.Gui.Has(area.Icon)) { area.Icon = "areas/null"; } area.Interlude = false; area.CanFullClear = true; area.TitleBaseColor = Calc.HexToColor("6c7c81"); area.TitleAccentColor = Calc.HexToColor("2f344b"); area.TitleTextColor = Color.White; area.IntroType = Player.IntroTypes.WakeUp; area.Dreaming = false; area.ColorGrade = null; area.Mode = new ModeProperties[] { new ModeProperties { Inventory = PlayerInventory.Default, AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main) } }; area.Wipe = (Scene scene, bool wipeIn, Action onComplete) => new AngledWipe(scene, wipeIn, onComplete); area.DarknessAlpha = 0.05f; area.BloomBase = 0f; area.BloomStrength = 1f; area.Jumpthru = "wood"; area.CassseteNoteColor = Calc.HexToColor("33a9ee"); area.CassetteSong = SFX.cas_01_forsaken_city; // Custom values can be set via the MapMeta. MapMeta meta = new MapMeta(); meta.ApplyTo(area); MapMeta metaLoaded = asset.GetMeta <MapMeta>(); if (metaLoaded != null) { area.SetMeta(null); metaLoaded.ApplyTo(area); meta = metaLoaded; } if (string.IsNullOrEmpty(area.Mode[0].Path)) { area.Mode[0].Path = asset.PathVirtual.Substring(5); } // Some of the game's code checks for [1] / [2] explicitly. // Let's just provide null modes to fill any gaps. meta.Modes = meta.Modes ?? new MapMetaModeProperties[3]; if (meta.Modes.Length < 3) { MapMetaModeProperties[] larger = new MapMetaModeProperties[3]; for (int i = 0; i < meta.Modes.Length; i++) { larger[i] = meta.Modes[i]; } meta.Modes = larger; } if (area.Mode.Length < 3) { ModeProperties[] larger = new ModeProperties[3]; for (int i = 0; i < area.Mode.Length; i++) { larger[i] = area.Mode[i]; } area.Mode = larger; } // Celeste levelset always appears first. if (area.GetLevelSet() == "Celeste") { Areas.Add(area); } else { modAreas.Add(area); } // Some special handling. area.OnLevelBegin = (level) => { MapMeta levelMeta = AreaData.Get(level.Session).GetMeta(); MapMetaModeProperties levelMetaMode = level.Session.MapData.GetMeta(); if (levelMetaMode?.SeekerSlowdown ?? false) { level.Add(new SeekerEffectsController()); } }; } } // Merge modAreas into Areas. Areas.AddRange(modAreas); // Find duplicates and remove any earlier copies. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; int otherIndex = Areas.FindIndex(other => other.GetSID() == area.GetSID()); if (otherIndex < i) { Areas[otherIndex] = area; Areas.RemoveAt(i); i--; } } // Sort areas. Areas.Sort(AreaComparison); // Remove AreaDatas which are now a mode of another AreaData. // This can happen late as the map data (.bin) can contain additional metadata. for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; string path = area.Mode[0].Path; int otherIndex = Areas.FindIndex(other => other.Mode.Any(otherMode => otherMode?.Path == path)); if (otherIndex != -1 && otherIndex != i) { Areas.RemoveAt(i); i--; continue; } int? order; AreaMode side; string name; ParseName(path, out order, out side, out name); // Also check for .bins possibly belonging to A side .bins by their path and lack of existing modes. for (int ii = 0; ii < Areas.Count; ii++) { AreaData other = Areas[ii]; int? otherOrder; AreaMode otherSide; string otherName; ParseName(other.Mode[0].Path, out otherOrder, out otherSide, out otherName); if (area.GetLevelSet() == other.GetLevelSet() && order == otherOrder && name == otherName && side != otherSide && !other.HasMode(side)) { if (other.Mode[(int)side] == null) { other.Mode[(int)side] = new ModeProperties { Inventory = PlayerInventory.Default, AudioState = new AudioState(SFX.music_city, SFX.env_amb_00_main) } } ; other.Mode[(int)side].Path = path; Areas.RemoveAt(i); i--; break; } } } for (int i = 0; i < Areas.Count; i++) { AreaData area = Areas[i]; area.ID = i; // Clean up non-existing modes. int modei = 0; for (; modei < area.Mode.Length; modei++) { ModeProperties mode = area.Mode[modei]; if (mode == null || string.IsNullOrEmpty(mode.Path)) { break; } } Array.Resize(ref area.Mode, modei); Logger.Log("AreaData", $"{i}: {area.GetSID()} - {area.Mode.Length} sides"); // Update old MapData areas and load any new areas. // Add the A side MapData or update its area key. if (area.Mode[0].MapData != null) { area.Mode[0].MapData.Area = area.ToKey(); } else { area.Mode[0].MapData = new MapData(area.ToKey()); } if (area.IsInterludeUnsafe()) { continue; } // A and (some) B sides have PoemIDs. Can be overridden via empty PoemID. if (area.Mode[0].PoemID == null) { area.Mode[0].PoemID = area.GetSID().DialogKeyify() + "_A"; } if (area.Mode.Length > 1 && area.Mode[1] != null && area.Mode[1].PoemID == null) { area.Mode[1].PoemID = area.GetSID().DialogKeyify() + "_B"; } // Update all other existing mode's area keys. for (int mode = 1; mode < area.Mode.Length; mode++) { if (area.Mode[mode] == null) { continue; } if (area.Mode[mode].MapData != null) { area.Mode[mode].MapData.Area = area.ToKey((AreaMode)mode); } else { area.Mode[mode].MapData = new MapData(area.ToKey((AreaMode)mode)); } } } // Load custom mountains // This needs to be done after areas are loaded because it depends on the MapMeta MTNExt.LoadMod(); MTNExt.LoadModData(); }
public override Dictionary <string, Action <BinaryPacker.Element> > Init() => new Dictionary <string, Action <BinaryPacker.Element> >() { { "root", root => { foreach (BinaryPacker.Element el in root.Children) { Context.Run(el.Name, el); } } }, { "Style", style => { // Celeste 1.2.5.0 optimizes BinaryPacker, which causes some issues. // Let's "unoptimize" Style and its Backgrounds and Foregrounds. if (style.Children == null) { style.Children = new List <BinaryPacker.Element>(); } foreach (BinaryPacker.Element el in style.Children) { if ((el.Name == "Backgrounds" || el.Name == "Foregrounds") && el.Children == null) { el.Children = new List <BinaryPacker.Element>(); } } } }, { "levels", levels => { if (levels.Children != null) { foreach (BinaryPacker.Element level in levels.Children) { Context.Run("level", level); if (level.Children != null) { foreach (BinaryPacker.Element levelChild in level.Children) { Context.Run(levelChild.Name, levelChild); } } } } } }, { "level", level => { // lvl_ was optional before Celeste 1.2.5.0 made it mandatory. // Certain level "tags" are supported as very early mods used them. LevelTags = level.Attr("name").Split(':'); LevelName = LevelTags[0]; if (LevelName.StartsWith("lvl_")) { LevelName = LevelName.Substring(4); } level.SetAttr("name", "lvl_" + LevelName); BinaryPacker.Element entities = level.Children.FirstOrDefault(el => el.Name == "entities"); BinaryPacker.Element triggers = level.Children.FirstOrDefault(el => el.Name == "triggers"); // Celeste 1.2.5.0 optimizes BinaryPacker (null instead of empty lists), // which causes some issues where the game still expects an empty list. // Let's "unoptimize" entities and triggers. if (entities == null) { level.Children.Add(entities = new BinaryPacker.Element { Name = "entities" }); } if (entities.Children == null) { entities.Children = new List <BinaryPacker.Element>(); } if (triggers == null) { level.Children.Add(triggers = new BinaryPacker.Element { Name = "triggers" }); } if (triggers.Children == null) { triggers.Children = new List <BinaryPacker.Element>(); } if (LevelTags.Contains("checkpoint") || LevelTags.Contains("cp")) { entities.Children.Add(new BinaryPacker.Element { Name = "checkpoint", Attributes = new Dictionary <string, object>() { { "x", "0" }, { "y", "0" } } }); } if (level.AttrBool("space")) { if (level.AttrBool("spaceSkipWrap") || LevelTags.Contains("nospacewrap") || LevelTags.Contains("nsw")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceControllerBlocker" }); } if (level.AttrBool("spaceSkipGravity") || LevelTags.Contains("nospacegravity") || LevelTags.Contains("nsg")) { entities.Children.Add(new BinaryPacker.Element { Name = "everest/spaceController" }); level.SetAttr("space", false); } if (!LevelTags.Contains("nospacefix") && !LevelTags.Contains("nsf") && !triggers.Children.Any(el => el.Name == "cameraTargetTrigger") && !entities.Children.Any(el => el.Name == "everest/spaceControllerBlocker")) { // Camera centers tile-perfectly on uneven heights. int heightForCenter = level.AttrInt("height"); heightForCenter /= 8; if (heightForCenter % 2 == 0) { heightForCenter--; } heightForCenter *= 8; triggers.Children.Add(new BinaryPacker.Element { Name = "cameraTargetTrigger", Attributes = new Dictionary <string, object>() { { "x", 0f }, { "y", 0f }, { "width", level.AttrInt("width") }, { "height", level.AttrInt("height") }, { "yOnly", true }, { "lerpStrength", 1f } }, Children = new List <BinaryPacker.Element>() { new BinaryPacker.Element { Attributes = new Dictionary <string, object>() { { "x", 160f }, { "y", heightForCenter / 2f } } } } }); } } } }, { "entities", levelChild => { // check if the room has a checkpoint first. foreach (BinaryPacker.Element entity in levelChild.Children) { if (entity.Name == "checkpoint") { if (CheckpointsAuto != null) { MapMeta modeMeta = AreaData.GetModeMeta(AreaKey.Mode); CheckpointData c = new CheckpointData( LevelName, (AreaData.GetSID() + "_" + LevelName).DialogKeyify(), MapMeta.GetInventory(entity.Attr("inventory")), entity.Attr("dreaming") == "" ? modeMeta.Dreaming ?? AreaData.Dreaming : entity.AttrBool("dreaming"), null ); if (entity.Attr("coreMode") == "") { c.CoreMode = modeMeta.CoreMode ?? AreaData.CoreMode; } else { entity.AttrIf("coreMode", v => c.CoreMode = (Session.CoreModes)Enum.Parse(typeof(Session.CoreModes), v, true)); } int id = entity.AttrInt("checkpointID", -1); if (id == -1) { CheckpointsAuto.Add(c); } else { while (CheckpointsAuto.Count <= id) { CheckpointsAuto.Add(null); } CheckpointsAuto[id] = c; } } Checkpoint++; StrawberryInCheckpoint = 0; } } // then, auto-assign strawberries and cassettes to checkpoints. foreach (BinaryPacker.Element entity in levelChild.Children) { Context.Run("entity:" + entity.Name, entity); } } }, { "entity:cassette", entity => { if (AreaData.CassetteCheckpointIndex < 0) { AreaData.CassetteCheckpointIndex = Checkpoint; } } }, { "entity:strawberry", entity => { if (!entity.AttrBool("moon", false)) { if (entity.AttrInt("checkpointID", -1) == -1) { entity.SetAttr("checkpointID", Checkpoint); } if (entity.AttrInt("order", -1) == -1) { entity.SetAttr("order", StrawberryInCheckpoint); } Strawberry++; StrawberryInCheckpoint++; } } } };
/// <summary> /// Set the custom metadata. /// </summary> public static AreaData SetMeta(this AreaData self, MapMeta value) { ((patch_AreaData)self).Meta = value; return(self); }
public void Draw(Vector pos) { for (var y = 0; y <= WindowRows; y++) { for (var x = 0; x <= WindowColumns; x++) { // don't draw extra tile if on the boundary if (pos.Y >= Height - Screen.HalfHeight && y == WindowRows) { break; } if (pos.X >= Width - Screen.HalfWidth && x == WindowColumns) { continue; } int playerTileX, playerTileY; int offsetX = 0, offsetY = 0; // handle player X coordinate if (pos.X < Screen.HalfWidth) { // offset player when near the left boundary of the map playerTileX = 0; } else if (pos.X > Width - Screen.HalfWidth) { // offset player when near the right boundary of the map playerTileX = (Width - Screen.Width) / TileWidth; } else { // position the player in the middle of the screen playerTileX = (pos.X - Screen.HalfWidth) / TileWidth; offsetX = pos.X % TileWidth; } // handle player Y coordinate if (pos.Y < Screen.HalfHeight) { // offset player when near the top boundary of the map playerTileY = 0; } else if (pos.Y > Height - Screen.HalfHeight) { // offset player when near the bottom boundary of the map playerTileY = (Height - Screen.Height) / TileHeight; } else { // position the player in the middle of the screen playerTileY = (pos.Y - Screen.HalfHeight) / TileHeight; offsetY = (pos.Y + TileHeight / 2) % TileHeight; } // draw all tile layers for (var layer = 0; layer < Layers.Length; layer++) { var tile = Layers[layer].Tiles[x + playerTileX, y + playerTileY]; if (tile?.Tileset != null) { var drawRect = new Rect(x * TileWidth - offsetX, y * TileHeight - offsetY, TileWidth, TileHeight); _tilesetManager.Draw(tile.Tileset, drawRect, tile.SpriteRect); if (layer == 0) { DrawEventCollection(MapMeta.GetEventId(new Vector(x + playerTileX, y + playerTileY)), new Vector(x * TileWidth - offsetX, y * TileHeight - offsetY)); } } } } } }
static void Main(string[] args) { Console.WriteLine("FLT-DungeonMapGenerator"); MapMeta meta = new MapMeta { MapSizeX = 96, MapSizeY = 64, StartRoomSize = 6, MaxRoomSize = 8, MinRoomSize = 4, MaxRoomNumber = 10, MaxRoomConnection = 2, MinRoomDistance = 200, MaxCorridorBranchCount = 100, MinCorridorStraight = 3, MaxCorridorStraight = 12, RouteTwistFactor = 3, RouteDirectFactor = 6, RouteBranchFactor = 12, RouteAdditionalBranchFactor = 12, }; char[][] map; List <RoomMeta> rooms = new List <RoomMeta>(); List <Corridors> corridors = new List <Corridors>(); List <Prefabs> prefabs = new List <Prefabs> { new Prefabs { Name = "Test", Tile = (int)TileType.TEST, SpawnChance = 20, } // 2% }; List <string> componentsPath = new List <string> { filePath }; Console.WriteLine("Initializing..."); map = Stage1_MapInit(meta); // Maybe can merge Stage2 and Stage3 Console.WriteLine("Making starting area..."); Stage2_MakeStartRoom(ref map, meta, rooms); Console.WriteLine("Making rooms..."); Stage3_MakeRooms(ref map, meta, rooms); Console.WriteLine("Making corridors..."); Stage4_ConnectRooms(ref map, meta, rooms, corridors, 0, 0); Console.WriteLine("Current corridors=" + corridors.Count); Console.WriteLine("Making branches..."); Stage5_MakeCorridorBranch(ref map, meta, rooms, corridors); Console.WriteLine("Current corridors=" + corridors.Count); Console.WriteLine("Validating connections..."); Stage6_ConnectionValidate(ref map, meta, rooms, corridors); Console.WriteLine("Current corridors=" + corridors.Count); Console.WriteLine("Making entities..."); Stage7_MakePrefab(ref map, meta, corridors, prefabs); Console.WriteLine("Current corridors=" + corridors.Count); Stage8_InsertComponent(ref map, meta, rooms, componentsPath); // Draw on Console for (int j = 0; j < meta.MapSizeY; j++) { for (int i = 0; i < meta.MapSizeX; i++) { if (map[i][j] == (char)TileType.WALL) { Console.Write('X'); } else if (map[i][j] == (char)TileType.FLOOR) { Console.Write(' '); } else if (map[i][j] == (char)TileType.FLOOR_START) { Console.Write('@'); } else if (map[i][j] == (char)TileType.TEST) { Console.Write('T'); } else { Console.Write('?'); } } Console.Write(Environment.NewLine); } Console.ReadKey(); }
public void ctor(Session session, Vector2?startPosition = default(Vector2?)) { if (CoreModule.Settings.LazyLoading) { MainThreadHelper.Do(() => VirtualContentExt.UnloadOverworld()); } // Vanilla TileToIndex mappings. SurfaceIndex.TileToIndex = new Dictionary <char, int> { { '1', 3 }, { '3', 4 }, { '4', 7 }, { '5', 8 }, { '6', 8 }, { '7', 8 }, { '8', 8 }, { '9', 13 }, { 'a', 8 }, { 'b', 23 }, { 'c', 8 }, { 'd', 8 }, { 'e', 8 }, { 'f', 8 }, { 'g', 8 }, { 'h', 33 }, { 'i', 4 }, { 'j', 8 }, { 'k', 3 }, { 'l', 25 }, { 'm', 44 }, { 'n', 40 }, { 'o', 43 } }; AreaData area = AreaData.Get(session); MapMeta meta = area.GetMeta(); string path; path = meta?.BackgroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "BackgroundTiles.xml"); } GFX.BGAutotiler = new Autotiler(path); path = meta?.ForegroundTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "ForegroundTiles.xml"); } GFX.FGAutotiler = new Autotiler(path); path = meta?.AnimatedTiles; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "AnimatedTiles.xml"); } GFX.AnimatedTilesBank = new AnimatedTilesBank(); XmlElement animatedData = Calc.LoadContentXML(path)["Data"]; foreach (XmlElement el in animatedData) { if (el != null) { GFX.AnimatedTilesBank.Add( el.Attr("name"), el.AttrFloat("delay", 0f), el.AttrVector2("posX", "posY", Vector2.Zero), el.AttrVector2("origX", "origY", Vector2.Zero), GFX.Game.GetAtlasSubtextures(el.Attr("path")) ); } } GFX.SpriteBank = new SpriteBank(GFX.Game, Path.Combine("Graphics", "Sprites.xml")); path = meta?.Sprites; if (!string.IsNullOrEmpty(path)) { SpriteBank bankOrig = GFX.SpriteBank; SpriteBank bankMod = new SpriteBank(GFX.Game, path); foreach (KeyValuePair <string, SpriteData> kvpBank in bankMod.SpriteData) { string key = kvpBank.Key; SpriteData valueMod = kvpBank.Value; if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig)) { IDictionary animsOrig = valueOrig.Sprite.GetAnimations(); IDictionary animsMod = valueMod.Sprite.GetAnimations(); foreach (DictionaryEntry kvpAnim in animsMod) { animsOrig[kvpAnim.Key] = kvpAnim.Value; } } else { bankOrig.SpriteData[key] = valueMod; } } } path = meta?.Portraits; if (string.IsNullOrEmpty(path)) { path = Path.Combine("Graphics", "Portraits.xml"); } GFX.PortraitsSpriteBank = new SpriteBank(GFX.Portraits, path); orig_ctor(session, startPosition); }