void ExpandWidth(int newWidth) { ChunkSet[] newMap = new ChunkSet[currentHeight * newWidth]; if (map != null) { int upperBound = currentWidth; if (currentWidth > newWidth) { upperBound = newWidth; } for (int x = 0; x < upperBound; x++) { for (int y = 0; y < currentHeight; y++) { int index = y * currentWidth + x; ChunkSet n = map[index]; index = y * newWidth + x; newMap[index] = n; } } } map = newMap; currentWidth = newWidth; }
void ExpandHeight(int newHeight) { ChunkSet[] newMap = new ChunkSet[newHeight * currentWidth]; if (map != null) { int upperBound = currentHeight; if (currentHeight > newHeight) { upperBound = newHeight; } //When expanding height, we don't need to do any tricky mathematics to cater for one-dimensional array storage for (int x = 0; x < currentWidth; x++) { for (int y = 0; y < upperBound; y++) { int index = y * currentWidth + x; ChunkSet n = map[index]; newMap[index] = n; } } } map = newMap; currentHeight = newHeight; }
void SetNodeAt(ChunkSet node, int x, int y) { int index = y * currentWidth + x; map[index] = node; node.SetCoordinates(x, y); }
public void Build_ThrowsException_IfNotEnoughTilesets() { Assert.Throws <ChunkSet.ChunkSetBuilder.InvalidTilesetCountException>( () => ChunkSet.NewBuilder() .Add(new Chunk(new Tile[1][][])) .Add(new Chunk(new Tile[1][][])) .Build(), "found 2"); }
public LevelTemplate(ChunkSet chunks, int width, int height, bool sky, string background, LevelTemplate destination, string theme) { Chunks = chunks; Width = width; Height = height; Sky = sky; Background = background; Destination = destination; Theme = theme; }
protected override void LoadContent() { TowerTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/tower"), 1), 1, 1, false, "bg_entrance", null, null); VillageTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/village"), 15), 12, 3, false, "bg_entrance", TowerTemplate, null); FountainTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/fountain"), 2), 2, 1, false, "bg_fountain", VillageTemplate, "Sounds/fountain"); JungleTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/jungle2"), 15), 8, 3, false, "bg_jungle", FountainTemplate, "Sounds/jungle"); TunnelTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/tunnel"), 2), 2, 1, false, "bg_tunnel", JungleTemplate, null); EntranceTemplate = new LevelTemplate(ChunkSet.FromTexture(Content.Load <Texture2D>("Levels/entrance"), 2), 2, 1, true, "bg_entrance", TunnelTemplate, null); _screens.Push(new StartScreen(this)); }
public void Build_ThrowsException_IfNullTileset() { int numTilesets = 9; ChunkSet.ChunkSetBuilder builder = ChunkSet.NewBuilder(); for (int i = 0; i < numTilesets; i++) { builder.Add(null); } Assert.Throws <NullReferenceException>( () => builder.Build(), "null"); }
public void Build_ThrowsException_IfTooManyTilesets() { int numTilesets = 15; ChunkSet.ChunkSetBuilder builder = ChunkSet.NewBuilder(); for (int i = 0; i < numTilesets; i++) { builder.Add(new Chunk(new Tile[1][][])); } Assert.Throws <ChunkSet.ChunkSetBuilder.InvalidTilesetCountException>( () => builder.Build(), "found 15."); }
public void Reload(ChunkSet chunk) { for (int i = 0; i < mapRows; i++) { for (int j = 0; j < mapColumns; j++) { int idx = i * mapRows + j; m_maps[idx].MakeMap(chunk.m_chunks[idx].m_tiles); m_maps[idx].transform.position = new Vector3( i * m_maps[idx].controller.WorldWidth, 0, j * m_maps[idx].controller.WorldHeight); } } }
/// <summary> ///Adds a chunk at the given coordinates and expands the map /// </summary> /// <param name="node"> ///The chunk you wish to add to the map /// </param> /// <param name="x"> ///The x target coordinate /// </param> /// <param name="y"> ///The y target coordinate /// </param> public void AddNodeAt(ChunkSet node, int x, int y) { node.SetCoordinates(x, y); if (x < 0) { //Debug.Log("ABS X: " + x); //expand and shuffle by Mathf.Abs(x) ExpandWidth(currentWidth + Mathf.Abs(x)); ShuffleMap(Mathf.Abs(x), 0); node.SetCoordinates(0, node.GetY()); } else if (x >= currentWidth) { //we just need to expand out by x - (currentWidth - 1), no need to shuffle if (currentWidth == 0) { ExpandWidth(x + 1); } else { ExpandWidth(x + 1); } } if (y < 0) { //Debug.Log("ABS Y: " + x); //expand and shuffle by Mathf.Abs(y) ExpandHeight(currentHeight + Mathf.Abs(y)); ShuffleMap(0, Mathf.Abs(y)); node.SetCoordinates(node.GetX(), 0); } else if (y >= currentHeight) { //we just need to expand up by y - (currentHeight - 1), no need to shuffle ExpandHeight(y + 1); } SetNodeAt(node, node.GetX(), node.GetY()); }
// Use this for initialization void Start() { Tile[][][] map = new Tile[32][][]; for (int i = 0; i < 32; i++) { map[i] = new Tile[16][]; for (int j = 0; j < 16; j++) { map[i][j] = new Tile[16]; for (int k = 0; k < 16; k++) { map[i][j][k] = new Tile(); if ((j + k) % 2 == 0) { map[i][j][k].ID = 1; } else { map[i][j][k].ID = 2; } } } } Chunk chunk = new Chunk(map); ChunkSet c = ChunkSet.NewBuilder() .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Add(chunk) .Build(); MapManager.instance.Reload(c); }
public static void Parse <T>(T node, GameBoxReader r, IProgress <GameBoxReadProgress> progress = null) where T : CMwNod { var stopwatch = Stopwatch.StartNew(); node.GBX = r.GBX; var type = node.GetType(); var chunks = new ChunkSet { Node = node }; node.Chunks = chunks; uint?previousChunk = null; while (!r.BaseStream.CanSeek || r.BaseStream.Position < r.BaseStream.Length) { if (r.BaseStream.CanSeek && r.BaseStream.Position + 4 > r.BaseStream.Length) { Debug.WriteLine($"Unexpected end of the stream: {r.BaseStream.Position}/{r.BaseStream.Length}"); var bytes = r.ReadBytes((int)(r.BaseStream.Length - r.BaseStream.Position)); break; } var chunkID = r.ReadUInt32(); if (chunkID == 0xFACADE01) // no more chunks { break; } else { var logChunk = $"[{node.ClassName}] 0x{chunkID:X8}"; if (r.BaseStream.CanSeek) { logChunk += $" ({(float)r.BaseStream.Position / r.BaseStream.Length:0.00%})"; } if (node.GBX?.ID.HasValue == true && Remap(node.GBX.ID.Value) == node.ID) { Log.Write(logChunk); } else { Log.Write($"~ {logChunk}"); } } Chunk chunk; var chunkRemapped = Chunk.Remap(chunkID); Type chunkClass = null; var reflected = ((chunkRemapped & 0xFFFFF000) == node.ID || NodeCacheManager.AvailableInheritanceClasses[type].Contains(chunkRemapped & 0xFFFFF000)) && (NodeCacheManager.AvailableChunkClasses[type].TryGetValue(chunkRemapped, out chunkClass) || NodeCacheManager.AvailableChunkClasses[type].TryGetValue(chunkID & 0xFFF, out chunkClass)); var skippable = reflected && chunkClass.BaseType.GetGenericTypeDefinition() == typeof(SkippableChunk <>); // Unknown or skippable chunk if (!reflected || skippable) { var skip = r.ReadUInt32(); if (skip != 0x534B4950) { if (chunkID != 0 && !reflected) { var logChunkError = $"[{node.ClassName}] 0x{chunkID:X8} ERROR (wrong chunk format or unknown unskippable chunk)"; if (node.GBX?.ID.HasValue == true && Remap(node.GBX.ID.Value) == node.ID) { Log.Write(logChunkError, ConsoleColor.Red); } else { Log.Write($"~ {logChunkError}", ConsoleColor.Red); } throw new Exception($"Wrong chunk format or unskippable chunk: 0x{chunkID:X8} (" + $"{NodeCacheManager.Names.Where(x => x.Key == Chunk.Remap(chunkID & 0xFFFFF000)).Select(x => x.Value).FirstOrDefault() ?? "unknown class"})" + $"\nPrevious chunk: 0x{previousChunk ?? 0:X8} (" + $"{(previousChunk.HasValue ? (NodeCacheManager.Names.Where(x => x.Key == Chunk.Remap(previousChunk.Value & 0xFFFFF000)).Select(x => x.Value).FirstOrDefault() ?? "unknown class") : "not a class")})"); /* Usually breaks in the current state and causes confusion * * var buffer = BitConverter.GetBytes(chunkID); * using (var restMs = new MemoryStream(ushort.MaxValue)) * { * restMs.Write(buffer, 0, buffer.Length); * * while (r.PeekUInt32() != 0xFACADE01) * restMs.WriteByte(r.ReadByte()); * * node.Rest = restMs.ToArray(); * } * Debug.WriteLine("FACADE found.");*/ } break; } var chunkDataSize = r.ReadInt32(); var chunkData = new byte[chunkDataSize]; if (chunkDataSize > 0) { r.Read(chunkData, 0, chunkDataSize); } if (reflected) { var ignoreChunkAttribute = chunkClass.GetCustomAttribute <IgnoreChunkAttribute>(); var constructor = Array.Find(chunkClass.GetConstructors(), x => x.GetParameters().Length == 0); if (constructor == null) { throw new ArgumentException($"{type.FullName} doesn't have a parameterless constructor."); } var c = (Chunk)constructor.Invoke(new object[0]); c.Node = node; c.GBX = node.GBX; ((ISkippableChunk)c).Data = chunkData; if (chunkData == null || chunkData.Length == 0) { ((ISkippableChunk)c).Discovered = true; } chunks.Add(c); if (ignoreChunkAttribute == null) { c.OnLoad(); if (chunkClass.GetCustomAttribute <ChunkAttribute>().ProcessSync) { ((ISkippableChunk)c).Discover(); } } chunk = c; } else { Debug.WriteLine("Unknown skippable chunk: " + chunkID.ToString("X")); chunk = (Chunk)Activator.CreateInstance(typeof(SkippableChunk <>).MakeGenericType(type), node, chunkRemapped, chunkData); chunk.GBX = node.GBX; chunks.Add(chunk); } } else // Known or unskippable chunk { var constructor = Array.Find(chunkClass.GetConstructors(), x => x.GetParameters().Length == 0); if (constructor == null) { throw new ArgumentException($"{type.FullName} doesn't have a parameterless constructor."); } var c = (Chunk)constructor.Invoke(new object[0]); c.Node = node; c.OnLoad(); chunks.Add(c); //r.Chunk = (Chunk)c; // Set chunk temporarily for reading var posBefore = r.BaseStream.Position; GameBoxReaderWriter gbxrw = new GameBoxReaderWriter(r); var attributes = chunkClass.GetCustomAttributes(); var ignoreChunkAttribute = default(IgnoreChunkAttribute); var autoReadWriteChunkAttribute = default(AutoReadWriteChunkAttribute); foreach (var att in attributes) { if (att is IgnoreChunkAttribute ignoreChunkAtt) { ignoreChunkAttribute = ignoreChunkAtt; } if (att is AutoReadWriteChunkAttribute autoReadWriteChunkAtt) { autoReadWriteChunkAttribute = autoReadWriteChunkAtt; } } try { if (ignoreChunkAttribute == null) { if (autoReadWriteChunkAttribute == null) { c.ReadWrite(node, gbxrw); } else { var unknown = new GameBoxWriter(c.Unknown, r.Lookbackable); var unknownData = r.ReadUntilFacade(); unknown.Write(unknownData, 0, unknownData.Length); } } else { throw new Exception($"Chunk 0x{chunkID & 0xFFF:x3} from class {node.ClassName} is known but its content is unknown to read."); } } catch (EndOfStreamException) { Debug.WriteLine($"Unexpected end of the stream while reading the chunk."); } c.Progress = (int)(r.BaseStream.Position - posBefore); chunk = c; } progress?.Report(new GameBoxReadProgress(GameBoxReadProgressStage.Body, (float)r.BaseStream.Position / r.BaseStream.Length, node.GBX, chunk)); previousChunk = chunkID; } stopwatch.Stop(); var logNodeCompletion = $"[{node.ClassName}] DONE! ({stopwatch.Elapsed.TotalMilliseconds}ms)"; if (node.GBX.ID.HasValue == true && Remap(node.GBX.ID.Value) == node.ID) { Log.Write(logNodeCompletion, ConsoleColor.Green); } else { Log.Write($"~ {logNodeCompletion}", ConsoleColor.Green); } }