2using System.Collections.Generic;
5using Terraria.DataStructures;
6using Terraria.GameContent.Tile_Entities;
8using Terraria.ModLoader.Default;
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() {
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++) {
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++) {
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");
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");
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();
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();
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;
421 itemFrames.Add(itemFrame.ID, tileEntity);
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);
439 writer.Write(item.
Name);
441 writer.Write((ushort)bodySlots.Count);
442 foreach (
int slot
in bodySlots) {
443 writer.Write((ushort)slot);
446 writer.Write(item.
Name);
448 writer.Write((ushort)legSlots.Count);
449 foreach (
int slot
in legSlots) {
450 writer.Write((ushort)slot);
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();
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();
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();
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) {
589 list.Add(
new TagCompound {
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) {
606 if (tileEntity !=
null) {
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");
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;
This serves as a central place to store equipment slots and their corresponding textures....
This serves as the central class from which item-related functions are carried out....
static ModItem GetItem(int type)
Gets the ModItem instance corresponding to the specified type. Returns null if no modded item has the...
static bool NeedsModSaving(Item item)
Manages content added by mods. Liasons between mod content and Terraria's arrays and oversees the Loa...
Mod is an abstract class that you will override. It serves as a central place from which the mod's co...
int TileType(string name)
Gets the type of the ModTile of this mod with the given name. Returns 0 if no ModTile with the given ...
ModItem GetItem(string name)
Gets the ModItem instance corresponding to the name. Because this method is in the Mod class,...
virtual string Name
Stores the name of the mod. This name serves as the mod's identification, and also helps with saving ...
int WallType(string name)
Gets the type of the ModWall of this mod with the given name. Returns 0 if no ModWall with the given ...
ModTileEntity GetTileEntity(string name)
Gets the ModTileEntity of this mod corresponding to the given name. Returns null if no ModTileEntity ...
This class serves as a place for you to place all your properties and hooks for each item....
string Name
The internal name of this ModItem.
Item item
The item object that this ModItem controls.
This serves as the central class which loads mods. It contains many static fields and methods related...
static Mod GetMod(string name)
Gets the instance of the Mod with the specified name.
Tile Entities are Entities tightly coupled with tiles, allowing the possibility of tiles to exhibit c...
int Type
The numeric type used to identify this kind of tile entity.
static ModTileEntity ConstructFromBase(ModTileEntity tileEntity)
Returns a new ModTileEntity with the same class, mod, name, and type as the parameter....
abstract bool ValidTile(int i, int j)
Whether or not this tile entity is allowed to survive at the given coordinates. You should check whet...
virtual void Load(TagCompound tag)
Allows you to load the custom data you have saved for this tile entity.
virtual TagCompound Save()
Allows you to save custom data for this tile entity.
string Name
The internal name of this ModTileEntity.
Mod mod
The mod that added this ModTileEntity.
This serves as the central class from which tile-related functions are supported and carried out.
static ModTile GetTile(int type)
Gets the ModTile instance with the given type. If no ModTile with the given type exists,...
This serves as the central class from which wall-related functions are supported and carried out.
static ModWall GetWall(int type)
Gets the ModWall instance with the given type. If no ModWall with the given type exists,...
EquipType
This is an enum of all the types of equipment that exist. An equipment type is defined as a type or l...