2 using System.Collections.Generic;
6 using Terraria.GameContent.Tile_Entities;
13 internal static class TileIO
16 internal struct TileTables
18 internal IDictionary<ushort, ushort> tiles;
19 internal IDictionary<ushort, bool> frameImportant;
20 internal IDictionary<ushort, ushort> walls;
21 internal IDictionary<ushort, string> tileModNames;
22 internal IDictionary<ushort, string> tileNames;
24 internal static TileTables Create() {
25 TileTables tables =
new TileTables {
26 tiles =
new Dictionary<ushort, ushort>(),
27 frameImportant =
new Dictionary<ushort, bool>(),
28 walls =
new Dictionary<ushort, ushort>(),
29 tileModNames =
new Dictionary<ushort, string>(),
30 tileNames =
new Dictionary<ushort, string>()
36 internal static TagCompound SaveTiles() {
37 var hasTile =
new bool[TileLoader.TileCount];
38 var hasWall =
new bool[WallLoader.WallCount];
39 using (var ms =
new MemoryStream())
41 WriteTileData(writer, hasTile, hasWall);
43 var tileList =
new List<TagCompound>();
44 for (
int type = TileID.Count; type < hasTile.Length; type++) {
48 var modTile = TileLoader.GetTile(type);
49 tileList.Add(
new TagCompound {
50 [
"value"] = (short)type,
51 [
"mod"] = modTile.mod.Name,
52 [
"name"] = modTile.Name,
53 [
"framed"] = Main.tileFrameImportant[type],
56 var wallList =
new List<TagCompound>();
57 for (
int wall = WallID.Count; wall < hasWall.Length; wall++) {
61 var modWall = WallLoader.GetWall(wall);
62 wallList.Add(
new TagCompound {
63 [
"value"] = (short)wall,
64 [
"mod"] = modWall.mod.Name,
65 [
"name"] = modWall.Name,
68 if (tileList.Count == 0 && wallList.Count == 0)
71 return new TagCompound {
72 [
"tileMap"] = tileList,
73 [
"wallMap"] = wallList,
74 [
"data"] = ms.ToArray()
79 internal static void LoadTiles(TagCompound tag) {
80 if (!tag.ContainsKey(
"data"))
83 var tables = TileTables.Create();
84 foreach (var tileTag
in tag.GetList<TagCompound>(
"tileMap")) {
85 ushort type = (ushort)tileTag.GetShort(
"value");
86 string modName = tileTag.GetString(
"mod");
87 string name = tileTag.GetString(
"name");
88 Mod mod = ModLoader.GetMod(modName);
89 tables.tiles[type] = mod == null ? (ushort)0 : (ushort)mod.TileType(name);
90 if (tables.tiles[type] == 0) {
91 tables.tiles[type] = (ushort)ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile");
92 tables.tileModNames[type] = modName;
93 tables.tileNames[type] = name;
95 tables.frameImportant[type] = tileTag.GetBool(
"framed");
97 foreach (var wallTag
in tag.GetList<TagCompound>(
"wallMap")) {
98 ushort wall = (ushort)wallTag.GetShort(
"value");
99 string modName = wallTag.GetString(
"mod");
100 string name = wallTag.GetString(
"name");
101 Mod mod = ModLoader.GetMod(modName);
102 tables.walls[wall] = mod == null ? (ushort)0 : (ushort)mod.WallType(name);
104 using (var memoryStream = new MemoryStream(tag.GetByteArray("data")))
106 ReadTileData(reader, tables);
107 WorldIO.ValidateSigns();
110 internal static
void LoadLegacyTiles(
BinaryReader reader) {
111 TileTables tables = TileTables.Create();
112 ushort count = reader.ReadUInt16();
113 for (
int k = 0; k < count; k++) {
114 ushort type = reader.ReadUInt16();
115 string modName = reader.ReadString();
116 string name = reader.ReadString();
117 Mod mod = ModLoader.GetMod(modName);
118 tables.tiles[type] = mod == null ? (ushort)0 : (ushort)mod.TileType(name);
119 if (tables.tiles[type] == 0) {
120 tables.tiles[type] = (ushort)ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile");
121 tables.tileModNames[type] = modName;
122 tables.tileNames[type] = name;
124 tables.frameImportant[type] = reader.ReadBoolean();
126 count = reader.ReadUInt16();
127 for (
int k = 0; k < count; k++) {
128 ushort wall = reader.ReadUInt16();
129 string modName = reader.ReadString();
130 string name = reader.ReadString();
131 Mod mod = ModLoader.GetMod(modName);
132 tables.walls[wall] = mod == null ? (ushort)0 : (ushort)mod.WallType(name);
134 ReadTileData(reader, tables);
137 internal static
void WriteTileData(
BinaryWriter writer,
bool[] hasTile,
bool[] hasWall) {
139 bool nextModTile =
false;
143 Tile tile = Main.tile[i, j];
144 if (HasModData(tile)) {
152 WriteModTile(ref i, ref j, writer, ref nextModTile, hasTile, hasWall);
162 while (NextTile(ref i, ref j));
168 internal static void ReadTileData(
BinaryReader reader, TileTables tables) {
171 bool nextModTile =
false;
174 byte skip = reader.ReadByte();
175 while (skip == 255) {
176 for (byte k = 0; k < 255; k++) {
177 if (!NextTile(ref i, ref j)) {
181 skip = reader.ReadByte();
183 for (byte k = 0; k < skip; k++) {
184 if (!NextTile(ref i, ref j)) {
192 ReadModTile(ref i, ref j, tables, reader, ref nextModTile);
194 while (NextTile(ref i, ref j));
197 internal static void WriteModTile(ref
int i, ref
int j,
BinaryWriter writer, ref
bool nextModTile,
bool[] hasTile,
bool[] hasWall) {
198 Tile tile = Main.tile[i, j];
200 byte[] data =
new byte[11];
202 if (tile.active() && tile.type >= TileID.Count) {
203 hasTile[tile.type] =
true;
205 data[index] = (byte)tile.type;
207 data[index] = (byte)(tile.type >> 8);
209 if (Main.tileFrameImportant[tile.type]) {
210 data[index] = (byte)tile.frameX;
212 if (tile.frameX >= 256) {
214 data[index] = (byte)(tile.frameX >> 8);
217 data[index] = (byte)tile.frameY;
219 if (tile.frameY >= 256) {
221 data[index] = (byte)(tile.frameY >> 8);
225 if (tile.color() != 0) {
227 data[index] = tile.color();
231 if (tile.wall >= WallID.Count) {
232 hasWall[tile.wall] =
true;
234 data[index] = (byte)tile.wall;
236 data[index] = (byte)(tile.wall >> 8);
238 if (tile.wallColor() != 0) {
240 data[index] = tile.wallColor();
247 while (NextTile(ref nextI, ref nextJ)) {
248 if (tile.isTheSameAs(Main.tile[nextI, nextJ]) && sameCount < 255) {
253 else if (HasModData(Main.tile[nextI, nextJ])) {
264 data[index] = sameCount;
268 writer.Write(data, 0, index);
271 internal static void ReadModTile(ref
int i, ref
int j, TileTables tables,
BinaryReader reader, ref
bool nextModTile) {
273 flags = reader.ReadByte();
274 Tile tile = Main.tile[i, j];
275 if ((flags & 1) == 1) {
277 ushort saveType = reader.ReadUInt16();
278 tile.type = tables.tiles[saveType];
279 if (tables.frameImportant[saveType]) {
280 if ((flags & 2) == 2) {
281 tile.frameX = reader.ReadInt16();
284 tile.frameX = reader.ReadByte();
286 if ((flags & 4) == 4) {
287 tile.frameY = reader.ReadInt16();
290 tile.frameY = reader.ReadByte();
297 if (tile.type == ModContent.GetInstance<ModLoaderMod>().TileType(
"PendingMysteryTile")
298 && tables.tileNames.ContainsKey(saveType)) {
299 MysteryTileInfo info;
300 if (tables.frameImportant[saveType]) {
301 info =
new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType],
302 tile.frameX, tile.frameY);
305 info =
new MysteryTileInfo(tables.tileModNames[saveType], tables.tileNames[saveType]);
307 MysteryTilesWorld modWorld = ModContent.GetInstance<MysteryTilesWorld>();
308 int pendingFrameID = modWorld.pendingInfos.IndexOf(info);
309 if (pendingFrameID < 0) {
310 pendingFrameID = modWorld.pendingInfos.Count;
311 modWorld.pendingInfos.Add(info);
313 MysteryTileFrame pendingFrame =
new MysteryTileFrame(pendingFrameID);
314 tile.frameX = pendingFrame.FrameX;
315 tile.frameY = pendingFrame.FrameY;
317 if ((flags & 8) == 8) {
318 tile.color(reader.ReadByte());
320 WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
322 if ((flags & 16) == 16) {
323 tile.wall = tables.walls[reader.ReadUInt16()];
324 if ((flags & 32) == 32) {
325 tile.wallColor(reader.ReadByte());
328 if ((flags & 64) == 64) {
329 byte sameCount = reader.ReadByte();
330 for (byte k = 0; k < sameCount; k++) {
331 NextTile(ref i, ref j);
332 Main.tile[i, j].CopyFrom(tile);
333 WorldGen.tileCounts[tile.type] += j <= Main.worldSurface ? 5 : 1;
336 if ((flags & 128) == 128) {
341 private static bool HasModData(Tile tile) {
342 return (tile.active() && tile.type >= TileID.Count) || tile.wall >= WallID.Count;
345 private static bool NextTile(ref
int i, ref
int j) {
347 if (j >= Main.maxTilesY) {
350 if (i >= Main.maxTilesX) {
359 internal static void VanillaSaveFrames(Tile tile, ref
short frameX) {
360 if (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin) {
361 int slot = tile.frameX / 100;
362 int position = tile.frameY / 18;
363 if (HasModArmor(slot, position)) {
369 internal struct ContainerTables
371 internal IDictionary<int, int> headSlots;
372 internal IDictionary<int, int> bodySlots;
373 internal IDictionary<int, int> legSlots;
375 internal static ContainerTables Create() {
376 ContainerTables tables =
new ContainerTables {
377 headSlots =
new Dictionary<int, int>(),
378 bodySlots =
new Dictionary<int, int>(),
379 legSlots =
new Dictionary<int, int>()
386 internal static TagCompound SaveContainers() {
387 var ms =
new MemoryStream();
389 byte[] flags =
new byte[1];
391 ISet<int> headSlots =
new HashSet<int>();
392 ISet<int> bodySlots =
new HashSet<int>();
393 ISet<int> legSlots =
new HashSet<int>();
394 IDictionary<int, int> itemFrames =
new Dictionary<int, int>();
395 for (
int i = 0; i < Main.maxTilesX; i++) {
396 for (
int j = 0; j < Main.maxTilesY; j++) {
397 Tile tile = Main.tile[i, j];
398 if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
399 int slot = tile.frameX / 100;
400 int position = tile.frameY / 18;
401 if (HasModArmor(slot, position)) {
405 else if (position == 1) {
408 else if (position == 2) {
418 foreach (KeyValuePair<int, TileEntity> entity
in TileEntity.ByID) {
419 TEItemFrame itemFrame = entity.Value as TEItemFrame;
420 if (itemFrame != null && ItemLoader.NeedsModSaving(itemFrame.item)) {
421 itemFrames.Add(itemFrame.ID, tileEntity);
425 if(!(entity.Value is ModTileEntity))
431 writer.Write(numFlags);
432 writer.Write(flags, 0, numFlags);
433 if ((flags[0] & 1) == 1) {
434 writer.Write((ushort)headSlots.Count);
435 foreach (
int slot
in headSlots) {
436 writer.Write((ushort)slot);
437 ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[
EquipType.Head][slot]);
438 writer.Write(item.mod.Name);
439 writer.Write(item.Name);
441 writer.Write((ushort)bodySlots.Count);
442 foreach (
int slot
in bodySlots) {
443 writer.Write((ushort)slot);
444 ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[
EquipType.Body][slot]);
445 writer.Write(item.mod.Name);
446 writer.Write(item.Name);
448 writer.Write((ushort)legSlots.Count);
449 foreach (
int slot
in legSlots) {
450 writer.Write((ushort)slot);
451 ModItem item = ItemLoader.GetItem(EquipLoader.slotToId[
EquipType.Legs][slot]);
452 writer.Write(item.mod.Name);
453 writer.Write(item.Name);
455 WriteContainerData(writer);
457 var tag =
new TagCompound();
458 tag.Set(
"data", ms.ToArray());
460 if (itemFrames.Count > 0) {
461 tag.Set(
"itemFrames", itemFrames.Select(entry =>
463 [
"id"] = entry.Value,
464 [
"item"] = ItemIO.Save(((TEItemFrame)TileEntity.ByID[entry.Key]).item)
471 internal static void LoadContainers(TagCompound tag) {
472 if (tag.ContainsKey(
"data"))
473 ReadContainers(
new BinaryReader(
new MemoryStream(tag.GetByteArray(
"data"))));
475 foreach (var frameTag
in tag.GetList<TagCompound>(
"itemFrames")) {
476 if (
TileEntity.ByID.TryGetValue(frameTag.GetInt(
"id"), out
TileEntity tileEntity) && tileEntity is TEItemFrame itemFrame)
477 ItemIO.Load(itemFrame.item, frameTag.GetCompound(
"item"));
479 Logging.tML.Warn($
"Due to a bug in previous versions of tModLoader, the following ItemFrame data has been lost: {frameTag.ToString()}");
483 internal static void ReadContainers(
BinaryReader reader) {
484 byte[] flags =
new byte[1];
485 reader.Read(flags, 0, reader.ReadByte());
486 if ((flags[0] & 1) == 1) {
487 ContainerTables tables = ContainerTables.Create();
488 int count = reader.ReadUInt16();
489 for (
int k = 0; k < count; k++) {
490 int slot = reader.ReadUInt16();
491 string modName = reader.ReadString();
492 string name = reader.ReadString();
493 Mod mod = ModLoader.GetMod(modName);
494 tables.headSlots[slot] = mod?.GetItem(name).item.headSlot ?? 0;
496 count = reader.ReadUInt16();
497 for (
int k = 0; k < count; k++) {
498 int slot = reader.ReadUInt16();
499 string modName = reader.ReadString();
500 string name = reader.ReadString();
501 Mod mod = ModLoader.GetMod(modName);
502 tables.bodySlots[slot] = mod?.GetItem(name).item.bodySlot ?? 0;
504 count = reader.ReadUInt16();
505 for (
int k = 0; k < count; k++) {
506 int slot = reader.ReadUInt16();
507 string modName = reader.ReadString();
508 string name = reader.ReadString();
509 Mod mod = ModLoader.GetMod(modName);
510 tables.legSlots[slot] = mod?.GetItem(name).item.legSlot ?? 0;
512 ReadContainerData(reader, tables);
515 if ((flags[0] & 2) == 2) {
516 int count = reader.ReadInt32();
517 for (
int k = 0; k < count; k++) {
518 int id = reader.ReadInt32();
519 TEItemFrame itemFrame =
TileEntity.ByID[id] as TEItemFrame;
520 ItemIO.LoadLegacy(itemFrame.item, reader,
true);
525 internal static void WriteContainerData(
BinaryWriter writer) {
526 for (
int i = 0; i < Main.maxTilesX; i++) {
527 for (
int j = 0; j < Main.maxTilesY; j++) {
528 Tile tile = Main.tile[i, j];
529 if (tile.active() && (tile.type == TileID.Mannequin || tile.type == TileID.Womannequin)) {
530 int slot = tile.frameX / 100;
531 int frameX = tile.frameX % 100;
532 int position = tile.frameY / 18;
533 if (HasModArmor(slot, position) && frameX % 36 == 0) {
536 writer.Write((byte)position);
537 writer.Write((ushort)slot);
545 internal static void ReadContainerData(
BinaryReader reader, ContainerTables tables) {
546 int i = reader.ReadInt32();
548 int j = reader.ReadInt32();
549 int position = reader.ReadByte();
550 int slot = reader.ReadUInt16();
551 Tile left = Main.tile[i, j];
552 Tile right = Main.tile[i + 1, j];
553 if (left.active() && right.active() && (left.type == TileID.Mannequin || left.type == TileID.Womannequin)
554 && left.type == right.type && (left.frameX == 0 || left.frameX == 36) && right.frameX == left.frameX + 18
555 && left.frameY / 18 == position && left.frameY == right.frameY) {
557 slot = tables.headSlots[slot];
559 else if (position == 1) {
560 slot = tables.bodySlots[slot];
562 else if (position == 2) {
563 slot = tables.legSlots[slot];
565 left.frameX += (short)(100 * slot);
567 i = reader.ReadInt32();
571 private static bool HasModArmor(
int slot,
int position) {
573 return slot >= Main.numArmorHead;
575 else if (position == 1) {
576 return slot >= Main.numArmorBody;
578 else if (position == 2) {
579 return slot >= Main.numArmorLegs;
584 internal static List<TagCompound> SaveTileEntities() {
585 List<TagCompound> list =
new List<TagCompound>();
586 foreach (KeyValuePair<int, TileEntity> pair
in TileEntity.ByID) {
587 if (pair.Value.type >= ModTileEntity.numVanilla) {
588 ModTileEntity tileEntity = (ModTileEntity)pair.Value;
589 list.Add(
new TagCompound {
590 [
"mod"] = tileEntity.mod.Name,
591 [
"name"] = tileEntity.Name,
592 [
"X"] = tileEntity.Position.X,
593 [
"Y"] = tileEntity.Position.Y,
594 [
"data"] = tileEntity.Save()
601 internal static void LoadTileEntities(IList<TagCompound> list) {
602 foreach (TagCompound tag
in list) {
603 Mod mod = ModLoader.GetMod(tag.GetString(
"mod"));
604 ModTileEntity tileEntity = mod?.GetTileEntity(tag.GetString(
"name"));
605 ModTileEntity newEntity;
606 if (tileEntity != null) {
607 newEntity = ModTileEntity.ConstructFromBase(tileEntity);
608 newEntity.type = (byte)tileEntity.Type;
609 newEntity.Position =
new Point16(tag.GetShort(
"X"), tag.GetShort(
"Y"));
610 if (tag.ContainsKey(
"data")) {
612 newEntity.Load(tag.GetCompound(
"data"));
613 if (newEntity is MysteryTileEntity) {
614 ((MysteryTileEntity)newEntity).TryRestore(ref newEntity);
619 "Error in reading " + tileEntity.Name +
" tile entity data for " + mod.Name, e);
624 tileEntity = ModContent.GetInstance<ModLoaderMod>().GetTileEntity(
"MysteryTileEntity");
625 newEntity = ModTileEntity.ConstructFromBase(tileEntity);
626 newEntity.type = (byte)tileEntity.Type;
627 newEntity.Position =
new Point16(tag.GetShort(
"X"), tag.GetShort(
"Y"));
628 ((MysteryTileEntity)newEntity).SetData(tag);
630 if (tileEntity.ValidTile(newEntity.Position.X, newEntity.Position.Y)) {
634 if (
TileEntity.ByPosition.TryGetValue(newEntity.Position, out other)) {
637 TileEntity.ByPosition[newEntity.Position] = newEntity;
EquipType
This is an enum of all the types of equipment that exist. An equipment type is defined as a type or l...