├── .gitattributes ├── .gitignore ├── Constants.cs ├── Devices ├── Area.cs ├── Comparison │ ├── Equals.cs │ ├── GreaterThan.cs │ └── LessThan.cs ├── Device.cs ├── DeviceSerializer.cs ├── Gates │ ├── AndGate.cs │ ├── If.cs │ ├── NotGate.cs │ └── OrGate.cs ├── IOutput.cs ├── ITriggered.cs ├── Inputs │ ├── AreaInput.cs │ ├── BooleanConstant.cs │ ├── IntegerConstant.cs │ ├── PointConstant.cs │ ├── RandomInt.cs │ ├── StringConstant.cs │ └── TeamColorConstant.cs ├── Maths │ ├── Add.cs │ ├── Divide.cs │ ├── Modulo.cs │ ├── Multiply.cs │ └── Subtract.cs ├── Other │ ├── Buffer.cs │ ├── ProtectArea.cs │ ├── Repulsor.cs │ └── Spawner.cs ├── Outputs │ ├── FlipFlop.cs │ ├── OutputLamp.cs │ └── Trigger.cs ├── Pin.cs ├── PinDesign.cs ├── Sensors │ ├── NPCCounter.cs │ ├── NPCDistanceSensor.cs │ ├── PlayerCounter.cs │ ├── PlayerDistanceSensor.cs │ ├── TileCounter.cs │ └── TimeSensor.cs ├── Storage │ ├── TileCopier.cs │ ├── TileInfo.cs │ ├── TilePaster.cs │ └── Variable.cs ├── Strings │ ├── Announcer.cs │ ├── Concat.cs │ └── TextTileController.cs └── Wire.cs ├── ENums.cs ├── Helpers.cs ├── Images ├── Add.png ├── AndGate.png ├── Announcer.png ├── AreaInput.png ├── BooleanConstant.png ├── Buffer.png ├── Concat.png ├── Divide.png ├── Equals.png ├── FlipFlop.png ├── GreaterThan.png ├── Icons │ ├── AddIcon.png │ ├── AndGateIcon.png │ ├── AnnouncerIcon.png │ ├── AreaInputIcon.png │ ├── BooleanConstantIcon.png │ ├── BufferIcon.png │ ├── ConcatIcon.png │ ├── DebugIcon.png │ ├── DeleteIcon.png │ ├── DivideIcon.png │ ├── EqualsIcon.png │ ├── FlipFlopIcon.png │ ├── GreaterThanIcon.png │ ├── IfIcon.png │ ├── IntegerConstantIcon.png │ ├── LessThanIcon.png │ ├── ModuloIcon.png │ ├── MultiplyIcon.png │ ├── NPCCounterIcon.png │ ├── NPCDistanceSensorIcon.png │ ├── NoneIcon.png │ ├── NotGateIcon.png │ ├── OrGateIcon.png │ ├── OutputLampIcon.png │ ├── OutputSignIcon.png │ ├── PlayerCounterIcon.png │ ├── PlayerDistanceSensorIcon.png │ ├── PointConstantIcon.png │ ├── ProtectAreaIcon.png │ ├── RandomIntIcon.png │ ├── RepulsorIcon.png │ ├── SpawnerIcon.png │ ├── StringConstantIcon.png │ ├── SubtractIcon.png │ ├── TeamColorConstantIcon.png │ ├── TextTileControllerIcon.png │ ├── TileCopierIcon.png │ ├── TileCounterIcon.png │ ├── TilePasterIcon.png │ ├── TimeSensorIcon.png │ ├── TriggerIcon.png │ ├── VariableIcon.png │ └── WiringIcon.png ├── If.png ├── IntegerConstant.png ├── LessThan.png ├── Modulo.png ├── Multiply.png ├── NPCCounter.png ├── NPCDistanceSensor.png ├── NotGate.png ├── OrGate.png ├── OutputLamp.png ├── OutputSign.png ├── PlayerCounter.png ├── PlayerDistanceSensor.png ├── PointConstant.png ├── ProtectArea.png ├── RandomInt.png ├── Repulsor.png ├── Spawner.png ├── StringConstant.png ├── Subtract.png ├── TeamColorConstant.png ├── TextTileController.png ├── TileCopier.png ├── TileCounter.png ├── TilePaster.png ├── TimeSensor.png ├── Trigger.png └── Variable.png ├── Items ├── ElectronicsManual.cs ├── ElectronicsManual.png ├── Microchip.cs ├── Microchip.png ├── TextTileItem.cs └── TextTileItem.png ├── PacketHandler.cs ├── Properties └── launchSettings.json ├── README.md ├── Tiles ├── TextTile.cs └── TextTile.png ├── UI ├── DebuggerUI.cs ├── ElectronicsManualButton.cs ├── ElectronicsManualUI.cs ├── ElectronicsVisionUI.cs ├── Elements │ ├── DragableUIPanel.cs │ ├── UIClickableButton.cs │ └── UIInputTextField.cs ├── HoverDebuggerUI.cs └── UserInputUI.cs ├── WireMod.cs ├── WireMod.csproj ├── WireMod.csproj.DotSettings ├── WireMod.csproj.user ├── WireMod.sln ├── WireModGlobalTile.cs ├── WireModPlayer.cs ├── WireModWorld.cs ├── app.config ├── build.txt ├── description.txt └── icon.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ -------------------------------------------------------------------------------- /Constants.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.ID; 3 | 4 | namespace WireMod 5 | { 6 | public static class Constants 7 | { 8 | public static readonly List ToolCategories = new List 9 | { 10 | "Tools", 11 | "Logic Gates", 12 | "Maths", 13 | "Comparison", 14 | "Basic Inputs", 15 | "Sensor Inputs", 16 | "Storage", 17 | "Text", 18 | "Outputs", 19 | "Experimental", 20 | }; 21 | 22 | public static readonly List> Tools = new List> 23 | { 24 | new List // Tools 25 | { 26 | "None", 27 | "Wiring", 28 | "Delete", 29 | "Debug", 30 | }, 31 | new List // Logic Gates 32 | { 33 | "AndGate", 34 | "OrGate", 35 | "NotGate", 36 | "If", 37 | }, 38 | new List // Maths 39 | { 40 | "Add", 41 | "Subtract", 42 | "Multiply", 43 | "Divide", 44 | "Modulo", 45 | }, 46 | new List // Comparison 47 | { 48 | "Equals", 49 | "LessThan", 50 | "GreaterThan", 51 | }, 52 | new List // Basic Inputs 53 | { 54 | "BooleanConstant", 55 | "IntegerConstant", 56 | "StringConstant", 57 | "TeamColorConstant", 58 | "PointConstant", 59 | "AreaInput", 60 | "RandomInt", 61 | }, 62 | new List // Sensor Inputs 63 | { 64 | "TimeSensor", 65 | "PlayerDistanceSensor", 66 | "NPCDistanceSensor", 67 | "PlayerCounter", 68 | "NPCCounter", 69 | "TileCounter", 70 | }, 71 | new List // Storage 72 | { 73 | "Variable", 74 | "TileCopier", 75 | "TilePaster", 76 | }, 77 | new List // Text 78 | { 79 | "Concat", 80 | "TextTileController", 81 | "Announcer", 82 | }, 83 | new List // Outputs 84 | { 85 | "OutputLamp", 86 | "FlipFlop", 87 | "Trigger", 88 | }, 89 | new List // Experimental 90 | { 91 | "Repulsor", 92 | "Spawner", 93 | "Buffer", 94 | "ProtectArea", 95 | }, 96 | }; 97 | 98 | public static readonly Dictionary ToolNames = new Dictionary 99 | { 100 | { "None", "None" }, 101 | { "Wiring", "Wiring" }, 102 | { "Delete", "Remove Device" }, 103 | { "Debug", "Debug Device" }, 104 | { "AndGate", "AND Logic Gate" }, 105 | { "OrGate", "OR Logic Gate" }, 106 | { "NotGate", "NOT Logic Gate" }, 107 | { "If", "Conditional Logic Gate" }, 108 | { "Add", "Add Operation" }, 109 | { "Subtract", "Subtract Operation" }, 110 | { "Multiply", "Multiply Operation" }, 111 | { "Divide", "Divide Operation" }, 112 | { "Modulo", "Modulo Operation" }, 113 | { "Equals", "Equals (=) Comparer" }, 114 | { "LessThan", "Less Than (<) Comparer" }, 115 | { "GreaterThan", "Greater Than (>) Comparer" }, 116 | { "BooleanConstant", "Constant Boolean Value" }, 117 | { "IntegerConstant", "Constant Number Value" }, 118 | { "StringConstant", "Constant String Value" }, 119 | { "TeamColorConstant", "Constant Team Color Value" }, 120 | { "PointConstant", "Constant Point Value" }, 121 | { "RandomInt", "Random Integer Value" }, 122 | { "AreaInput", "Area Value" }, 123 | { "Concat", "Concatenate Strings" }, 124 | { "TextTileController", "Text Tile Controller" }, 125 | { "Announcer", "Announces Messages" }, 126 | { "FlipFlop", "Flip/Flop" }, 127 | { "OutputLamp", "Output Lamp" }, 128 | { "Trigger", "Trigger Output" }, 129 | { "PlayerDistanceSensor", "Nearest Player Distance Sensor" }, 130 | { "NPCDistanceSensor", "Nearest NPC Distance Sensor" }, 131 | { "TimeSensor", "Time Sensor - basically a glorified clock" }, 132 | { "PlayerCounter", "Count nearby Players" }, 133 | { "NPCCounter", "Count nearby NPCs" }, 134 | { "TileCounter", "Count nearby tiles" }, 135 | { "Repulsor", "Repulsor (USE WITH CAUTION)" }, 136 | { "Spawner", "NPC Spawner (USE WITH CAUTION)" }, 137 | { "Buffer", "Buffer (USE WITH CAUTION)" }, 138 | { "Variable", "Store value" }, 139 | { "TileCopier", "Copies Tiles" }, 140 | { "TilePaster", "Pastes Tiles" }, 141 | { "ProtectArea", "Protect an area of tiles" } 142 | }; 143 | 144 | public static readonly List CopyTileBlacklist = new List 145 | { 146 | TileID.DemonAltar, 147 | TileID.ShadowOrbs, 148 | TileID.Containers, 149 | TileID.Containers2, 150 | }; 151 | } 152 | } -------------------------------------------------------------------------------- /Devices/Area.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using Terraria; 5 | 6 | namespace WireMod.Devices 7 | { 8 | public static class AreaFactory 9 | { 10 | public static Area Create(string input) 11 | { 12 | if (string.IsNullOrEmpty(input) || !input.Contains(":")) return null; 13 | 14 | var split = input.Split(':'); 15 | 16 | if (!int.TryParse(split[1], out var distance)) return null; 17 | 18 | var pos = split[2].Split(','); 19 | if (!int.TryParse(pos[0], out var x) || !int.TryParse(pos[1], out var y)) return null; 20 | 21 | return Create(split[0], distance, new Vector2(x, y)); 22 | } 23 | 24 | public static Area Create(string shape, int distance, Vector2 pos) 25 | { 26 | switch (shape) 27 | { 28 | case "Circle": 29 | return new CircArea 30 | { 31 | Center = pos, 32 | Radius = distance 33 | }; 34 | case "Square": 35 | default: 36 | return new RectArea 37 | { 38 | Center = pos, 39 | Radius = distance 40 | }; 41 | } 42 | } 43 | } 44 | 45 | public abstract class Area 46 | { 47 | public Vector2 Center { get; set; } 48 | public int Radius { get; set; } 49 | 50 | public abstract bool Contains(Vector2 point); 51 | 52 | public virtual void Draw(SpriteBatch spriteBatch, Color color) { } 53 | } 54 | 55 | public class RectArea : Area 56 | { 57 | public Rectangle GetRect() => new Rectangle((int)this.Center.X - this.Radius - 8, (int)this.Center.Y - this.Radius - 8, this.Radius * 2 + 16, this.Radius * 2 + 16); 58 | 59 | public override bool Contains(Vector2 point) => this.GetRect().Contains(point.ToPoint()); 60 | 61 | public override void Draw(SpriteBatch spriteBatch, Color color) 62 | { 63 | if (!Helpers.GetScreenRect().Intersects(this.GetRect())) return; 64 | 65 | var size = this.GetRect().Width; 66 | var rect = Helpers.CreateRectangle(size, size); 67 | spriteBatch.Draw(rect, this.Center - Main.screenPosition - new Vector2(size / 2, size / 2) - Helpers.Drift, color * 0.25f); 68 | } 69 | 70 | public TileArea GetTileArea() 71 | { 72 | return new TileArea 73 | { 74 | Center = this.Center / 16, 75 | Radius = this.Radius / 16, 76 | }; 77 | } 78 | } 79 | 80 | public class TileArea : Area 81 | { 82 | public Rectangle GetRect() => new Rectangle((int)this.Center.X - this.Radius, (int)this.Center.Y - this.Radius, this.Radius * 2 + 1, this.Radius * 2 + 1); 83 | 84 | public override bool Contains(Vector2 point) => this.GetRect().Contains(point.ToPoint()); 85 | 86 | public override void Draw(SpriteBatch spriteBatch, Color color) 87 | { 88 | var rect = this.GetRect(); 89 | var worldRect = new Rectangle(rect.X * 16, rect.Y * 16, rect.Width * 16, rect.Height * 16); 90 | if (!Helpers.GetScreenRect().Intersects(worldRect)) return; 91 | 92 | spriteBatch.Draw(Helpers.CreateRectangle(worldRect.Width, worldRect.Height), this.Center - Main.screenPosition - new Vector2(worldRect.Width / 2, worldRect.Height / 2) - Helpers.Drift, color * 0.25f); 93 | } 94 | } 95 | 96 | public class CircArea : Area 97 | { 98 | public override bool Contains(Vector2 point) => (this.Center - point).Length() < this.Radius; 99 | 100 | public override void Draw(SpriteBatch spriteBatch, Color color) 101 | { 102 | var circ = Helpers.CreateCircle(this.Radius * 2); 103 | spriteBatch.Draw(circ, this.Center - Main.screenPosition - new Vector2(this.Radius, this.Radius) - Helpers.Drift, color * 0.25f); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Devices/Comparison/Equals.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | using Terraria.DataStructures; 4 | 5 | namespace WireMod.Devices 6 | { 7 | internal class Equals : Device, IOutput 8 | { 9 | public Equals() 10 | { 11 | this.Name = "Equals"; 12 | this.Width = 3; 13 | this.Height = 2; 14 | this.Origin = new Point16(1, 0); 15 | 16 | this.AutoTypes.AddRange(new [] {"int", "bool", "string"}); 17 | 18 | this.PinLayout = new List 19 | { 20 | new PinDesign("In", 0, new Point16(0, 0), "int", "", true), 21 | new PinDesign("In", 1, new Point16(2, 0), "int", "", true), 22 | new PinDesign("Out", 0, new Point16(1, 1), "bool"), 23 | }; 24 | } 25 | 26 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 27 | 28 | private int GetOutput() 29 | { 30 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 31 | if (this.GetPinIn(0).DataType != this.GetPinIn(1).DataType) return -1; 32 | 33 | switch (this.GetPinIn(0).DataType) 34 | { 35 | case "int": 36 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -2; 37 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -2; 38 | return in0 == in1 ? 1 : 0; 39 | case "string": 40 | default: 41 | return GetPinIn(0).GetValue() == this.GetPinIn(1).GetValue() ? 1 : 0; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Devices/Comparison/GreaterThan.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class GreaterThan : Device, IOutput 7 | { 8 | public GreaterThan() 9 | { 10 | this.Name = "Greater Than"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "bool"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.Pins["In"][1].IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 > in1 ? 1 : 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Comparison/LessThan.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class LessThan : Device, IOutput 7 | { 8 | public LessThan() 9 | { 10 | this.Name = "Less Than"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "bool"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 < in1 ? 1 : 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Device.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | using WireMod.Items; 8 | 9 | namespace WireMod.Devices 10 | { 11 | public abstract class Device 12 | { 13 | // Design 14 | public string Name { get; set; } = ""; 15 | public int Width { get; set; } = 1; 16 | public int Height { get; set; } = 2; 17 | 18 | public List AutoTypes { get; set; } = new List(); 19 | public bool AutoDetectType => this.AutoTypes.Count > 0; 20 | 21 | public Dictionary Settings { get; set; } = new Dictionary(); 22 | 23 | public string RightClickHelp { get; set; } 24 | 25 | /// 26 | /// Local origin location, almost always the 'chip' part of the 'tile'. 27 | /// This is also where the 'tile' is centered when placing. 28 | /// 29 | public Point16 Origin { get; set; } = new Point16(0, 0); 30 | public List PinLayout { get; set; } = new List(); 31 | 32 | 33 | // Instance 34 | public Dictionary> Pins = new Dictionary> 35 | { 36 | {"In", new Dictionary()}, 37 | {"Out", new Dictionary()}, 38 | }; 39 | 40 | /// 41 | /// Location of this device in the world (Tile Co-ords) 42 | /// 43 | public Rectangle LocationRect { get; private set; } 44 | public Point16 LocationTile { get; private set; } 45 | public Vector2 LocationWorld { get; private set; } 46 | public Rectangle LocationWorldRect { get; private set; } 47 | public Rectangle LocationScreenRect 48 | { 49 | get 50 | { 51 | var screenRect = Helpers.GetScreenRect(); 52 | var worldRect = this.LocationWorldRect; 53 | 54 | return !worldRect.Intersects(screenRect) ? default(Rectangle) : new Rectangle(worldRect.X - screenRect.X, worldRect.Y - screenRect.Y, worldRect.Width, worldRect.Height); 55 | } 56 | } 57 | 58 | public Point16 LocationOriginTile { get; private set; } 59 | public Vector2 LocationOriginWorld { get; private set; } 60 | public Vector2 LocationOriginScreen { get; private set; } 61 | 62 | public void SetLocation(int x, int y) 63 | { 64 | this.LocationRect = new Rectangle(x, y, this.Width, this.Height); 65 | this.LocationTile = new Point16(this.LocationRect.X, this.LocationRect.Y); 66 | this.LocationWorld = new Vector2(this.LocationRect.X * 16, this.LocationRect.Y * 16); 67 | this.LocationWorldRect = new Rectangle(this.LocationRect.X * 16, this.LocationRect.Y * 16, this.Width * 16, this.Height * 16); 68 | 69 | this.LocationOriginTile = this.LocationTile + this.Origin; 70 | this.LocationOriginWorld = this.LocationOriginTile.ToWorldCoordinates(); 71 | this.LocationOriginScreen = this.LocationOriginWorld - Main.screenPosition; 72 | } 73 | 74 | public string DetectType() 75 | { 76 | if (!this.AutoDetectType) return ""; 77 | 78 | var dataType = "auto"; 79 | 80 | foreach (var p in this.Pins.Values.SelectMany(p => p.Values).Where(p => p.Auto)) 81 | { 82 | if (p.IsConnected()) 83 | { 84 | dataType = (p is PinIn pinIn) ? pinIn.ConnectedPin.DataType : ((PinOut)p).ConnectedPins.First().DataType; 85 | break; 86 | } 87 | } 88 | 89 | return dataType; 90 | } 91 | 92 | public void SetAutoType(string dataType = null) 93 | { 94 | if (dataType == null) dataType = this.DetectType(); 95 | if (dataType == "auto") dataType = this.AutoTypes.First(); 96 | 97 | foreach (var p in this.Pins.Values.SelectMany(p => p.Values).Where(p => p.Auto)) 98 | { 99 | p.DataType = dataType; 100 | } 101 | } 102 | 103 | public Pin GetPin(string name) => (Pin)GetPinIn(name) ?? GetPinOut(name); 104 | public PinIn GetPinIn(string name) => (PinIn)this.Pins["In"].Values.FirstOrDefault(p => p.Name == name); 105 | public PinIn GetPinIn(int index) => (PinIn)this.Pins["In"][index]; 106 | public PinOut GetPinOut(string name) => (PinOut)this.Pins["Out"].Values.FirstOrDefault(p => p.Name == name); 107 | public PinOut GetPinOut(int index) => (PinOut)this.Pins["Out"][index]; 108 | 109 | public virtual void OnRightClick(Pin pin = null) { } 110 | public virtual void OnHitWire(Pin pin = null) { } 111 | 112 | public virtual void OnKill() 113 | { 114 | // Drop a microchip at the player's location 115 | Item.NewItem((int)Main.LocalPlayer.position.X, (int)Main.LocalPlayer.position.Y, 16, 16, WireMod.Instance.ItemType()); 116 | } 117 | 118 | public virtual void OnPlace() { } 119 | 120 | public virtual void Draw(SpriteBatch spriteBatch) { } 121 | public virtual void Update(GameTime gameTime) { } 122 | 123 | public virtual Rectangle GetSourceRect(int style = -1) => new Rectangle(0, 0, this.Width * 16, this.Height * 16); 124 | 125 | public void SetPins() 126 | { 127 | if (this.LocationRect == default(Rectangle)) return; 128 | 129 | this.Pins["In"].Clear(); 130 | this.Pins["Out"].Clear(); 131 | 132 | foreach (var pinDesign in this.PinLayout) 133 | { 134 | var pin = pinDesign.Type == "In" ? (Pin)(new PinIn(this)) : (Pin)(new PinOut(this)); 135 | pin.Num = pinDesign.Num; 136 | pin.DataType = pinDesign.DataType; 137 | pin.Type = pinDesign.Type; 138 | pin.Name = pinDesign.Name; 139 | pin.Auto = pinDesign.Auto; 140 | 141 | this.Pins[pinDesign.Type].Add(pinDesign.Num, pin); 142 | } 143 | } 144 | 145 | public virtual List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 146 | { 147 | var defaultColor = Color.Black; 148 | var titleColor = Color.DarkBlue; 149 | var highlightColor = Color.Red; 150 | 151 | var lines = new List<(string, Color, float)> 152 | { 153 | (this.Name, titleColor, 1f), 154 | ($"X: {(pin != null ? pin.Location.X : this.LocationTile.X + this.Origin.X)}, Y: {(pin != null ? pin.Location.Y : this.LocationTile.Y + this.Origin.Y)}", titleColor, WireMod.SmallText), 155 | ("-----------------", defaultColor, 1f), 156 | }; 157 | 158 | foreach (var p in this.Pins.Values.SelectMany(p => p.Values)) 159 | { 160 | lines.AddRange(p.GetDebug().Select(d => (d, p == Main.LocalPlayer.GetModPlayer().ConnectingPin ? Color.Green : (p == pin ? highlightColor : defaultColor), WireMod.SmallText))); 161 | } 162 | 163 | if (this.Settings.Any()) 164 | { 165 | lines.Add(("-----------------", defaultColor, WireMod.SmallText)); 166 | foreach (var setting in this.Settings) 167 | { 168 | lines.Add(($"{setting.Key}: {setting.Value}", Color.Red, WireMod.SmallText)); 169 | } 170 | } 171 | 172 | if (!string.IsNullOrEmpty(this.RightClickHelp)) 173 | { 174 | lines.Add(("-----------------", defaultColor, WireMod.SmallText)); 175 | lines.Add((this.RightClickHelp, Color.Blue, WireMod.SmallText)); 176 | 177 | } 178 | 179 | return lines; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Devices/DeviceSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria.ModLoader.IO; 5 | 6 | namespace WireMod.Devices 7 | { 8 | public class DeviceSerializer : TagSerializer 9 | { 10 | public override TagCompound Serialize(Device device) 11 | { 12 | var tag = new TagCompound 13 | { 14 | ["name"] = device.GetType().FullName, 15 | ["x"] = device.LocationRect.X + device.Origin.X, 16 | ["y"] = device.LocationRect.Y + device.Origin.Y, 17 | }; 18 | 19 | foreach (var setting in device.Settings) 20 | { 21 | tag[setting.Key] = setting.Value; 22 | } 23 | 24 | return tag; 25 | } 26 | 27 | public override Device Deserialize(TagCompound tag) 28 | { 29 | var name = tag.GetString("name"); 30 | if (!name.Contains(".")) name = "WireMod.Devices." + name; 31 | 32 | var device = (Device)Activator.CreateInstance(Type.GetType(name) ?? throw new InvalidOperationException("Device not found!")); 33 | device.SetLocation(tag.GetInt("x"), tag.GetInt("y")); 34 | 35 | foreach (var setting in tag.Where(t => !new[] {"name", "x", "y"}.Contains(t.Key))) 36 | { 37 | if (!device.Settings.ContainsKey(setting.Key)) continue; 38 | device.Settings[setting.Key] = setting.Value.ToString(); 39 | } 40 | 41 | return device; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Devices/Gates/AndGate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class AndGate : Device, IOutput 7 | { 8 | public AndGate() 9 | { 10 | this.Name = "AND Gate"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "bool"), 18 | new PinDesign("In", 1, new Point16(2, 0), "bool"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "bool"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.Pins["In"][1].IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 == 1 && in1 == 1 ? 1 : 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Gates/If.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class If : Device, IOutput 7 | { 8 | public If() 9 | { 10 | this.Name = "If Condition"; 11 | this.Width = 3; 12 | this.Height = 3; 13 | this.Origin = new Point16(1, 1); 14 | 15 | this.AutoTypes.AddRange(new [] {"int", "bool", "string", "area"}); 16 | 17 | this.PinLayout = new List 18 | { 19 | new PinDesign("In", 0, new Point16(1, 0), "bool", "Condition"), 20 | new PinDesign("In", 1, new Point16(0, 1), "int", "TrueValue", true), 21 | new PinDesign("In", 2, new Point16(2, 1), "int", "FalseValue", true), 22 | new PinDesign("Out", 0, new Point16(1, 2), "int", "", true), 23 | }; 24 | } 25 | 26 | public string Output(Pin pin = null) 27 | { 28 | if (!this.GetPin("Condition").IsConnected()) return "-1"; 29 | if (!int.TryParse(this.GetPin("Condition").GetValue(), out var condition)) return "-2"; 30 | 31 | switch (this.DetectType()) 32 | { 33 | case "auto": return "-1"; 34 | case "bool": 35 | case "int": 36 | if (!this.GetPin("TrueValue").IsConnected() || !int.TryParse(this.GetPin("TrueValue").GetValue(), out var trueInput)) trueInput = 0; 37 | if (!this.GetPin("FalseValue").IsConnected() || !int.TryParse(this.GetPin("FalseValue").GetValue(), out var falseInput)) falseInput = 0; 38 | return condition == 1 ? trueInput.ToString() : falseInput.ToString(); 39 | case "string": 40 | case "area": 41 | return condition == 1 42 | ? (this.GetPin("TrueValue").IsConnected() ? this.GetPin("TrueValue").GetValue() : "") 43 | : (this.GetPin("FalseValue").IsConnected() ? this.GetPin("FalseValue").GetValue() : ""); 44 | 45 | // TODO: Add other data types 46 | } 47 | 48 | return "-2"; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Devices/Gates/NotGate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class NotGate : Device, IOutput 7 | { 8 | public NotGate() 9 | { 10 | this.Name = "NOT Gate"; 11 | this.Width = 1; 12 | this.Height = 3; 13 | this.Origin = new Point16(0, 1); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "bool"), 18 | new PinDesign("Out", 0, new Point16(0, 2), "bool"), 19 | }; 20 | } 21 | 22 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 23 | 24 | private int GetOutput() 25 | { 26 | if (!this.GetPinIn(0).IsConnected()) return -1; 27 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 28 | return in0 == 1 ? 0 : 1; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Devices/Gates/OrGate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class OrGate : Device, IOutput 7 | { 8 | public OrGate() 9 | { 10 | this.Name = "OR Gate"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "bool"), 18 | new PinDesign("In", 1, new Point16(2, 0), "bool"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "bool"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.Pins["In"][1].IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 == 1 || in1 == 1 ? 1 : 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/IOutput.cs: -------------------------------------------------------------------------------- 1 | namespace WireMod.Devices 2 | { 3 | public interface IOutput 4 | { 5 | string Output(Pin pin = null); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Devices/ITriggered.cs: -------------------------------------------------------------------------------- 1 | namespace WireMod.Devices 2 | { 3 | public interface ITriggered 4 | { 5 | void Trigger(Pin pin = null); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Devices/Inputs/AreaInput.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | 8 | namespace WireMod.Devices 9 | { 10 | internal class AreaInput : Device, IOutput 11 | { 12 | public AreaInput() 13 | { 14 | this.Name = "Area Input"; 15 | this.Width = 2; 16 | this.Height = 3; 17 | this.Origin = new Point16(1, 1); 18 | 19 | this.Settings.Add("AreaType", AreaTypes.First()); 20 | 21 | this.RightClickHelp = "Right Click to change area type"; 22 | 23 | this.PinLayout = new List 24 | { 25 | new PinDesign("In", 0, new Point16(1, 0), "int", "Distance"), 26 | new PinDesign("In", 1, new Point16(0, 1), "point", "Point"), 27 | new PinDesign("Out", 0, new Point16(1, 2), "area", "Area"), 28 | }; 29 | } 30 | 31 | public string Output(Pin pin = null) 32 | { 33 | if (!this.GetPin("Distance").IsConnected() || !int.TryParse(this.GetPin("Distance").GetValue(), out var distance)) return ""; 34 | 35 | var pos = this.LocationOriginWorld; 36 | if (this.GetPin("Point").IsConnected() && Helpers.TryParsePoint(this.GetPin("Point").GetValue(), out var point) && point.HasValue) 37 | { 38 | pos = point.Value.ToWorldCoordinates(); 39 | } 40 | 41 | return $"{this.Settings["AreaType"]}:{distance}:{pos.X},{pos.Y}"; 42 | } 43 | 44 | public override void Draw(SpriteBatch spriteBatch) 45 | { 46 | if (!this.GetPin("Distance").IsConnected()) return; 47 | 48 | var area = AreaFactory.Create(this.Output()); 49 | 50 | area.Draw(spriteBatch, Color.LightGreen); 51 | } 52 | 53 | public override void OnRightClick(Pin pin = null) 54 | { 55 | this.Settings["AreaType"] = AreaTypes[(AreaTypes.IndexOf(this.Settings["AreaType"]) + 1) % AreaTypes.Count]; 56 | } 57 | 58 | private static readonly List AreaTypes = new List 59 | { 60 | "Square", 61 | "Circle", 62 | }; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Devices/Inputs/BooleanConstant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | using Terraria.ID; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class BooleanConstant : Device, IOutput 10 | { 11 | public BooleanConstant() 12 | { 13 | this.Name = "Boolean Constant"; 14 | 15 | this.Settings.Add("Value", "0"); 16 | 17 | this.RightClickHelp = "Right Click to toggle value"; 18 | 19 | this.PinLayout = new List 20 | { 21 | new PinDesign("Out", 0, new Point16(0, 1), "bool"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) => this.Settings["Value"]; 26 | 27 | public override void OnRightClick(Pin pin = null) 28 | { 29 | this.Settings["Value"] = (this.Settings["Value"] == "0" ? "1" : "0"); 30 | 31 | if (Main.netMode == NetmodeID.MultiplayerClient) 32 | { 33 | WireMod.PacketHandler.SendChangeSetting(256, Main.myPlayer, this.LocationTile.X, this.LocationTile.Y, "Value", this.Settings["Value"]); 34 | } 35 | } 36 | 37 | public override Rectangle GetSourceRect(int style = -1) 38 | { 39 | if (style == -1) 40 | { 41 | if (!int.TryParse(this.Settings["Value"], out var value)) return base.GetSourceRect(style); 42 | style = value; 43 | } 44 | 45 | return new Rectangle(style * (this.Width * 16), 0, this.Width * 16, this.Height * 16); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Devices/Inputs/IntegerConstant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria; 3 | using Terraria.DataStructures; 4 | using Terraria.ID; 5 | using WireMod.UI; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class IntegerConstant : Device, IOutput 10 | { 11 | public IntegerConstant() 12 | { 13 | this.Name = "Constant Integer"; 14 | 15 | this.Settings.Add("Value", "0"); 16 | 17 | this.RightClickHelp = "Right Click to change value"; 18 | 19 | this.PinLayout = new List 20 | { 21 | new PinDesign("Out", 0, new Point16(0, 1), "int"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) => this.Settings["Value"]; 26 | 27 | public override void OnRightClick(Pin pin = null) 28 | { 29 | var input = new UserInputUI(this.Settings["Value"]); 30 | 31 | input.OnSave += (s, e) => 32 | { 33 | if (!int.TryParse(input.Value, out var value)) 34 | { 35 | Main.NewText($"Could not parse '{input.Value}' as an integer"); 36 | return; 37 | } 38 | 39 | this.Settings["Value"] = input.Value; 40 | 41 | if (Main.netMode == NetmodeID.MultiplayerClient) 42 | { 43 | WireMod.PacketHandler.SendChangeSetting(256, Main.myPlayer, this.LocationTile.X, this.LocationTile.Y, "Value", this.Settings["Value"]); 44 | } 45 | }; 46 | 47 | WireMod.Instance.UserInputUserInterface.SetState(input); 48 | UserInputUI.Visible = true; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Devices/Inputs/PointConstant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class PointConstant : Device, IOutput 7 | { 8 | public PointConstant() 9 | { 10 | this.Name = "Point Input"; 11 | this.Width = 1; 12 | this.Height = 2; 13 | this.Origin = new Point16(0, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("Out", 0, new Point16(0, 1), "point"), 18 | }; 19 | } 20 | 21 | public string Output(Pin pin = null) => $"{this.LocationOriginTile.X},{this.LocationOriginTile.Y}"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Devices/Inputs/RandomInt.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria; 3 | using Terraria.DataStructures; 4 | 5 | namespace WireMod.Devices 6 | { 7 | internal class RandomInt : Device, IOutput, ITriggered 8 | { 9 | public RandomInt() 10 | { 11 | this.Name = "Random Integer"; 12 | this.Width = 3; 13 | this.Height = 3; 14 | this.Origin = new Point16(1, 1); 15 | 16 | this.Settings.Add("Value", "0"); 17 | 18 | this.RightClickHelp = "Right Click to generate new value"; 19 | 20 | this.PinLayout = new List 21 | { 22 | new PinDesign("In", 0, new Point16(1, 0), "bool", "Trigger"), 23 | new PinDesign("In", 1, new Point16(0, 1), "int", "Min"), 24 | new PinDesign("In", 2, new Point16(2, 1), "int", "Max"), 25 | new PinDesign("Out", 0, new Point16(1, 2), "int"), 26 | }; 27 | } 28 | 29 | public string Output(Pin pin = null) => this.Settings["Value"]; 30 | 31 | public override void OnRightClick(Pin pin = null) => this.Trigger(); 32 | 33 | public void Trigger(Pin pin = null) 34 | { 35 | if (!int.TryParse(this.GetPin("Min").GetValue(), out var min)) min = 0; 36 | if (!int.TryParse(this.GetPin("Max").GetValue(), out var max)) max = 100; 37 | 38 | this.Settings["Value"] = WorldGen.genRand.Next(min, max).ToString(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Devices/Inputs/StringConstant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria; 3 | using Terraria.DataStructures; 4 | using Terraria.ID; 5 | using WireMod.UI; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class StringConstant : Device, IOutput 10 | { 11 | public StringConstant() 12 | { 13 | this.Name = "Constant String"; 14 | 15 | this.Settings.Add("Value", ""); 16 | 17 | this.RightClickHelp = "Right Click to change value"; 18 | 19 | this.PinLayout = new List 20 | { 21 | new PinDesign("Out", 0, new Point16(0, 1), "string"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) => this.Settings["Value"]; 26 | 27 | public override void OnRightClick(Pin pin = null) 28 | { 29 | var input = new UserInputUI(this.Settings["Value"]); 30 | input.OnSave += (s, e) => 31 | { 32 | this.Settings["Value"] = input.Value; 33 | 34 | if (Main.netMode == NetmodeID.MultiplayerClient) 35 | { 36 | WireMod.PacketHandler.SendChangeSetting(256, Main.myPlayer, this.LocationTile.X, this.LocationTile.Y, "Value", this.Settings["Value"]); 37 | } 38 | }; 39 | 40 | WireMod.Instance.UserInputUserInterface.SetState(input); 41 | UserInputUI.Visible = true; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Devices/Inputs/TeamColorConstant.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | using Terraria.ID; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class TeamColorConstant : Device, IOutput 10 | { 11 | public TeamColorConstant() 12 | { 13 | this.Name = "Team Color Constant"; 14 | 15 | this.Settings.Add("Value", ((int)TeamColor.White).ToString()); 16 | 17 | this.RightClickHelp = "Right Click to toggle value"; 18 | 19 | this.PinLayout = new List 20 | { 21 | new PinDesign("Out", 0, new Point16(0, 1), "teamColor"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) => this.Settings["Value"]; 26 | 27 | public override void OnRightClick(Pin pin = null) 28 | { 29 | if (!int.TryParse(this.Settings["Value"], out var tc)) return; 30 | 31 | this.Settings["Value"] = ((tc + 1) % 6).ToString(); 32 | 33 | if (Main.netMode == NetmodeID.MultiplayerClient) 34 | { 35 | WireMod.PacketHandler.SendChangeSetting(256, Main.myPlayer, this.LocationTile.X, this.LocationTile.Y, "Value", this.Settings["Value"]); 36 | } 37 | } 38 | 39 | public override Rectangle GetSourceRect(int style = -1) 40 | { 41 | if (!int.TryParse(this.Settings["Value"], out var teamcolor)) return default(Rectangle); 42 | 43 | var output = style == -1 ? teamcolor : style + 1; 44 | 45 | return new Rectangle(output * this.Width * 16, 0, this.Width * 16, this.Height * 16); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Devices/Maths/Add.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Add : Device, IOutput 7 | { 8 | public Add() 9 | { 10 | this.Name = "Add"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "int"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 + in1; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Maths/Divide.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Divide : Device, IOutput 7 | { 8 | public Divide() 9 | { 10 | this.Name = "Divide"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "int"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | if (in1 == 0) return 0; // Divide by zero protection 31 | return in0 / in1; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Devices/Maths/Modulo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Modulo : Device, IOutput 7 | { 8 | public Modulo() 9 | { 10 | this.Name = "Modulo"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "int"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | if (in1 == 0) return 0; // Mod by zero protection 31 | return in0 % in1; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Devices/Maths/Multiply.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Multiply : Device, IOutput 7 | { 8 | public Multiply() 9 | { 10 | this.Name = "Multiply"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "int"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 * in1; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Maths/Subtract.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Subtract : Device, IOutput 7 | { 8 | public Subtract() 9 | { 10 | this.Name = "Subtract"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "int"), 18 | new PinDesign("In", 1, new Point16(2, 0), "int"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "int"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 24 | 25 | private int GetOutput() 26 | { 27 | if (!this.GetPinIn(0).IsConnected() || !this.GetPinIn(1).IsConnected()) return -1; 28 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) return -1; 29 | if (!int.TryParse(this.GetPinIn(1).GetValue(), out var in1)) return -1; 30 | return in0 - in1; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Other/Buffer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | 8 | namespace WireMod.Devices 9 | { 10 | internal class Buffer : Device 11 | { 12 | public Buffer() 13 | { 14 | this.Name = "Buffer"; 15 | this.Width = 3; 16 | this.Height = 2; 17 | this.Origin = new Point16(1, 1); 18 | 19 | this.Settings.Add("TargetType", "Players"); 20 | 21 | this.RightClickHelp = $"Right Click to change targetting type ({string.Join("/", TargetTypes)})"; 22 | 23 | this.PinLayout = new List 24 | { 25 | new PinDesign("In", 0, new Point16(1, 0), "int", "BuffID"), 26 | new PinDesign("In", 1, new Point16(0, 1), "int", "Duration"), 27 | new PinDesign("In", 2, new Point16(2, 1), "int", "Distance"), 28 | }; 29 | } 30 | 31 | public override void Update(GameTime gameTime) 32 | { 33 | if (!this.GetPin("BuffID").IsConnected()) return; 34 | if (!int.TryParse(this.GetPin("BuffID").GetValue(), out var buffId)) return; 35 | if (buffId == 0) return; 36 | 37 | if (!int.TryParse(this.GetPin("Distance").GetValue(), out var maxDistance)) return; 38 | 39 | if (!int.TryParse(this.GetPin("Duration").GetValue(), out var duration)) duration = 10; 40 | 41 | duration *= 100; 42 | 43 | var players = Main.player.Where(p => (p.position - this.LocationWorld).Length() < maxDistance).ToList(); 44 | var npcs = Main.npc.Where(p => (p.position - this.LocationWorld).Length() < maxDistance).ToList(); 45 | 46 | switch (this.Settings["TargetType"]) 47 | { 48 | case "Players" when !players.Any(): 49 | return; 50 | case "NPCs" when !npcs.Any(): 51 | return; 52 | case "NPCs": 53 | npcs.ForEach(n => n.AddBuff(buffId, duration)); 54 | break; 55 | case "Players": 56 | players.ForEach(p => p.AddBuff(buffId, duration)); 57 | break; 58 | } 59 | } 60 | 61 | public override void Draw(SpriteBatch spriteBatch) 62 | { 63 | if (!this.GetPin("Distance").IsConnected() || !int.TryParse(this.GetPin("Distance").GetValue(), out var maxDistance)) return; 64 | if (maxDistance == 0) return; 65 | if (!int.TryParse(this.GetPin("BuffID").GetValue(), out var buffId)) return; 66 | if (buffId == 0) return; 67 | 68 | var pixels = 16; 69 | var screenRect = new Rectangle((int)Main.screenPosition.X, (int)Main.screenPosition.Y, Main.screenWidth, Main.screenHeight); 70 | 71 | var deviceWorldRect = new Rectangle((int)this.LocationWorld.X, (int)this.LocationWorld.Y, (int)(this.Width * pixels), (int)(this.Height * pixels)); 72 | if (!deviceWorldRect.Intersects(screenRect)) return; 73 | 74 | var deviceScreenRect = new Rectangle(deviceWorldRect.X - screenRect.X, deviceWorldRect.Y - screenRect.Y, deviceWorldRect.Width, deviceWorldRect.Height); 75 | 76 | var circle = Helpers.CreateCircle(maxDistance * 2); 77 | var pos = new Vector2(deviceScreenRect.X + (deviceScreenRect.Width / 2) - maxDistance, deviceScreenRect.Y + (deviceScreenRect.Height / 2) - maxDistance) - Helpers.Drift; 78 | 79 | spriteBatch.Draw(circle, pos, Color.Red * 0.25f); 80 | } 81 | 82 | public override void OnRightClick(Pin pin = null) 83 | { 84 | this.Settings["TargetType"] = TargetTypes[(TargetTypes.IndexOf(this.Settings["TargetType"]) + 1) % TargetTypes.Count]; 85 | } 86 | 87 | private static readonly List TargetTypes = new List 88 | { 89 | "Players", 90 | "NPCs", 91 | }; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Devices/Other/ProtectArea.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | internal class ProtectArea : Device 9 | { 10 | public ProtectArea() 11 | { 12 | this.Name = "Protect Area"; 13 | this.Width = 3; 14 | this.Height = 2; 15 | this.Origin = new Point16(1, 1); 16 | 17 | this.PinLayout = new List 18 | { 19 | new PinDesign("In", 0, new Point16(1, 0), "area", "Area"), 20 | new PinDesign("In", 1, new Point16(0, 1), "bool", "ProtectPlace"), 21 | new PinDesign("In", 2, new Point16(2, 1), "bool", "ProtectDestroy"), 22 | }; 23 | } 24 | 25 | public Area GetProtectPlaceArea() 26 | { 27 | if (!this.GetPin("ProtectPlace").IsConnected() || !this.GetPin("ProtectPlace").GetBool()) return null; 28 | 29 | return this.GetArea(); 30 | } 31 | 32 | public Area GetProtectDestroyArea() 33 | { 34 | if (!this.GetPin("ProtectDestroy").IsConnected() || !this.GetPin("ProtectDestroy").GetBool()) return null; 35 | 36 | return this.GetArea(); 37 | } 38 | 39 | private Area GetArea() 40 | { 41 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 42 | if (!(area is RectArea rectArea)) return null; 43 | 44 | return rectArea.GetTileArea(); 45 | } 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Devices/Other/Repulsor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.Xna.Framework; 5 | using Microsoft.Xna.Framework.Graphics; 6 | using Terraria; 7 | using Terraria.DataStructures; 8 | 9 | namespace WireMod.Devices 10 | { 11 | internal class Repulsor : Device 12 | { 13 | public Repulsor() 14 | { 15 | this.Name = "Repulsor"; 16 | this.Width = 3; 17 | this.Height = 2; 18 | this.Origin = new Point16(1, 1); 19 | 20 | this.Settings.Add("TargetType", TargetTypes.First()); 21 | 22 | this.RightClickHelp = $"Right Click to change targetting type ({string.Join("/", TargetTypes)})"; 23 | 24 | this.PinLayout = new List 25 | { 26 | new PinDesign("In", 0, new Point16(1, 0), "bool", "Armed"), 27 | new PinDesign("In", 1, new Point16(0, 1), "int", "Velocity"), 28 | new PinDesign("In", 2, new Point16(2, 1), "int", "Distance"), 29 | }; 30 | } 31 | 32 | public override void Draw(SpriteBatch spriteBatch) 33 | { 34 | if (!this.GetPin("Distance").IsConnected() || !int.TryParse(this.GetPin("Distance").GetValue(), out var maxDistance)) return; 35 | if (maxDistance == 0) return; 36 | if (!int.TryParse(this.GetPin("Armed").GetValue(), out var armed)) return; 37 | if (armed == 0) return; 38 | 39 | var deviceScreenRect = this.LocationScreenRect; 40 | if (deviceScreenRect == default(Rectangle)) return; 41 | 42 | var circle = Helpers.CreateCircle(maxDistance * 2); 43 | var pos = this.LocationWorld + this.Origin.ToWorldCoordinates() - Helpers.Drift; 44 | 45 | spriteBatch.Draw(circle, pos, Color.LightBlue * 0.25f); 46 | } 47 | 48 | public override void Update(GameTime gameTime) 49 | { 50 | if (!this.GetPin("Armed").IsConnected()) return; 51 | if (!int.TryParse(this.Pins["In"][0].GetValue(), out var armed)) return; 52 | 53 | if (armed == 0) return; 54 | 55 | if (!this.GetPin("Velocity").IsConnected() || !int.TryParse(this.GetPin("Velocity").GetValue(), out var maxVelocity)) return; 56 | if (!this.GetPin("Distance").IsConnected() || !int.TryParse(this.GetPin("Distance").GetValue(), out var maxDistance)) return; 57 | 58 | var entities = new List(); 59 | 60 | if (this.Settings["TargetType"] == "All" || this.Settings["TargetType"] == "Players") 61 | { 62 | entities.AddRange(Main.player.Select(p => new { player = p, distance = (this.LocationOriginWorld - p.position).Length() }).OrderBy(p => p.distance).Select(p => p.player)); 63 | } 64 | 65 | if (this.Settings["TargetType"] == "All" || this.Settings["TargetType"] == "NPCs") 66 | { 67 | entities.AddRange(Main.npc.Select(p => new { player = p, distance = (this.LocationOriginWorld - p.position).Length() }).OrderBy(p => p.distance).Select(p => p.player)); 68 | } 69 | 70 | if (!entities.Any()) return; 71 | 72 | foreach (var entity in entities) 73 | { 74 | var direction = this.LocationOriginWorld - entity.position; 75 | 76 | var distance = direction.Length(); 77 | 78 | if (distance > maxDistance) return; 79 | 80 | var power = Math.Max(1, maxDistance / Math.Abs(distance)); 81 | 82 | var left = direction.X < 0; 83 | var up = direction.Y < 0; 84 | 85 | var x = left 86 | ? Math.Max(maxVelocity * -1, direction.X * power) 87 | : Math.Min(maxVelocity, direction.X * power); 88 | 89 | var y = up 90 | ? Math.Max(maxVelocity * -1, direction.Y * power) 91 | : Math.Min(maxVelocity, direction.Y * power); 92 | 93 | entity.velocity.X = Math.Max(Math.Min(entity.velocity.X + x, maxVelocity), maxVelocity * -1); 94 | entity.velocity.Y = Math.Max(Math.Min(entity.velocity.Y + y, maxVelocity), maxVelocity * -1); 95 | } 96 | } 97 | 98 | public override void OnRightClick(Pin pin = null) 99 | { 100 | this.Settings["TargetType"] = TargetTypes[(TargetTypes.IndexOf(this.Settings["TargetType"]) + 1) % TargetTypes.Count]; 101 | } 102 | 103 | private static readonly List TargetTypes = new List 104 | { 105 | "All", 106 | "Players", 107 | "NPCs", 108 | }; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Devices/Other/Spawner.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | internal class Spawner : Device, ITriggered 9 | { 10 | /// 11 | /// List of disallowed NPCs 12 | /// 13 | private readonly List _blacklist = new List 14 | { 15 | 142, // Santa Claus 16 | }; 17 | 18 | public Spawner() 19 | { 20 | this.Name = "Spawner"; 21 | this.Width = 2; 22 | this.Height = 2; 23 | this.Origin = new Point16(1, 1); 24 | 25 | this.PinLayout = new List 26 | { 27 | new PinDesign("In", 0, new Point16(1, 0), "bool", "Trigger"), 28 | new PinDesign("In", 1, new Point16(0, 1), "int", "NPCID"), 29 | }; 30 | } 31 | 32 | public void Trigger(Pin pin = null) 33 | { 34 | if (!int.TryParse(this.GetPin("NPCID").GetValue(), out var id)) return; 35 | 36 | if (this._blacklist.Contains(id)) 37 | { 38 | Main.NewText($"NPC ID {id} is blacklisted"); 39 | return; 40 | } 41 | 42 | // TODO: Figure out what the 'Start' argument actually does 43 | var npcId = NPC.NewNPC((int)this.LocationOriginWorld.X, (int)this.LocationOriginWorld.Y, id, 1); 44 | var npc = Main.npc[npcId]; 45 | 46 | // Do something with NPC? 47 | } 48 | 49 | public override void OnRightClick(Pin pin = null) 50 | { 51 | this.Trigger(); 52 | } 53 | 54 | public override List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 55 | { 56 | var debug = base.Debug(pin); 57 | 58 | if (pin == null) 59 | { 60 | debug.Add(("----------------", Color.Black, WireMod.SmallText)); 61 | debug.Add(("Right Click to spawn NPC", Color.Red, WireMod.SmallText)); 62 | } 63 | 64 | return debug; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Devices/Outputs/FlipFlop.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria; 5 | using Terraria.DataStructures; 6 | using Terraria.ID; 7 | 8 | namespace WireMod.Devices 9 | { 10 | internal class FlipFlop : Device, IOutput 11 | { 12 | public FlipFlop() 13 | { 14 | this.Name = "Flip Flop"; 15 | this.Width = 1; 16 | this.Height = 3; 17 | this.Origin = new Point16(0, 1); 18 | 19 | this.Settings.Add("Value", "-1"); 20 | this.Settings.Add("Trigger", Triggers.Last()); 21 | 22 | this.RightClickHelp = $"Right Click to change Trigger type ({string.Join("/", Triggers)})"; 23 | 24 | this.PinLayout = new List 25 | { 26 | new PinDesign("In", 0, new Point16(0, 0), "bool"), 27 | new PinDesign("Out", 0, new Point16(0, 2), "bool"), 28 | }; 29 | } 30 | 31 | public override void Update(GameTime gameTime) 32 | { 33 | if (!this.GetPinIn(0).IsConnected()) 34 | { 35 | this.Settings["Value"] = "-1"; 36 | return; 37 | }; 38 | 39 | if (!int.TryParse(this.Settings["Value"], out var val) || !int.TryParse(this.GetPinIn(0).GetValue(), out var in0)) 40 | { 41 | this.Settings["Value"] = "-2"; 42 | return; 43 | } 44 | 45 | if ((in0 <= 0 && val > 0) || (in0 > 0 && val <= 0)) 46 | { 47 | if (this.Settings["Trigger"] == Triggers[2] || // Enter and Exit 48 | (in0 == 1 && this.Settings["Trigger"] == Triggers[1]) || // Enter Only 49 | (in0 == 0 && this.Settings["Trigger"] == Triggers[0])) // Exit Only 50 | { 51 | Wiring.blockPlayerTeleportationForOneIteration = true; 52 | 53 | if (Main.netMode != NetmodeID.MultiplayerClient) 54 | { 55 | Wiring.TripWire(this.GetPinOut(0).Location.X, this.GetPinOut(0).Location.Y, 1, 1); 56 | } 57 | else 58 | { 59 | Wiring.TripWire(this.GetPinOut(0).Location.X, this.GetPinOut(0).Location.Y, 1, 1); 60 | WireMod.PacketHandler.SendTripWire(256, Main.myPlayer, this.GetPinOut(0).Location.X, this.GetPinOut(0).Location.Y); 61 | } 62 | } 63 | } 64 | 65 | this.Settings["Value"] = in0.ToString(); 66 | } 67 | 68 | public string Output(Pin pin = null) => this.Settings["Value"]; 69 | 70 | public override void OnRightClick(Pin pin = null) 71 | { 72 | this.Settings["Trigger"] = Triggers[(Triggers.IndexOf(this.Settings["Trigger"]) + 1) % Triggers.Count]; 73 | } 74 | 75 | private static readonly List Triggers = new List 76 | { 77 | "Exit Only", 78 | "Enter Only", 79 | "Enter and Exit" 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Devices/Outputs/OutputLamp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria.DataStructures; 4 | 5 | namespace WireMod.Devices 6 | { 7 | internal class OutputLamp : Device 8 | { 9 | public OutputLamp() 10 | { 11 | this.Name = "Output Lamp"; 12 | this.Width = 1; 13 | this.Height = 2; 14 | this.Origin = new Point16(0, 1); 15 | 16 | this.PinLayout = new List 17 | { 18 | new PinDesign("In", 0, new Point16(0, 0), "bool"), 19 | }; 20 | } 21 | 22 | public override Rectangle GetSourceRect(int style = -1) 23 | { 24 | if (style == -1) 25 | { 26 | if (!int.TryParse(this.GetPinIn(0).GetValue(), out var input)) return base.GetSourceRect(style); 27 | style = input + 1; 28 | } 29 | 30 | return new Rectangle(style * (this.Width * 16), 0, this.Width * 16, this.Height * 16); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Devices/Outputs/Trigger.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria; 5 | using Terraria.DataStructures; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class Trigger : Device, IOutput 10 | { 11 | private bool _reset; 12 | 13 | public Trigger() 14 | { 15 | this.Name = "Trigger"; 16 | this.Width = 2; 17 | this.Height = 3; 18 | this.Origin = new Point16(1, 1); 19 | 20 | this.Settings.Add("Value", "-1"); 21 | this.Settings.Add("TriggerType", "All"); 22 | this.Settings.Add("TriggerTarget", "0"); 23 | 24 | this.RightClickHelp = "Right Click to toggle trigger type"; 25 | 26 | this.PinLayout = new List 27 | { 28 | new PinDesign("In", 0, new Point16(1, 0), "bool", "Trigger"), 29 | new PinDesign("In", 1, new Point16(0, 1), "bool", "Reset"), 30 | new PinDesign("Out", 0, new Point16(1, 2), "bool"), 31 | }; 32 | } 33 | 34 | public string Output(Pin pin = null) => this.Settings["Value"]; 35 | 36 | public override void OnHitWire(Pin pin = null) 37 | { 38 | this.TriggerDevices(); 39 | } 40 | 41 | public override void Update(GameTime gameTime) 42 | { 43 | if (!int.TryParse(this.Settings["Value"], out var val)) return; 44 | if (!this.Pins["In"][0].IsConnected()) return; 45 | if (!int.TryParse(this.Pins["In"][0].GetValue(), out var trigger)) return; 46 | 47 | if (this.GetPin("Reset").IsConnected() && int.TryParse(this.GetPin("Reset").GetValue(), out var reset)) 48 | { 49 | if (reset == 1) this._reset = true; 50 | } 51 | 52 | if (trigger > 0 && val <= 0 && this._reset) 53 | { 54 | this._reset = false; 55 | 56 | if (!this.GetPinOut(0).IsConnected()) 57 | { 58 | this.Settings["Value"] = trigger.ToString(); 59 | return; 60 | } 61 | 62 | this.TriggerDevices(); 63 | 64 | this.Settings["Value"] = trigger.ToString(); 65 | return; 66 | } 67 | 68 | if (trigger <= 0 && val > 0 && !this.GetPinIn(1).IsConnected()) 69 | { 70 | this._reset = true; 71 | } 72 | 73 | this.Settings["Value"] = trigger.ToString(); 74 | } 75 | 76 | private void TriggerDevices() 77 | { 78 | var pins = this.GetPinOut(0).ConnectedPins.Where(p => p.Device is ITriggered).ToList(); 79 | if (!int.TryParse(this.Settings["TriggerTarget"], out var target)) return; 80 | if (target >= pins.Count) target = pins.Count - 1; 81 | 82 | // Trigger connected devices 83 | if (this.Settings["TriggerType"] == "All") 84 | { 85 | pins.ForEach(pin => 86 | { 87 | ((ITriggered)pin.Device).Trigger(pin); 88 | }); 89 | this.Settings["TriggerTarget"] = "0"; 90 | } 91 | else 92 | { 93 | var pin = this.Settings["TriggerType"] == "Sequential" ? pins[(target + 1) % pins.Count] : pins[WorldGen.genRand.Next(0, pins.Count)]; 94 | ((ITriggered)pin.Device).Trigger(pin); 95 | this.Settings["TriggerTarget"] = pins.IndexOf(pin).ToString(); 96 | } 97 | } 98 | 99 | public override List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 100 | { 101 | var debug = base.Debug(pin); 102 | 103 | debug.Add(($"Reset: {(this._reset ? "True" : "False")}", Color.Black, WireMod.SmallText)); 104 | 105 | return debug; 106 | } 107 | 108 | public override void OnRightClick(Pin pin = null) 109 | { 110 | this.Settings["TriggerType"] = TriggerTypes[(TriggerTypes.IndexOf(this.Settings["TriggerType"]) + 1) % TriggerTypes.Count]; 111 | } 112 | 113 | private static readonly List TriggerTypes = new List 114 | { 115 | "All", 116 | "Sequential", 117 | "Random", 118 | }; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Devices/Pin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | public abstract class Pin 9 | { 10 | public Device Device { get; set; } 11 | public int Num { get; set; } 12 | public string Type { get; set; } 13 | public string DataType { get; set; } = "bool"; 14 | public bool Auto { get; set; } 15 | 16 | public Point16 Location { get; set; } 17 | 18 | public List Wires { get; set; } = new List(); 19 | 20 | private string _name = ""; 21 | public string Name 22 | { 23 | get => !string.IsNullOrEmpty(this._name) ? this._name : $"Pin{this.Type}{this.Num}"; 24 | set => this._name = value; 25 | } 26 | 27 | public abstract bool IsConnected(Pin pin = null); 28 | 29 | public abstract void Connect(Pin pin, Wire wire); 30 | public abstract void Disconnect(Pin pin = null); 31 | 32 | public abstract string GetValue(); 33 | 34 | public bool GetBool() 35 | { 36 | var value = this.GetValue(); 37 | if (!int.TryParse(value, out var b)) throw new InvalidOperationException($"Could not parse \"{value}\" as a boolean"); 38 | return b == 1; 39 | } 40 | 41 | public int GetInt() 42 | { 43 | var value = this.GetValue(); 44 | if (!int.TryParse(value, out var i)) throw new InvalidOperationException($"Could not parse \"{value}\" as an integer"); 45 | return i; 46 | } 47 | 48 | public abstract List GetDebug(); 49 | 50 | public Wire GetWire(Pin connectedPin) => this.Wires.FirstOrDefault(w => w.StartPin == this && w.EndPin == connectedPin || w.StartPin == connectedPin && w.EndPin == this); 51 | } 52 | 53 | public class PinIn : Pin 54 | { 55 | public Pin ConnectedPin { get; set; } 56 | private string _value => this.ConnectedPin?.GetValue(); 57 | 58 | public PinIn(Device device) 59 | { 60 | this.Device = device; 61 | this.Type = "In"; 62 | } 63 | 64 | public override bool IsConnected(Pin pin = null) => this.ConnectedPin != null; 65 | 66 | public override void Connect(Pin pin, Wire wire) 67 | { 68 | if (this.IsConnected()) this.ConnectedPin.Disconnect(this); 69 | this.ConnectedPin = pin; 70 | this.Wires.Add(wire); 71 | 72 | this.Device.SetAutoType(); 73 | } 74 | 75 | public override void Disconnect(Pin pin = null) 76 | { 77 | if (!this.IsConnected()) return; 78 | 79 | this.Wires.RemoveAll(w => w.StartPin == this.ConnectedPin || w.EndPin == this.ConnectedPin); 80 | 81 | this.ConnectedPin.Disconnect(this); 82 | this.ConnectedPin = null; 83 | 84 | this.Device.SetAutoType(); 85 | } 86 | 87 | public override string GetValue() => this._value; 88 | 89 | public override List GetDebug() 90 | { 91 | return this.IsConnected() 92 | ? new List { $"{this.Name} ({this.DataType}) [{(this.DataType == "bool" ? (this.GetValue() == "1" ? "True" : (this.GetValue() == "0" ? "False" : "Disconnected")) : this.GetValue())}] => {this.ConnectedPin.Device.Name} {this.ConnectedPin.Name}" } 93 | : new List { $"{this.Name} ({this.DataType}) (Disconnected)" }; 94 | } 95 | } 96 | 97 | public class PinOut : Pin 98 | { 99 | public List ConnectedPins { get; set; } = new List(); 100 | private string _value => (this.Device is IOutput ? ((IOutput)this.Device).Output(this) : ""); 101 | 102 | public PinOut(Device device) 103 | { 104 | this.Device = device; 105 | this.Type = "Out"; 106 | } 107 | 108 | public override bool IsConnected(Pin pin = null) 109 | { 110 | if (pin == null) 111 | { 112 | return this.ConnectedPins.Count > 0; 113 | } 114 | 115 | return this.ConnectedPins.Contains(pin); 116 | } 117 | 118 | public override void Connect(Pin pin, Wire wire) 119 | { 120 | this.ConnectedPins.Add(pin); 121 | this.Wires.Add(wire); 122 | 123 | this.Device.SetAutoType(); 124 | } 125 | 126 | public override void Disconnect(Pin pin = null) 127 | { 128 | if (!this.IsConnected()) return; 129 | 130 | if (pin != null) 131 | { 132 | if (this.ConnectedPins.Contains(pin)) 133 | { 134 | this.Wires.RemoveAll(w => w.StartPin == pin || w.EndPin == pin); 135 | this.ConnectedPins.Remove(pin); 136 | this.Device.SetAutoType(); 137 | } 138 | return; 139 | } 140 | 141 | var pins = this.ConnectedPins.Where(p => p.IsConnected()).ToList(); 142 | 143 | foreach (var p in pins) 144 | { 145 | this.Wires.RemoveAll(w => w.StartPin == p || w.EndPin == p); 146 | p.Disconnect(); 147 | } 148 | 149 | this.Device.SetAutoType(); 150 | } 151 | 152 | public override string GetValue() => this._value; 153 | 154 | public override List GetDebug() 155 | { 156 | var lines = new List(); 157 | 158 | if (this.IsConnected()) 159 | { 160 | foreach (var pin in this.ConnectedPins) 161 | { 162 | lines.Add($"{this.Name} ({this.DataType}) [{(this.DataType == "bool" ? (this.GetValue() == "1" ? "True" : (this.GetValue() == "0" ? "False" : "Disconnected")) : this.GetValue())}] => {pin.Device.Name} {pin.Name}"); 163 | } 164 | } 165 | else 166 | { 167 | lines.Add($"{this.Name} ({this.DataType}) (Disconnected) [{(this.DataType == "bool" ? (this.GetValue() == "1" ? "True" : (this.GetValue() == "0" ? "False" : "Disconnected")) : this.GetValue())}]"); 168 | } 169 | 170 | return lines; 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Devices/PinDesign.cs: -------------------------------------------------------------------------------- 1 | using Terraria.DataStructures; 2 | 3 | namespace WireMod.Devices 4 | { 5 | public class PinDesign 6 | { 7 | public string Type { get; set; } 8 | public int Num { get; set; } 9 | public Point16 Offset { get; set; } 10 | public string DataType { get; set; } 11 | public string Name { get; set; } 12 | public bool Auto { get; set; } 13 | 14 | public PinDesign(string type, int num, Point16 offset, string dataType, string name = "", bool auto = false) 15 | { 16 | this.Type = type; 17 | this.Num = num; 18 | this.Offset = offset; 19 | this.DataType = dataType; 20 | this.Name = name; 21 | this.Auto = auto; 22 | } 23 | 24 | public override string ToString() => this.Name != "" ? this.Name : $"Pin{this.Type}{this.Num}"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Devices/Sensors/NPCCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria; 5 | using Terraria.DataStructures; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class NPCCounter : Device, IOutput 10 | { 11 | public NPCCounter() 12 | { 13 | this.Name = "Nearby NPC Counter Sensor"; 14 | this.Width = 3; 15 | this.Height = 3; 16 | this.Origin = new Point16(1, 1); 17 | 18 | this.PinLayout = new List 19 | { 20 | new PinDesign("In", 0, new Point16(1, 0), "area", "Area"), 21 | new PinDesign("In", 1, new Point16(0, 1), "bool", "IsHostile"), 22 | new PinDesign("In", 2, new Point16(2, 1), "bool", "IsTownNPC"), 23 | new PinDesign("Out", 0, new Point16(1, 2), "int", "Count"), 24 | }; 25 | } 26 | 27 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 28 | 29 | private int GetOutput() 30 | { 31 | if (!this.GetPin("Area").IsConnected()) return -1; 32 | 33 | return this.GetNPCs().Count(); 34 | } 35 | 36 | private IEnumerable GetNPCs() 37 | { 38 | if (!this.GetPin("Area").IsConnected()) return new List(); 39 | 40 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 41 | 42 | if (area == null) return new List(); 43 | 44 | var npc = Main.npc.Select(n => n).Where(n => !n.dontCountMe); 45 | 46 | if (this.GetPin("IsHostile").IsConnected() && int.TryParse(this.GetPin("IsHostile").GetValue(), out var hostile)) 47 | { 48 | npc = npc.Where(n => n.friendly == (hostile == 0)); 49 | } 50 | 51 | if (this.GetPin("IsTownNPC").IsConnected() && int.TryParse(this.GetPin("IsTownNPC").GetValue(), out var townNPC)) 52 | { 53 | npc = npc.Where(n => n.townNPC == (townNPC == 1)); 54 | } 55 | 56 | return npc.Where(p => area.Contains(p.position)); 57 | } 58 | 59 | public override List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 60 | { 61 | var debug = base.Debug(pin); 62 | 63 | debug.Add(("----------------", Color.Black, WireMod.SmallText)); 64 | debug.AddRange(this.GetNPCs().Select(npc => ($"NPC: {npc.FullName}", Color.Red, WireMod.SmallText))); 65 | 66 | return debug; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Devices/Sensors/NPCDistanceSensor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | internal class NPCDistanceSensor : Device, IOutput 9 | { 10 | public NPCDistanceSensor() 11 | { 12 | this.Name = "Nearest NPC Distance Sensor"; 13 | this.Width = 3; 14 | this.Height = 3; 15 | this.Origin = new Point16(1, 1); 16 | 17 | this.PinLayout = new List 18 | { 19 | new PinDesign("In", 0, new Point16(0, 1), "bool", "IsHostile"), 20 | new PinDesign("In", 1, new Point16(1, 0), "bool", "IsTownNPC"), 21 | new PinDesign("Out", 0, new Point16(1, 2), "int", "Distance"), 22 | new PinDesign("Out", 1, new Point16(2, 1), "string", "Name"), 23 | }; 24 | } 25 | 26 | public string Output(Pin pin = null) 27 | { 28 | switch (pin?.Name) 29 | { 30 | case "Distance": return this.GetOutputDistance(pin).ToString(); 31 | case "Name": return this.GetOutputName(pin); 32 | default: return ""; 33 | } 34 | } 35 | 36 | private int GetOutputDistance(Pin pin) 37 | { 38 | if (pin == null) return -2; 39 | if (Main.npc.Length == 0) return -1; 40 | 41 | var nearest = this.GetNearestNPC(); 42 | 43 | if (nearest == null) return -1; 44 | 45 | return (int)(this.LocationOriginWorld - nearest.position).Length(); 46 | } 47 | 48 | private string GetOutputName(Pin pin) 49 | { 50 | if (pin == null) return ""; 51 | if (Main.npc.Length == 0) return ""; 52 | 53 | var nearest = this.GetNearestNPC(); 54 | 55 | return nearest?.FullName ?? ""; 56 | } 57 | 58 | private NPC GetNearestNPC() 59 | { 60 | var npc = Main.npc.Select(n => n); 61 | 62 | if (this.GetPin("IsHostile").IsConnected() && int.TryParse(this.GetPin("IsHostile").GetValue(), out var hostile)) 63 | { 64 | npc = npc.Where(n => n.friendly == (hostile == 0)); 65 | } 66 | 67 | if (this.GetPin("IsTownNPC").IsConnected() && int.TryParse(this.GetPin("IsTownNPC").GetValue(), out var townNPC)) 68 | { 69 | npc = npc.Where(n => n.townNPC == (townNPC == 1)); 70 | } 71 | 72 | return npc.OrderBy(n => (this.LocationOriginWorld - n.position).Length()).FirstOrDefault(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Devices/Sensors/PlayerCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria; 5 | using Terraria.DataStructures; 6 | 7 | namespace WireMod.Devices 8 | { 9 | internal class PlayerCounter : Device, IOutput 10 | { 11 | public PlayerCounter() 12 | { 13 | this.Name = "Nearby Player Counter Sensor"; 14 | this.Width = 2; 15 | this.Height = 3; 16 | this.Origin = new Point16(1, 1); 17 | 18 | this.PinLayout = new List 19 | { 20 | new PinDesign("In", 0, new Point16(1, 0), "area", "Area"), 21 | new PinDesign("In", 1, new Point16(0, 1), "teamColor", "TeamColorFilter"), 22 | new PinDesign("Out", 0, new Point16(1, 2), "int", "Count"), 23 | }; 24 | } 25 | 26 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 27 | 28 | private int GetOutput() 29 | { 30 | if (!this.GetPin("Area").IsConnected()) return -1; 31 | 32 | return this.GetPlayers().Count(); 33 | } 34 | 35 | private IEnumerable GetPlayers() 36 | { 37 | if (!this.GetPin("Area").IsConnected()) return new List(); 38 | 39 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 40 | if (area == null) return new List(); 41 | 42 | var players = Main.player.Select(p => p); 43 | 44 | if (this.GetPin("TeamColorFilter").IsConnected()) 45 | { 46 | var team = TeamColor.White; 47 | if (int.TryParse(this.GetPin("TeamColorFilter").GetValue(), out var tc)) 48 | { 49 | team = (TeamColor)tc; 50 | } 51 | players = players.Where(p => p.team == (int)team); 52 | } 53 | 54 | return players.Where(p => area.Contains(p.position)); 55 | } 56 | 57 | public override List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 58 | { 59 | var debug = base.Debug(pin); 60 | 61 | debug.Add(("----------------", Color.Black, WireMod.SmallText)); 62 | debug.AddRange(this.GetPlayers().Select(player => ($"Player: {player.name}", Color.Red, WireMod.SmallText))); 63 | 64 | return debug; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Devices/Sensors/PlayerDistanceSensor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | internal class PlayerDistanceSensor : Device, IOutput 9 | { 10 | public PlayerDistanceSensor() 11 | { 12 | this.Name = "Nearest Player Distance Sensor"; 13 | this.Width = 2; 14 | this.Height = 3; 15 | this.Origin = new Point16(0, 1); 16 | 17 | this.PinLayout = new List 18 | { 19 | new PinDesign("In", 0, new Point16(0, 0), "teamColor", "TeamColorFilter"), 20 | new PinDesign("Out", 0, new Point16(0, 2), "int", "Distance"), 21 | new PinDesign("Out", 1, new Point16(1, 1), "string", "Name"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) 26 | { 27 | switch (pin?.Name) 28 | { 29 | case "Distance": return this.GetOutputDistance(pin).ToString(); 30 | case "Name": return this.GetOutputName(pin); 31 | default: return ""; 32 | } 33 | } 34 | 35 | private int GetOutputDistance(Pin pin) 36 | { 37 | if (pin == null) return -2; 38 | if (Main.PlayerList.Count == 0) return -1; 39 | 40 | var nearest = this.GetNearestPlayer(); 41 | 42 | if (nearest == null) return -1; 43 | 44 | return (int)(this.LocationOriginWorld - nearest.position).Length(); 45 | } 46 | 47 | private string GetOutputName(Pin pin) 48 | { 49 | if (pin == null) return ""; 50 | if (Main.PlayerList.Count == 0) return ""; 51 | 52 | var nearest = this.GetNearestPlayer(); 53 | 54 | return nearest?.name ?? ""; 55 | } 56 | 57 | private Player GetNearestPlayer() 58 | { 59 | var player = Main.player.OrderBy(p => (this.LocationOriginWorld - p.position).Length()); 60 | 61 | if (!this.GetPin("TeamColorFilter").IsConnected()) return player.FirstOrDefault(); 62 | 63 | var team = TeamColor.White; 64 | if (int.TryParse(this.GetPin("TeamColorFilter").GetValue(), out var tc)) 65 | { 66 | team = (TeamColor)tc; 67 | } 68 | 69 | return player.FirstOrDefault(p => p.team == (int)team); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Devices/Sensors/TileCounter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod.Devices 7 | { 8 | internal class TileCounter : Device, IOutput 9 | { 10 | public TileCounter() 11 | { 12 | this.Name = "Nearby Tile Counter Sensor"; 13 | this.Width = 2; 14 | this.Height = 3; 15 | this.Origin = new Point16(1, 1); 16 | 17 | this.PinLayout = new List 18 | { 19 | new PinDesign("In", 0, new Point16(1, 0), "area", "Area"), 20 | new PinDesign("In", 1, new Point16(0, 1), "int", "TileID"), 21 | new PinDesign("Out", 0, new Point16(1, 2), "int", "Count"), 22 | }; 23 | } 24 | 25 | public string Output(Pin pin = null) => this.GetOutput().ToString(); 26 | 27 | private int GetOutput() 28 | { 29 | if (!this.GetPin("Area").IsConnected()) return -1; 30 | 31 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 32 | if (!(area is RectArea)) return -2; 33 | 34 | if (this.GetPin("TileID").IsConnected() && !int.TryParse(this.GetPin("TileID").GetValue(), out var _)) return -2; 35 | 36 | return this.GetTiles().Count(); 37 | } 38 | 39 | private IEnumerable GetTiles() 40 | { 41 | var tiles = new List(); 42 | 43 | if (!this.GetPin("TileID").IsConnected() || !int.TryParse(this.GetPin("TileID").GetValue(), out var id)) id = -1; 44 | 45 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 46 | if (!(area is RectArea rectArea)) return tiles; 47 | 48 | var areaRect = rectArea.GetTileArea().GetRect(); 49 | 50 | for (var y = 0; y < areaRect.Height; y++) 51 | { 52 | for (var x = 0; x < areaRect.Width; x++) 53 | { 54 | var tile = Main.tile[areaRect.X + x, areaRect.Y + y]; 55 | if (!tile.active() || (id > -1 && tile.type != id)) continue; 56 | tiles.Add(tile); 57 | } 58 | } 59 | 60 | return tiles; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Devices/Sensors/TimeSensor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria; 3 | using Terraria.DataStructures; 4 | 5 | namespace WireMod.Devices 6 | { 7 | internal class TimeSensor : Device, IOutput 8 | { 9 | public TimeSensor() 10 | { 11 | this.Name = "Time Sensor"; 12 | this.Width = 3; 13 | this.Height = 2; 14 | this.Origin = new Point16(1, 0); 15 | 16 | this.PinLayout = new List 17 | { 18 | new PinDesign("Out", 0, new Point16(0, 0), "int", "Hours"), 19 | new PinDesign("Out", 1, new Point16(1, 1), "int", "Minutes"), 20 | new PinDesign("Out", 2, new Point16(2, 0), "int", "Seconds"), 21 | }; 22 | } 23 | 24 | public string Output(Pin pin = null) 25 | { 26 | var time = Main.time; 27 | 28 | var hours = (int)(((((time / 60) + 30) / 60) + 19) % 24); 29 | if (Main.dayTime) hours = (hours + 9) % 24; 30 | var minutes = (int)((((time % 3600) / 60) + 30) % 60); 31 | var seconds = (int)(time % 60); 32 | 33 | if (pin != null) 34 | { 35 | switch (pin.Num) 36 | { 37 | case 0: 38 | return hours.ToString(); 39 | case 1: 40 | return minutes.ToString(); 41 | case 2: 42 | return seconds.ToString(); 43 | } 44 | } 45 | 46 | return $"{hours}:{minutes}:{seconds}"; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Devices/Storage/TileCopier.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Terraria; 5 | using Terraria.DataStructures; 6 | 7 | namespace WireMod.Devices 8 | { 9 | public class TileCopier : Device, IOutput 10 | { 11 | public TileCopier() 12 | { 13 | this.Name = "Tile Copier"; 14 | this.Width = 2; 15 | this.Height = 3; 16 | this.Origin = new Point16(1, 1); 17 | 18 | this.Settings.Add("Value", ""); 19 | 20 | this.PinLayout = new List 21 | { 22 | new PinDesign("In", 0, new Point16(1, 0), "area", "Area"), 23 | new PinDesign("In", 1, new Point16(0, 1), "bool", "Copy"), 24 | new PinDesign("Out", 0, new Point16(1, 2), "tile[]"), 25 | }; 26 | } 27 | 28 | public override void Update(GameTime gameTime) 29 | { 30 | if (!this.GetPin("Copy").IsConnected() || this.GetPin("Copy").GetValue() != "1") return; 31 | 32 | if (!this.GetPin("Area").IsConnected()) 33 | { 34 | this.Settings["Value"] = ""; 35 | return; 36 | } 37 | 38 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 39 | if (!(area is RectArea)) return; 40 | 41 | this.Settings["Value"] = string.Join(";", this.GetTiles().Select(t => t.ToString())); 42 | } 43 | 44 | public string Output(Pin pin = null) => this.Settings["Value"]; 45 | 46 | private IEnumerable GetTiles() 47 | { 48 | var tiles = new List(); 49 | 50 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 51 | if (!(area is RectArea rectArea)) return tiles; 52 | 53 | var areaRect = rectArea.GetTileArea().GetRect(); 54 | 55 | for (var y = areaRect.Y; y < areaRect.Y + areaRect.Height; y++) 56 | { 57 | for (var x = areaRect.X; x < areaRect.X + areaRect.Width; x++) 58 | { 59 | tiles.Add(new TileInfo(x, y)); 60 | } 61 | } 62 | 63 | return tiles; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Devices/Storage/TileInfo.cs: -------------------------------------------------------------------------------- 1 | namespace WireMod.Devices 2 | { 3 | public class TileInfo 4 | { 5 | public int X { get; set; } 6 | public int Y { get; set; } 7 | 8 | public TileInfo() 9 | { 10 | } 11 | 12 | public TileInfo(int x, int y) 13 | { 14 | this.X = x; 15 | this.Y = y; 16 | } 17 | 18 | public TileInfo(string data) 19 | { 20 | this.Load(data); 21 | } 22 | 23 | public void Load(string data) 24 | { 25 | var fields = data.Split(','); 26 | 27 | if (!int.TryParse(fields[0], out var x)) return; 28 | if (!int.TryParse(fields[1], out var y)) return; 29 | 30 | this.X = x; 31 | this.Y = y; 32 | } 33 | 34 | public override string ToString() => $"{this.X},{this.Y}"; 35 | } 36 | } -------------------------------------------------------------------------------- /Devices/Storage/TilePaster.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.Xna.Framework; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | using Terraria.ID; 8 | using Terraria.ModLoader; 9 | 10 | namespace WireMod.Devices 11 | { 12 | public class TilePaster : Device, ITriggered 13 | { 14 | public TilePaster() 15 | { 16 | this.Name = "Tile Paster"; 17 | this.Width = 2; 18 | this.Height = 3; 19 | this.Origin = new Point16(1, 1); 20 | 21 | this.PinLayout = new List 22 | { 23 | new PinDesign("In", 0, new Point16(1, 0), "tile[]", "Data"), 24 | new PinDesign("In", 1, new Point16(0, 1), "bool", "Trigger"), 25 | new PinDesign("In", 2, new Point16(1, 2), "area", "Area") 26 | }; 27 | } 28 | 29 | public void Trigger(Pin pin = null) 30 | { 31 | if (!this.GetPin("Data").IsConnected() || !this.GetPin("Area").IsConnected()) return; 32 | 33 | var area = AreaFactory.Create(this.GetPin("Area").GetValue()); 34 | if (!(area is RectArea rectArea)) return; 35 | 36 | var data = this.GetPin("Data").GetValue().Split(';'); 37 | 38 | var srcTiles = data.Select(d => new TileInfo(d)).ToList(); 39 | var srcRect = new Rectangle(srcTiles.Min(t => t.X), srcTiles.Min(t => t.Y), (srcTiles.Max(t => t.X) - srcTiles.Min(t => t.X)) + 1, (srcTiles.Max(t => t.Y) - srcTiles.Min(t => t.Y)) + 1); 40 | 41 | var destRect = rectArea.GetTileArea().GetRect(); 42 | 43 | for (var y = 0; y < Math.Min(srcRect.Height, destRect.Height); y++) 44 | { 45 | for (var x = 0; x < Math.Min(srcRect.Width, destRect.Width); x++) 46 | { 47 | var srcTile = Main.tile[srcRect.X + x, srcRect.Y + y]; 48 | var destTile = Main.tile[destRect.X + x, destRect.Y + y]; 49 | 50 | if (Constants.CopyTileBlacklist.Contains(srcTile.type)) continue; 51 | 52 | var (wire, wire2, wire3, wire4) = (destTile.wire(), destTile.wire2(), destTile.wire3(), destTile.wire4()); 53 | 54 | destTile.CopyFrom(srcTile); 55 | 56 | // Preserve tile wires 57 | destTile.wire(wire); 58 | destTile.wire2(wire2); 59 | destTile.wire3(wire3); 60 | destTile.wire4(wire4); 61 | 62 | WorldGen.SquareTileFrame(destRect.X + x, destRect.Y + y); 63 | if (Main.netMode == NetmodeID.Server) 64 | { 65 | NetMessage.SendTileSquare(-1, destRect.X + x, destRect.Y + y, 1); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Devices/Storage/Variable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria.DataStructures; 4 | 5 | namespace WireMod.Devices 6 | { 7 | internal class Variable : Device, IOutput 8 | { 9 | public Variable() 10 | { 11 | this.Name = "Variable"; 12 | 13 | this.Width = 2; 14 | this.Height = 3; 15 | this.Origin = new Point16(1, 0); 16 | 17 | this.AutoTypes.AddRange(new [] {"int", "bool", "string"}); 18 | 19 | this.Settings.Add("Value", "0"); 20 | 21 | this.PinLayout = new List 22 | { 23 | new PinDesign("In", 0, new Point16(1, 0), "int", "", true), 24 | new PinDesign("In", 1, new Point16(0, 1), "bool", "Write"), 25 | new PinDesign("Out", 0, new Point16(1, 2), "int", "", true), 26 | }; 27 | } 28 | 29 | public override void Update(GameTime gameTime) 30 | { 31 | if (!this.GetPin("Write").IsConnected() || !int.TryParse(this.GetPin("Write").GetValue(), out var write)) write = 0; 32 | 33 | if (this.GetPinIn(0).IsConnected() && write == 1) 34 | { 35 | this.Settings["Value"] = this.GetPinIn(0).GetValue(); 36 | } 37 | } 38 | 39 | public string Output(Pin pin = null) => this.Settings["Value"]; 40 | } 41 | } -------------------------------------------------------------------------------- /Devices/Strings/Announcer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | using Terraria.ID; 6 | using Terraria.Localization; 7 | 8 | namespace WireMod.Devices 9 | { 10 | internal class Announcer : Device, ITriggered 11 | { 12 | private readonly Color _textColor = new Color(255, 255, 20); 13 | 14 | public Announcer() 15 | { 16 | this.Name = "Announcer"; 17 | this.Width = 3; 18 | this.Height = 2; 19 | this.Origin = new Point16(1, 1); 20 | 21 | this.PinLayout = new List 22 | { 23 | new PinDesign("In", 0, new Point16(1, 0), "string"), 24 | new PinDesign("In", 1, new Point16(0, 1), "bool", "Trigger"), 25 | new PinDesign("In", 2, new Point16(2, 1), "teamColor", "TeamColorFilter"), 26 | }; 27 | } 28 | 29 | public void Trigger(Pin pin = null) 30 | { 31 | if (!this.GetPinIn(0).IsConnected()) return; 32 | 33 | var message = this.GetPinIn(0).GetValue(); 34 | 35 | switch (Main.netMode) 36 | { 37 | case NetmodeID.SinglePlayer: 38 | Main.NewText(message, this._textColor); 39 | break; 40 | case NetmodeID.Server: 41 | NetMessage.BroadcastChatMessage(NetworkText.FromLiteral(message), this._textColor); 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Devices/Strings/Concat.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | internal class Concat : Device, IOutput 7 | { 8 | public Concat() 9 | { 10 | this.Name = "Concat"; 11 | this.Width = 3; 12 | this.Height = 2; 13 | this.Origin = new Point16(1, 0); 14 | 15 | this.PinLayout = new List 16 | { 17 | new PinDesign("In", 0, new Point16(0, 0), "string"), 18 | new PinDesign("In", 1, new Point16(2, 0), "string"), 19 | new PinDesign("Out", 0, new Point16(1, 1), "string"), 20 | }; 21 | } 22 | 23 | public string Output(Pin pin = null) => this.GetPinIn(0).GetValue() + this.GetPinIn(1).GetValue(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Devices/Strings/TextTileController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Microsoft.Xna.Framework; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | using WireMod.Tiles; 8 | 9 | namespace WireMod.Devices 10 | { 11 | internal class TextTileController : Device 12 | { 13 | public TextTileController() 14 | { 15 | this.Name = "TextTileController"; 16 | this.Width = 2; 17 | this.Height = 1; 18 | this.Origin = new Point16(1, 0); 19 | 20 | this.PinLayout = new List 21 | { 22 | new PinDesign("In", 0, new Point16(0, 0), "string"), 23 | }; 24 | } 25 | 26 | public override void Update(GameTime gameTime) 27 | { 28 | var tiles = this.GetTiles(); 29 | 30 | var input = this.GetPinIn(0).IsConnected() ? this.GetPinIn(0).GetValue() : ""; 31 | 32 | input = input.PadRight(tiles.Count, ' '); 33 | 34 | for (var i = 0; i < Math.Min(input.Length, tiles.Count); i++) 35 | { 36 | var style = GetStyle(input.Substring(i, 1)); 37 | tiles[i].frameX = (short)((style % 16) * 18); 38 | tiles[i].frameY = (short)((style / 16) * 18); 39 | } 40 | } 41 | 42 | private List GetTiles() 43 | { 44 | var tiles = new List(); 45 | 46 | var pos = this.LocationOriginTile; 47 | var x = pos.X; 48 | 49 | for (var i = 0; i < 255; i++) 50 | { 51 | var tile = Main.tile[++x, pos.Y]; 52 | if (tile == null) break; 53 | if (tile.type != WireMod.Instance.TileType()) break; 54 | 55 | tiles.Add(tile); 56 | } 57 | 58 | return tiles; 59 | } 60 | 61 | public override List<(string Line, Color Color, float Size)> Debug(Pin pin = null) 62 | { 63 | var debug = base.Debug(pin); 64 | 65 | debug.Add(("----------------", Color.Black, WireMod.SmallText)); 66 | debug.Add(($"Found {this.GetTiles().Count} tiles", Color.Red, WireMod.SmallText)); 67 | 68 | return debug; 69 | } 70 | 71 | private static int GetStyle(string inputChar) 72 | { 73 | var ccc = Encoding.GetEncoding("ISO-8859-1"); 74 | var by = ccc.GetBytes(inputChar); 75 | return string.Equals(inputChar, ccc.GetString(by)) ? by[0] : 0; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Devices/Wire.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Terraria.DataStructures; 3 | 4 | namespace WireMod.Devices 5 | { 6 | public class Wire 7 | { 8 | public Pin StartPin { get; set; } 9 | public Pin EndPin { get; set; } 10 | public List Points { get; set; } = new List(); 11 | 12 | public Wire(Pin startPin) 13 | { 14 | this.StartPin = startPin; 15 | } 16 | 17 | public Wire(Pin startPin, Pin endPin, List points) 18 | { 19 | this.StartPin = startPin; 20 | this.EndPin = endPin; 21 | this.Points = points; 22 | } 23 | 24 | public List GetPoints(Pin start = null, bool ends = true) 25 | { 26 | if (start == null) start = this.StartPin; 27 | 28 | var points = new List(); 29 | if (start == this.StartPin) 30 | { 31 | if (ends) points.Add(start.Location); 32 | points.AddRange(this.Points); 33 | if (ends && this.EndPin != null) points.Add(this.EndPin.Location); 34 | } 35 | else 36 | { 37 | if (this.EndPin == null) return points; 38 | if (ends) points.Add(start.Location); 39 | foreach (var p in this.Points) points.Insert(ends ? 1 : 0, p); 40 | if (ends) points.Add(this.StartPin.Location); 41 | } 42 | 43 | return points; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /ENums.cs: -------------------------------------------------------------------------------- 1 | namespace WireMod 2 | { 3 | public enum TeamColor 4 | { 5 | White = 0, // None 6 | Red = 1, 7 | Green = 2, 8 | Blue = 3, 9 | Yellow = 4, 10 | Pink = 5 11 | } 12 | } -------------------------------------------------------------------------------- /Helpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | 6 | namespace WireMod 7 | { 8 | public static class Helpers 9 | { 10 | public static Vector2 Drift => new Vector2(Main.screenWidth, Main.screenHeight) * (Main.UIScale - 1) * 0.5f; 11 | public static Rectangle GetScreenRect() => new Rectangle((int)Main.screenPosition.X, (int)Main.screenPosition.Y, (int)(Main.screenWidth * Main.UIScale), (int)(Main.screenHeight * Main.UIScale)); 12 | 13 | public static Texture2D CreateCircle(int diameter) 14 | { 15 | var texture = new Texture2D(Main.graphics.GraphicsDevice, diameter, diameter); 16 | var colorData = new Color[diameter * diameter]; 17 | 18 | var radius = diameter / 2f; 19 | 20 | for (var x = 0; x < diameter; x++) 21 | { 22 | for (var y = 0; y < diameter; y++) 23 | { 24 | var index = x * diameter + y; 25 | if (new Vector2(x - radius, y - radius).LengthSquared() <= radius * radius) 26 | { 27 | colorData[index] = Color.White; 28 | } 29 | else 30 | { 31 | colorData[index] = Color.Transparent; 32 | } 33 | } 34 | } 35 | 36 | texture.SetData(colorData); 37 | return texture; 38 | } 39 | 40 | public static Texture2D CreateRectangle(int width, int height) 41 | { 42 | var texture = new Texture2D(Main.graphics.GraphicsDevice, width, height); 43 | var colorData = new Color[width * height]; 44 | 45 | for (var x = 0; x < width; x++) 46 | { 47 | for (var y = 0; y < height; y++) 48 | { 49 | colorData[x * width + y] = Color.White; 50 | } 51 | } 52 | 53 | texture.SetData(colorData); 54 | return texture; 55 | } 56 | 57 | public static bool TryParsePoint(string input, out Point16? output) 58 | { 59 | if (string.IsNullOrEmpty(input) || !input.Contains(",")) 60 | { 61 | output = null; 62 | return false; 63 | } 64 | 65 | var split = input.Split(','); 66 | 67 | if (!int.TryParse(split[0], out var x) || !int.TryParse(split[1], out var y)) 68 | { 69 | output = null; 70 | return false; 71 | } 72 | 73 | output = new Point16(x, y); 74 | return true; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Images/Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Add.png -------------------------------------------------------------------------------- /Images/AndGate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/AndGate.png -------------------------------------------------------------------------------- /Images/Announcer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Announcer.png -------------------------------------------------------------------------------- /Images/AreaInput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/AreaInput.png -------------------------------------------------------------------------------- /Images/BooleanConstant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/BooleanConstant.png -------------------------------------------------------------------------------- /Images/Buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Buffer.png -------------------------------------------------------------------------------- /Images/Concat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Concat.png -------------------------------------------------------------------------------- /Images/Divide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Divide.png -------------------------------------------------------------------------------- /Images/Equals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Equals.png -------------------------------------------------------------------------------- /Images/FlipFlop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/FlipFlop.png -------------------------------------------------------------------------------- /Images/GreaterThan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/GreaterThan.png -------------------------------------------------------------------------------- /Images/Icons/AddIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/AddIcon.png -------------------------------------------------------------------------------- /Images/Icons/AndGateIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/AndGateIcon.png -------------------------------------------------------------------------------- /Images/Icons/AnnouncerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/AnnouncerIcon.png -------------------------------------------------------------------------------- /Images/Icons/AreaInputIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/AreaInputIcon.png -------------------------------------------------------------------------------- /Images/Icons/BooleanConstantIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/BooleanConstantIcon.png -------------------------------------------------------------------------------- /Images/Icons/BufferIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/BufferIcon.png -------------------------------------------------------------------------------- /Images/Icons/ConcatIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/ConcatIcon.png -------------------------------------------------------------------------------- /Images/Icons/DebugIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/DebugIcon.png -------------------------------------------------------------------------------- /Images/Icons/DeleteIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/DeleteIcon.png -------------------------------------------------------------------------------- /Images/Icons/DivideIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/DivideIcon.png -------------------------------------------------------------------------------- /Images/Icons/EqualsIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/EqualsIcon.png -------------------------------------------------------------------------------- /Images/Icons/FlipFlopIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/FlipFlopIcon.png -------------------------------------------------------------------------------- /Images/Icons/GreaterThanIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/GreaterThanIcon.png -------------------------------------------------------------------------------- /Images/Icons/IfIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/IfIcon.png -------------------------------------------------------------------------------- /Images/Icons/IntegerConstantIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/IntegerConstantIcon.png -------------------------------------------------------------------------------- /Images/Icons/LessThanIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/LessThanIcon.png -------------------------------------------------------------------------------- /Images/Icons/ModuloIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/ModuloIcon.png -------------------------------------------------------------------------------- /Images/Icons/MultiplyIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/MultiplyIcon.png -------------------------------------------------------------------------------- /Images/Icons/NPCCounterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/NPCCounterIcon.png -------------------------------------------------------------------------------- /Images/Icons/NPCDistanceSensorIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/NPCDistanceSensorIcon.png -------------------------------------------------------------------------------- /Images/Icons/NoneIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/NoneIcon.png -------------------------------------------------------------------------------- /Images/Icons/NotGateIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/NotGateIcon.png -------------------------------------------------------------------------------- /Images/Icons/OrGateIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/OrGateIcon.png -------------------------------------------------------------------------------- /Images/Icons/OutputLampIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/OutputLampIcon.png -------------------------------------------------------------------------------- /Images/Icons/OutputSignIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/OutputSignIcon.png -------------------------------------------------------------------------------- /Images/Icons/PlayerCounterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/PlayerCounterIcon.png -------------------------------------------------------------------------------- /Images/Icons/PlayerDistanceSensorIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/PlayerDistanceSensorIcon.png -------------------------------------------------------------------------------- /Images/Icons/PointConstantIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/PointConstantIcon.png -------------------------------------------------------------------------------- /Images/Icons/ProtectAreaIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/ProtectAreaIcon.png -------------------------------------------------------------------------------- /Images/Icons/RandomIntIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/RandomIntIcon.png -------------------------------------------------------------------------------- /Images/Icons/RepulsorIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/RepulsorIcon.png -------------------------------------------------------------------------------- /Images/Icons/SpawnerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/SpawnerIcon.png -------------------------------------------------------------------------------- /Images/Icons/StringConstantIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/StringConstantIcon.png -------------------------------------------------------------------------------- /Images/Icons/SubtractIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/SubtractIcon.png -------------------------------------------------------------------------------- /Images/Icons/TeamColorConstantIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TeamColorConstantIcon.png -------------------------------------------------------------------------------- /Images/Icons/TextTileControllerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TextTileControllerIcon.png -------------------------------------------------------------------------------- /Images/Icons/TileCopierIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TileCopierIcon.png -------------------------------------------------------------------------------- /Images/Icons/TileCounterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TileCounterIcon.png -------------------------------------------------------------------------------- /Images/Icons/TilePasterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TilePasterIcon.png -------------------------------------------------------------------------------- /Images/Icons/TimeSensorIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TimeSensorIcon.png -------------------------------------------------------------------------------- /Images/Icons/TriggerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/TriggerIcon.png -------------------------------------------------------------------------------- /Images/Icons/VariableIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/VariableIcon.png -------------------------------------------------------------------------------- /Images/Icons/WiringIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Icons/WiringIcon.png -------------------------------------------------------------------------------- /Images/If.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/If.png -------------------------------------------------------------------------------- /Images/IntegerConstant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/IntegerConstant.png -------------------------------------------------------------------------------- /Images/LessThan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/LessThan.png -------------------------------------------------------------------------------- /Images/Modulo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Modulo.png -------------------------------------------------------------------------------- /Images/Multiply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Multiply.png -------------------------------------------------------------------------------- /Images/NPCCounter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/NPCCounter.png -------------------------------------------------------------------------------- /Images/NPCDistanceSensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/NPCDistanceSensor.png -------------------------------------------------------------------------------- /Images/NotGate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/NotGate.png -------------------------------------------------------------------------------- /Images/OrGate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/OrGate.png -------------------------------------------------------------------------------- /Images/OutputLamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/OutputLamp.png -------------------------------------------------------------------------------- /Images/OutputSign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/OutputSign.png -------------------------------------------------------------------------------- /Images/PlayerCounter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/PlayerCounter.png -------------------------------------------------------------------------------- /Images/PlayerDistanceSensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/PlayerDistanceSensor.png -------------------------------------------------------------------------------- /Images/PointConstant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/PointConstant.png -------------------------------------------------------------------------------- /Images/ProtectArea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/ProtectArea.png -------------------------------------------------------------------------------- /Images/RandomInt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/RandomInt.png -------------------------------------------------------------------------------- /Images/Repulsor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Repulsor.png -------------------------------------------------------------------------------- /Images/Spawner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Spawner.png -------------------------------------------------------------------------------- /Images/StringConstant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/StringConstant.png -------------------------------------------------------------------------------- /Images/Subtract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Subtract.png -------------------------------------------------------------------------------- /Images/TeamColorConstant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TeamColorConstant.png -------------------------------------------------------------------------------- /Images/TextTileController.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TextTileController.png -------------------------------------------------------------------------------- /Images/TileCopier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TileCopier.png -------------------------------------------------------------------------------- /Images/TileCounter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TileCounter.png -------------------------------------------------------------------------------- /Images/TilePaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TilePaster.png -------------------------------------------------------------------------------- /Images/TimeSensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/TimeSensor.png -------------------------------------------------------------------------------- /Images/Trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Trigger.png -------------------------------------------------------------------------------- /Images/Variable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Images/Variable.png -------------------------------------------------------------------------------- /Items/ElectronicsManual.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Terraria; 3 | using Terraria.DataStructures; 4 | using Terraria.ID; 5 | using Terraria.ModLoader; 6 | using WireMod.Devices; 7 | using WireMod.UI; 8 | 9 | namespace WireMod.Items 10 | { 11 | internal class ElectronicsManual : ModItem 12 | { 13 | public override void SetStaticDefaults() 14 | { 15 | DisplayName.SetDefault("Electronics Manual"); 16 | Tooltip.SetDefault("Grants use of WireMod functionality"); 17 | } 18 | 19 | public override void SetDefaults() 20 | { 21 | item.CloneDefaults(ItemID.ActuationRod); 22 | item.autoReuse = false; 23 | item.holdStyle = 0; 24 | item.noMelee = true; 25 | } 26 | 27 | public override void AddRecipes() 28 | { 29 | var recipe = new ModRecipe(mod); 30 | recipe.AddIngredient(ItemID.Wire, 50); 31 | recipe.AddIngredient(ItemID.Book); 32 | recipe.AddTile(TileID.TinkerersWorkbench); 33 | recipe.SetResult(this); 34 | recipe.AddRecipe(); 35 | } 36 | 37 | public override bool AltFunctionUse(Player player) => true; 38 | 39 | public override void HoldItem(Player player) 40 | { 41 | if (Main.netMode == NetmodeID.Server) return; 42 | if (player != Main.LocalPlayer) return; 43 | 44 | var modPlayer = player.GetModPlayer(); 45 | 46 | ElectronicsManualUI.Visible = true; 47 | ElectronicsVisionUI.Visible = true; 48 | 49 | modPlayer.ShowPreview = false; 50 | 51 | if (modPlayer.PlacingDevice != null) 52 | { 53 | if (modPlayer.ToolCategoryMode > 0) 54 | { 55 | modPlayer.ShowPreview = true; 56 | } 57 | } 58 | else 59 | { 60 | (var x, var y) = WireMod.Instance.GetMouseTilePosition(); 61 | 62 | var dev = WireMod.GetDevice(x, y); 63 | if (dev == null) return; 64 | 65 | var pin = WireMod.GetDevicePin(x, y); 66 | 67 | WireMod.Instance.DebuggerHoverUserInterface.SetState(new HoverDebuggerUI(dev, pin)); 68 | HoverDebuggerUI.Visible = true; 69 | } 70 | } 71 | 72 | public override bool CanUseItem(Player player) 73 | { 74 | if (Main.netMode == NetmodeID.Server) return false; 75 | if (player != Main.LocalPlayer) return false; 76 | 77 | var modPlayer = player.GetModPlayer(); 78 | 79 | var (x, y) = WireMod.Instance.GetMouseTilePosition(); 80 | var device = WireMod.GetDevice(x, y); 81 | 82 | var pin = WireMod.Pins.FirstOrDefault(p => p.Location == new Point16(x, y)); 83 | 84 | // Wiring tools 85 | if (modPlayer.ToolCategoryMode == 0) 86 | { 87 | if (modPlayer.ToolMode == 0) // No tool selected 88 | { 89 | if (device == null) return false; 90 | 91 | if (player.altFunctionUse == 2) 92 | { 93 | // Right click 94 | device.OnRightClick(pin); 95 | 96 | return true; 97 | } 98 | 99 | // Do something? 100 | } 101 | 102 | if (modPlayer.ToolMode == 1) // Wiring Tool 103 | { 104 | if (player.altFunctionUse == 2) 105 | { 106 | // Right Click 107 | 108 | if (device == null) 109 | { 110 | modPlayer.ConnectingPin = null; 111 | modPlayer.PlacingWire = null; 112 | return false; 113 | } 114 | 115 | if (pin == null) 116 | { 117 | device.OnRightClick(); 118 | return true; 119 | } 120 | 121 | pin.Disconnect(); 122 | 123 | if (Main.netMode == NetmodeID.MultiplayerClient) 124 | { 125 | WireMod.PacketHandler.SendDisconnect(256, Main.myPlayer, x, y); 126 | } 127 | 128 | return true; 129 | } 130 | 131 | if (pin == null) 132 | { 133 | modPlayer.PlacingWire?.Points.Add(new Point16(x, y)); 134 | return false; 135 | } 136 | 137 | #region Connect Wires 138 | // Connect wires 139 | if (modPlayer.ConnectingPin == null) 140 | { 141 | Main.NewText("Connecting..."); 142 | modPlayer.ConnectingPin = pin; 143 | modPlayer.PlacingWire = new Wire(pin); 144 | 145 | return true; 146 | } 147 | 148 | if (modPlayer.ConnectingPin.Type == pin.Type) 149 | { 150 | Main.NewText("Cancelled - must connect a PinIn to a PinOut (or vice versa)"); 151 | modPlayer.ConnectingPin = null; 152 | modPlayer.PlacingWire = null; 153 | return false; 154 | } 155 | 156 | if (modPlayer.ConnectingPin.Device == pin.Device) 157 | { 158 | Main.NewText("Cancelled - cannot connect to same device"); 159 | modPlayer.ConnectingPin = null; 160 | modPlayer.PlacingWire = null; 161 | return false; 162 | } 163 | 164 | if (modPlayer.ConnectingPin.DataType != pin.DataType) 165 | { 166 | if (!(modPlayer.ConnectingPin.DataType == "int" && pin.DataType == "string" && pin.Device.Name == "Concat")) 167 | { 168 | if (modPlayer.ConnectingPin.Device.DetectType() != "auto" && pin.Device.DetectType() != "auto") 169 | { 170 | Main.NewText("Cancelled - cannot connect different data types"); 171 | modPlayer.ConnectingPin = null; 172 | modPlayer.PlacingWire = null; 173 | return false; 174 | } 175 | } 176 | } 177 | 178 | if (modPlayer.ConnectingPin.Device.Pins.SelectMany(p => p.Value.Values).Any(p => 179 | { 180 | return p.Type == "In" 181 | ? ((PinIn)p).ConnectedPin?.Device == pin.Device 182 | : ((PinOut)p).ConnectedPins.Any(cp => ((PinIn)cp).ConnectedPin?.Device == pin.Device); 183 | })) 184 | { 185 | Main.NewText("Cancelled - circular connection detected"); 186 | modPlayer.ConnectingPin = null; 187 | modPlayer.PlacingWire = null; 188 | return false; 189 | } 190 | 191 | modPlayer.PlacingWire.EndPin = pin; 192 | modPlayer.ConnectingPin.Connect(pin, modPlayer.PlacingWire); 193 | pin.Connect(modPlayer.ConnectingPin, modPlayer.PlacingWire); 194 | 195 | if (Main.netMode == NetmodeID.MultiplayerClient) 196 | { 197 | WireMod.PacketHandler.SendConnect(256, Main.myPlayer, modPlayer.ConnectingPin.Location.X, modPlayer.ConnectingPin.Location.Y, pin.Location.X, pin.Location.Y, modPlayer.PlacingWire); 198 | } 199 | 200 | modPlayer.ConnectingPin = null; 201 | modPlayer.PlacingWire = null; 202 | 203 | return true; 204 | #endregion 205 | } 206 | 207 | if (modPlayer.ToolMode == 2) 208 | { 209 | // Delete device 210 | if (device == null) return false; 211 | 212 | WireMod.RemoveDevice(device); 213 | 214 | if (Main.netMode == NetmodeID.MultiplayerClient) 215 | { 216 | WireMod.PacketHandler.SendRemove(256, Main.myPlayer, x, y); 217 | } 218 | 219 | return true; 220 | } 221 | 222 | if (modPlayer.ToolMode == 3) 223 | { 224 | if (player.altFunctionUse == 2) 225 | { 226 | WireMod.Instance.DebuggerUserInterface.SetState(null); 227 | DebuggerUI.Visible = true; 228 | 229 | return true; 230 | } 231 | 232 | // Debug device 233 | if (device == null) return false; 234 | 235 | WireMod.Instance.DebuggerUserInterface.SetState(new DebuggerUI(device, pin)); 236 | DebuggerUI.Visible = true; 237 | } 238 | } 239 | 240 | if (device != null) return false; 241 | 242 | // Find microchips in inventory 243 | var microChipIndex = -1; 244 | for (var i = 0; i < player.inventory.Length; i++) 245 | { 246 | if (player.inventory[i].type != mod.ItemType()) continue; 247 | 248 | microChipIndex = i; 249 | break; 250 | } 251 | 252 | // No microchips in inventory 253 | if (microChipIndex == -1) return false; 254 | if (modPlayer.PlacingDevice == null) return false; 255 | if (!WireMod.CanPlace(modPlayer.PlacingDevice, x, y)) return false; 256 | 257 | // Place the device 258 | WireMod.PlaceDevice(modPlayer.PlacingDevice, x, y); 259 | 260 | if (Main.netMode == NetmodeID.MultiplayerClient) 261 | { 262 | WireMod.PacketHandler.SendPlace(256, Main.myPlayer, modPlayer.PlacingDevice.GetType().Name, modPlayer.PlacingDevice.Settings, x, y); 263 | } 264 | 265 | // Consume a microchip 266 | player.inventory[microChipIndex].stack--; 267 | 268 | // Reset 269 | modPlayer.PlacingDevice = null; 270 | modPlayer.ConnectingPin = null; 271 | modPlayer.PlacingWire = null; 272 | modPlayer.ToolCategoryMode = 0; 273 | modPlayer.ToolMode = 0; 274 | 275 | return true; 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /Items/ElectronicsManual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Items/ElectronicsManual.png -------------------------------------------------------------------------------- /Items/Microchip.cs: -------------------------------------------------------------------------------- 1 | using Terraria.ID; 2 | using Terraria.ModLoader; 3 | 4 | namespace WireMod.Items 5 | { 6 | internal class Microchip : ModItem 7 | { 8 | public override void SetDefaults() 9 | { 10 | item.width = 16; 11 | item.height = 16; 12 | item.maxStack = 99; 13 | item.consumable = true; 14 | item.value = 50; 15 | } 16 | 17 | public override void AddRecipes() 18 | { 19 | var recipe = new ModRecipe(mod); 20 | recipe.AddIngredient(ItemID.Wire); 21 | recipe.AddIngredient(ItemID.Actuator); 22 | recipe.AddIngredient(ItemID.CopperCoin); 23 | recipe.AddTile(TileID.WorkBenches); 24 | recipe.SetResult(this); 25 | recipe.AddRecipe(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Items/Microchip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Items/Microchip.png -------------------------------------------------------------------------------- /Items/TextTileItem.cs: -------------------------------------------------------------------------------- 1 | using Terraria.ID; 2 | using Terraria.ModLoader; 3 | 4 | namespace WireMod.Items 5 | { 6 | internal class TextTileItem : ModItem 7 | { 8 | public override void SetDefaults() 9 | { 10 | item.width = 16; 11 | item.height = 16; 12 | item.maxStack = 99; 13 | item.consumable = true; 14 | item.autoReuse = true; 15 | item.useStyle = 1; 16 | item.value = 50; 17 | item.createTile = mod.TileType("TextTile"); 18 | } 19 | 20 | public override void AddRecipes() 21 | { 22 | var recipe = new ModRecipe(mod); 23 | recipe.AddIngredient(ItemID.Wire); 24 | recipe.AddIngredient(ItemID.StoneBlock); 25 | recipe.AddTile(TileID.TinkerersWorkbench); 26 | recipe.SetResult(this); 27 | recipe.AddRecipe(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Items/TextTileItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Items/TextTileItem.png -------------------------------------------------------------------------------- /PacketHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Microsoft.Xna.Framework; 6 | using Terraria; 7 | using Terraria.DataStructures; 8 | using Terraria.ID; 9 | using Terraria.ModLoader; 10 | using WireMod.Devices; 11 | 12 | // ReSharper disable ConditionIsAlwaysTrueOrFalse 13 | 14 | namespace WireMod 15 | { 16 | internal class DevicePacketHandler 17 | { 18 | public const byte Place = 1; 19 | public const byte Remove = 2; 20 | public const byte Connect = 3; 21 | public const byte Disconnect = 4; 22 | public const byte ChangeSetting = 5; 23 | public const byte TripWire = 6; 24 | public const byte Request = 7; 25 | 26 | 27 | protected ModPacket GetPacket(byte packetType, int from) 28 | { 29 | var packet = WireMod.Instance.GetPacket(); 30 | packet.Write(packetType); 31 | return packet; 32 | } 33 | 34 | public void HandlePacket(BinaryReader reader, int from) 35 | { 36 | switch (reader.ReadByte()) 37 | { 38 | case Place: 39 | ReceivePlace(reader, from); 40 | break; 41 | case Remove: 42 | ReceiveRemove(reader, from); 43 | break; 44 | case Connect: 45 | ReceiveConnect(reader, from); 46 | break; 47 | case Disconnect: 48 | ReceiveDisconnect(reader, from); 49 | break; 50 | case ChangeSetting: 51 | ReceiveChangeSetting(reader, from); 52 | break; 53 | case TripWire: 54 | ReceiveTripWire(reader, from); 55 | break; 56 | case Request: 57 | ReceiveRequest(reader, from); 58 | break; 59 | } 60 | } 61 | 62 | #region Place 63 | public void SendPlace(int to, int from, string name, Dictionary settings, int x, int y) 64 | { 65 | var packet = GetPacket(Place, from); 66 | 67 | packet.Write(name); 68 | packet.Write(x); 69 | packet.Write(y); 70 | packet.Write(settings.Count); 71 | 72 | foreach (var setting in settings) 73 | { 74 | packet.Write(setting.Key); 75 | packet.Write(setting.Value); 76 | } 77 | 78 | packet.Send(to, from); 79 | } 80 | 81 | public void ReceivePlace(BinaryReader reader, int from) 82 | { 83 | var name = reader.ReadString(); 84 | var x = reader.ReadInt32(); 85 | var y = reader.ReadInt32(); 86 | 87 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received Place: name {name}, x {x}, y {y}"); 88 | 89 | var device = (Device)Activator.CreateInstance(Type.GetType("WireMod.Devices." + name) ?? throw new InvalidOperationException("Device not found!")); 90 | 91 | if (!WireMod.CanPlace(device, x, y)) 92 | { 93 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} Place: Cannot place device at: x {x}, y {y}"); 94 | } 95 | 96 | var settingCount = reader.ReadInt32(); 97 | var settings = new Dictionary(); 98 | for (var i = 0; i < settingCount; i++) 99 | { 100 | var key = reader.ReadString(); 101 | var value = reader.ReadString(); 102 | 103 | settings.Add(key, value); 104 | } 105 | 106 | if (Main.netMode == NetmodeID.Server) 107 | { 108 | SendPlace(-1, from, name, settings, x, y); 109 | } 110 | 111 | device.SetLocation(x - device.Origin.X, y - device.Origin.Y); 112 | device.Settings = settings; 113 | WireMod.PlaceDevice(device, x, y); 114 | } 115 | #endregion 116 | 117 | #region Request 118 | public void SendRequest(int to, int from) 119 | { 120 | var packet = GetPacket(Request, from); 121 | 122 | packet.Write(from); 123 | 124 | packet.Send(to, from); 125 | } 126 | 127 | public void ReceiveRequest(BinaryReader reader, int from) 128 | { 129 | from = reader.ReadInt32(); 130 | 131 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received Request from {Main.player[from].name}"); 132 | 133 | if (Main.netMode != NetmodeID.Server) return; 134 | 135 | foreach (var device in WireMod.Devices) 136 | { 137 | this.SendPlace(from, 256, device.GetType().Name, device.Settings, device.LocationRect.X + device.Origin.X, device.LocationRect.Y + device.Origin.Y); 138 | } 139 | 140 | var wires = WireMod.Pins.Where(p => p.Type == "In" && p.IsConnected()).Select(p => new 141 | { 142 | src = p.Location, 143 | dest = ((PinIn)p).ConnectedPin.Location, 144 | wire = p.GetWire(((PinIn)p).ConnectedPin) 145 | }).ToList(); 146 | 147 | foreach (var conn in wires) 148 | { 149 | this.SendConnect(from, 256, conn.src.X, conn.src.Y, conn.dest.X, conn.dest.Y, conn.wire); 150 | } 151 | } 152 | #endregion 153 | 154 | #region Remove 155 | public void SendRemove(int to, int from, int x, int y) 156 | { 157 | var packet = GetPacket(Remove, from); 158 | packet.Write(x); 159 | packet.Write(y); 160 | packet.Send(to, from); 161 | } 162 | 163 | public void ReceiveRemove(BinaryReader reader, int from) 164 | { 165 | var x = reader.ReadInt32(); 166 | var y = reader.ReadInt32(); 167 | 168 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received Remove: x {x}, y {y}"); 169 | 170 | if (!WireMod.Devices.Any(d => d.LocationRect.Intersects(new Rectangle(x, y, 1, 1)))) 171 | { 172 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} Remove: No device found at: x {x}, y {y}"); 173 | } 174 | 175 | if (Main.netMode == NetmodeID.Server) 176 | { 177 | SendRemove(-1, from, x, y); 178 | } 179 | 180 | WireMod.RemoveDevice(x, y); 181 | } 182 | #endregion 183 | 184 | #region Connect 185 | public void SendConnect(int to, int from, int srcX, int srcY, int destX, int destY, Wire wire) 186 | { 187 | var packet = GetPacket(Connect, from); 188 | 189 | packet.Write(srcX); 190 | packet.Write(srcY); 191 | packet.Write(destX); 192 | packet.Write(destY); 193 | 194 | packet.Write(wire.Points.Count); 195 | foreach (var point in wire.Points) 196 | { 197 | packet.Write((int)point.X); 198 | packet.Write((int)point.Y); 199 | } 200 | 201 | packet.Send(to, from); 202 | } 203 | 204 | public void ReceiveConnect(BinaryReader reader, int from) 205 | { 206 | var srcX = reader.ReadInt32(); 207 | var srcY = reader.ReadInt32(); 208 | var destX = reader.ReadInt32(); 209 | var destY = reader.ReadInt32(); 210 | 211 | var pointsCount = reader.ReadInt32(); 212 | var points = new List(); 213 | if (pointsCount > 0) 214 | { 215 | for (var i = 0; i < pointsCount; i++) 216 | { 217 | var x = reader.ReadInt32(); 218 | var y = reader.ReadInt32(); 219 | 220 | points.Add(new Point16(x, y)); 221 | } 222 | } 223 | 224 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received Connect: srcX {srcX}, srcY {srcY}, destX {destX}, destY {destY}"); 225 | 226 | var src = WireMod.GetDevicePin(srcX, srcY); 227 | if (src == null) 228 | { 229 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} Connect: No pin found at: x {srcX}, y {srcY}"); 230 | return; 231 | } 232 | 233 | var dest = WireMod.GetDevicePin(destX, destY); 234 | if (dest == null) 235 | { 236 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} Connect: No pin found at: x {srcX}, y {srcY}"); 237 | return; 238 | } 239 | 240 | var wire = new Wire(src, dest, points); 241 | 242 | if (Main.netMode == NetmodeID.Server) 243 | { 244 | this.SendConnect(-1, from, srcX, srcY, destX, destY, wire); 245 | } 246 | 247 | src.Connect(dest, wire); 248 | dest.Connect(src, wire); 249 | } 250 | #endregion 251 | 252 | #region Disconnect 253 | public void SendDisconnect(int to, int from, int x, int y) 254 | { 255 | var packet = GetPacket(Disconnect, from); 256 | 257 | packet.Write(x); 258 | packet.Write(y); 259 | 260 | packet.Send(to, from); 261 | } 262 | 263 | public void ReceiveDisconnect(BinaryReader reader, int from) 264 | { 265 | var x = reader.ReadInt32(); 266 | var y = reader.ReadInt32(); 267 | 268 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received Disconnect: x {x}, y {y}"); 269 | 270 | var src = WireMod.GetDevicePin(x, y); 271 | if (src == null) 272 | { 273 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} Disconnect: No pin found at: x {x}, y {y}"); 274 | return; 275 | } 276 | 277 | if (Main.netMode == NetmodeID.Server) 278 | { 279 | this.SendDisconnect(-1, from, x, y); 280 | } 281 | 282 | src.Disconnect(); 283 | } 284 | #endregion 285 | 286 | #region Change Setting 287 | public void SendChangeSetting(int to, int from, int x, int y, string key, string value) 288 | { 289 | var packet = GetPacket(ChangeSetting, from); 290 | packet.Write(x); 291 | packet.Write(y); 292 | packet.Write(key); 293 | packet.Write(value); 294 | packet.Send(to, from); 295 | } 296 | 297 | public void ReceiveChangeSetting(BinaryReader reader, int from) 298 | { 299 | var x = reader.ReadInt32(); 300 | var y = reader.ReadInt32(); 301 | var key = reader.ReadString(); 302 | var value = reader.ReadString(); 303 | 304 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received ChangeValue: x {x}, y {y}, value {value}"); 305 | 306 | if (Main.netMode == NetmodeID.Server) 307 | { 308 | this.SendChangeSetting(-1, from, x, y, key, value); 309 | } 310 | 311 | var device = WireMod.GetDevice(x, y); 312 | if (device == null) 313 | { 314 | if (WireMod.Debug) WireMod.Instance.Logger.Error($"{from} ChangeValue: No device found at: x {x}, y {y}"); 315 | return; 316 | } 317 | 318 | device.Settings[key] = value; 319 | } 320 | #endregion 321 | 322 | #region TripWire 323 | public void SendTripWire(int to, int from, int x, int y) 324 | { 325 | var packet = GetPacket(TripWire, from); 326 | 327 | packet.Write(x); 328 | packet.Write(y); 329 | 330 | packet.Send(to, from); 331 | } 332 | 333 | public void ReceiveTripWire(BinaryReader reader, int from) 334 | { 335 | // TODO: Find out what happens if player 0 leaves 336 | if (from != 0) return; 337 | 338 | var x = reader.ReadInt32(); 339 | var y = reader.ReadInt32(); 340 | 341 | if (WireMod.Debug) WireMod.Instance.Logger.Info($"{from} Received TripWire: x {x}, y {y}"); 342 | 343 | Wiring.TripWire(x, y, 1, 1); 344 | } 345 | #endregion 346 | } 347 | } -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Terraria": { 4 | "commandName": "Executable", 5 | "executablePath": "$(tMLPath)", 6 | "workingDirectory": "$(TerrariaSteamPath)" 7 | }, 8 | "TerrariaServer": { 9 | "commandName": "Executable", 10 | "executablePath": "$(tMLServerPath)", 11 | "workingDirectory": "$(TerrariaSteamPath)" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WireMod is an advanced electronics/logic circuit mod for Terraria, using tModLoader. 2 | 3 | This mod intends to improve upon and ultimately replace the existing wiring mechanics in the vanilla game. 4 | 5 | Check out the [wiki](https://github.com/MatLomax/WireMod/wiki) for information on what you can do with this mod and how to use it. -------------------------------------------------------------------------------- /Tiles/TextTile.cs: -------------------------------------------------------------------------------- 1 | using Terraria; 2 | using Terraria.DataStructures; 3 | using Terraria.ModLoader; 4 | using Terraria.ObjectData; 5 | 6 | namespace WireMod.Tiles 7 | { 8 | public class TextTile : ModTile 9 | { 10 | public override void SetDefaults() 11 | { 12 | base.SetDefaults(); 13 | 14 | Main.tileFrameImportant[Type] = true; 15 | 16 | TileObjectData.newTile.CopyFrom(TileObjectData.Style3x3Wall); 17 | TileObjectData.newTile.StyleHorizontal = true; 18 | TileObjectData.newTile.Width = 1; 19 | TileObjectData.newTile.Height = 1; 20 | TileObjectData.newTile.Origin = Point16.Zero; 21 | TileObjectData.newTile.CoordinateHeights = new [] { 16 }; 22 | 23 | TileObjectData.addTile(Type); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Tiles/TextTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/Tiles/TextTile.png -------------------------------------------------------------------------------- /UI/DebuggerUI.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System.Linq; 4 | using Terraria; 5 | using Terraria.UI; 6 | using Terraria.UI.Chat; 7 | using WireMod.Devices; 8 | 9 | namespace WireMod.UI 10 | { 11 | internal class DebuggerUI : UIState 12 | { 13 | public static bool Visible { get; set; } 14 | 15 | public readonly Device Device; 16 | public readonly Pin Pin; 17 | 18 | public DebuggerUI(Device device, Pin pin = null) 19 | { 20 | this.Device = device; 21 | this.Pin = pin; 22 | } 23 | 24 | protected override void DrawSelf(SpriteBatch spriteBatch) 25 | { 26 | base.DrawSelf(spriteBatch); 27 | 28 | var lines = this.Device.Debug(this.Pin); 29 | if (lines == null) return; 30 | 31 | const int padding = 10; 32 | const int border = 2; 33 | 34 | var width = (int)(lines.Max(t => (int)ChatManager.GetStringSize(Main.fontMouseText, t.Line, Vector2.One).X * t.Size) + (padding * 2)); 35 | var height = (int)lines.ToList().Sum(t => Main.fontMouseText.MeasureString(t.Line).Y) + padding * 2; 36 | 37 | var x = 100; 38 | var y = Main.screenHeight - height - 30; 39 | 40 | spriteBatch.Draw(Main.magicPixel, new Rectangle(x, y, width + border * 2, height + border * 2), null, Color.Black, 0f, new Vector2(0, 0), SpriteEffects.None, 0f); 41 | spriteBatch.Draw(Main.magicPixel, new Rectangle(x + border, y + border, width, height), null, Color.White, 0f, new Vector2(0, 0), SpriteEffects.None, 0f); 42 | 43 | var i = 0; 44 | foreach ((string line, Color color, float size) in lines) 45 | { 46 | var lineHeight = Main.fontMouseText.MeasureString(line).Y; 47 | Utils.DrawBorderStringFourWay(spriteBatch, Main.fontMouseText, line, x + padding, y + padding + (lineHeight * i) + padding, color, Color.White, new Vector2(0.3f), size); 48 | i++; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /UI/ElectronicsManualButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using Terraria; 4 | using Terraria.GameContent.UI.Elements; 5 | using WireMod.Devices; 6 | 7 | namespace WireMod.UI 8 | { 9 | public class ElectronicsManualButton : UIImageButton 10 | { 11 | public string Name { get; set; } 12 | public int ToolCat { get; set; } 13 | public int Tool { get; set; } 14 | 15 | public ElectronicsManualButton(string name, Texture2D texture) : base(texture) 16 | { 17 | this.Name = name; 18 | } 19 | 20 | protected override void DrawSelf(SpriteBatch spriteBatch) 21 | { 22 | var modPlayer = Main.LocalPlayer.GetModPlayer(); 23 | if (modPlayer == null) 24 | { 25 | base.DrawSelf(spriteBatch); 26 | return; 27 | } 28 | 29 | var hovering = this.ContainsPoint(Main.MouseScreen); 30 | 31 | // Set button opacity 32 | if (this.ToolCat == modPlayer.ToolCategoryMode && this.Tool == modPlayer.ToolMode && modPlayer.ToolMode + modPlayer.ToolCategoryMode > 0) 33 | { 34 | this.SetVisibility(1f, 1f); 35 | } 36 | else if (hovering) 37 | { 38 | this.SetVisibility(0.75f, 0.75f); 39 | } 40 | else 41 | { 42 | this.SetVisibility(0.5f, 0.5f); 43 | } 44 | 45 | if (hovering) 46 | { 47 | Main.instance.MouseText(Constants.ToolNames[this.Name]); 48 | 49 | // Click 50 | if (!Main.mouseLeftRelease || !Main.mouseLeft) 51 | { 52 | base.DrawSelf(spriteBatch); 53 | return; 54 | } 55 | 56 | modPlayer.PlacingDevice = null; 57 | modPlayer.ConnectingPin = null; 58 | 59 | modPlayer.ToolCategoryMode = this.ToolCat; 60 | modPlayer.ToolMode = this.Tool; 61 | 62 | if (this.ToolCat <= 0) 63 | { 64 | base.DrawSelf(spriteBatch); 65 | return; 66 | } 67 | 68 | modPlayer.PlacingDevice = (Device)Activator.CreateInstance(Type.GetType("WireMod.Devices." + Constants.Tools[this.ToolCat][this.Tool]) ?? throw new InvalidOperationException("Device not found!")); 69 | modPlayer.PlacingDevice.SetPins(); 70 | } 71 | 72 | base.DrawSelf(spriteBatch); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /UI/ElectronicsManualUI.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using Terraria; 4 | using Terraria.DataStructures; 5 | using Terraria.GameContent.UI.Elements; 6 | using Terraria.ID; 7 | using Terraria.UI; 8 | using Terraria.UI.Chat; 9 | using WireMod.UI.Elements; 10 | 11 | namespace WireMod.UI 12 | { 13 | public class ElectronicsManualUI : UIState 14 | { 15 | public static bool Visible { get; set; } 16 | 17 | public DragableUIPanel Panel; 18 | 19 | public float OffsetX = 300f; 20 | public float OffsetY = 0f; 21 | public float PanelWidth = 270f; 22 | public float PanelHeight = 320f; 23 | public float ButtonSize = 32f; 24 | public float Padding = 20f; 25 | public float InnerPadding = 10f; 26 | public float ScaleX; 27 | public float ScaleY; 28 | public int PerRow = 6; 29 | 30 | public override void OnInitialize() 31 | { 32 | this.Panel = new DragableUIPanel(); 33 | this.Panel.Left.Set(((Main.screenWidth - this.PanelWidth) / 2) + this.OffsetX, 0f); 34 | this.Panel.Width.Set(this.PanelWidth, 0f); 35 | this.Panel.BorderColor = new Color(0, 0, 0, 0); 36 | 37 | var textHeight = ChatManager.GetStringSize(Main.fontMouseText, Constants.ToolCategories[0], new Vector2(0, 0)).Y; 38 | 39 | var currentY = 0f; 40 | var currentX = this.Padding; 41 | 42 | var uiTitle = new UIText("Electronics Manual", 1.5f); 43 | uiTitle.Height.Set(textHeight, 0f); 44 | uiTitle.Top.Set(currentY, 0f); 45 | this.Panel.Append(uiTitle); 46 | 47 | currentY += 40; 48 | 49 | for (var cat = 0; cat < Constants.ToolCategories.Count; cat++) 50 | { 51 | var uiCatTitle = new UIText(Constants.ToolCategories[cat]); 52 | uiCatTitle.Height.Set(textHeight, 0f); 53 | uiCatTitle.Top.Set(currentY, 0f); 54 | this.Panel.Append(uiCatTitle); 55 | 56 | var i = 0; 57 | foreach (var tool in Constants.Tools[cat]) 58 | { 59 | if (i % this.PerRow == 0) 60 | { 61 | currentY += this.ButtonSize + this.InnerPadding; 62 | currentX = 0; 63 | } 64 | else 65 | { 66 | currentX += this.ButtonSize + this.InnerPadding; 67 | } 68 | 69 | // TODO: Issue #6: Remove need for separate device icons 70 | var button = new ElectronicsManualButton(tool, WireMod.Instance.GetTexture($"Images/Icons/{tool}Icon")) 71 | { 72 | ToolCat = cat, 73 | Tool = i 74 | }; 75 | 76 | button.Height.Set(this.ButtonSize, 0f); 77 | button.Width.Set(this.ButtonSize, 0f); 78 | button.Left.Set(currentX, 0f); 79 | button.Top.Set(currentY - (this.ButtonSize / 2), 0f); 80 | button.SetVisibility(0f, 0.25f); 81 | 82 | this.Panel.Append(button); 83 | 84 | i++; 85 | } 86 | 87 | currentY += this.Padding + this.InnerPadding; 88 | currentX = this.Padding; 89 | } 90 | 91 | this.Panel.Height.Set(currentY + this.Padding, 0f); 92 | this.Panel.Top.Set(((Main.screenHeight - this.PanelHeight) / 2) + this.OffsetY, 0f); 93 | 94 | this.Append(this.Panel); 95 | Recalculate(); 96 | } 97 | 98 | protected override void DrawSelf(SpriteBatch spriteBatch) 99 | { 100 | base.DrawSelf(spriteBatch); 101 | 102 | var modPlayer = Main.LocalPlayer.GetModPlayer(); 103 | if (modPlayer != null && modPlayer.ShowPreview) this.DrawPreview(spriteBatch); 104 | } 105 | 106 | protected void DrawPreview(SpriteBatch spriteBatch) 107 | { 108 | if (Main.netMode == NetmodeID.Server) return; 109 | 110 | var modPlayer = Main.LocalPlayer.GetModPlayer(WireMod.Instance); 111 | var dev = modPlayer?.PlacingDevice; 112 | 113 | if (dev == null) return; 114 | 115 | var screenRect = new Rectangle((int)Main.screenPosition.X, (int)Main.screenPosition.Y, Main.screenWidth, Main.screenHeight); 116 | 117 | var (x, y) = WireMod.Instance.GetMouseTilePosition(); 118 | var offsetMouseTile = new Point16(x - dev.Origin.X, y - dev.Origin.Y); 119 | var offsetMouseWorld = new Point16(offsetMouseTile.X * 16, offsetMouseTile.Y * 16); 120 | var offsetMouseScreen = new Point16(offsetMouseWorld.X - screenRect.X, offsetMouseWorld.Y - screenRect.Y); 121 | 122 | var texture = WireMod.Instance.GetTexture($"Images/{dev.GetType().Name}"); 123 | 124 | var deviceScreenRect = new Rectangle(offsetMouseScreen.X, offsetMouseScreen.Y, dev.Width * 16, dev.Height * 16); 125 | 126 | spriteBatch.Draw(texture, deviceScreenRect, dev.GetSourceRect(0), Color.White * 0.5f); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /UI/ElectronicsVisionUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | using Terraria; 6 | using Terraria.UI; 7 | using WireMod.Devices; 8 | 9 | namespace WireMod.UI 10 | { 11 | public class ElectronicsVisionUI : UIState 12 | { 13 | public static bool Visible { get; set; } 14 | 15 | public const int Thickness = 4; 16 | 17 | public const float DeviceVisibility = 1f; 18 | public const float WireVisibility = 0.5f; 19 | 20 | public static Rectangle WireRect = new Rectangle(0, 0, 1, Thickness); 21 | 22 | 23 | protected override void DrawSelf(SpriteBatch spriteBatch) 24 | { 25 | DrawDevices(spriteBatch); 26 | DrawWires(spriteBatch); 27 | } 28 | 29 | private static void DrawDevices(SpriteBatch spriteBatch) 30 | { 31 | var screenRect = Helpers.GetScreenRect(); 32 | 33 | foreach (var device in WireMod.Devices) 34 | { 35 | if (!device.LocationWorldRect.Intersects(screenRect)) continue; 36 | 37 | var pos = (device.LocationWorld - Main.screenPosition) - Helpers.Drift; 38 | 39 | var texture = WireMod.Instance.GetTexture($"Images/{device.GetType().Name}"); 40 | spriteBatch.Draw(texture, pos, device.GetSourceRect(), Color.White * DeviceVisibility, 0f, new Vector2(0, 0), /*1 + ((Main.UIScale - 1) / 2)*/ 1f, SpriteEffects.None, 0f); 41 | device.Draw(spriteBatch); 42 | } 43 | } 44 | 45 | private static void DrawWires(SpriteBatch spriteBatch) 46 | { 47 | var screenRect = Helpers.GetScreenRect(); 48 | var modPlayer = Main.LocalPlayer.GetModPlayer(); 49 | 50 | foreach (var pin in WireMod.Pins) 51 | { 52 | if (pin.Type != "Out") continue; 53 | if (!pin.IsConnected()) continue; 54 | 55 | foreach (var p in ((PinOut)pin).ConnectedPins) 56 | { 57 | if (!screenRect.Contains(pin.Location.ToWorldCoordinates().ToPoint()) && !screenRect.Contains(p.Location.ToWorldCoordinates().ToPoint())) continue; 58 | 59 | var pinWire = pin.Wires.FirstOrDefault(w => w.StartPin == p || w.EndPin == p); 60 | if (pinWire == null) continue; 61 | 62 | var vis = WireVisibility; 63 | if (HoverDebuggerUI.Visible) 64 | { 65 | var hoverDevice = ((HoverDebuggerUI)WireMod.Instance.DebuggerHoverUserInterface.CurrentState).Device; 66 | if (pin.Device == hoverDevice || p.Device == hoverDevice) vis = 1f; 67 | } 68 | 69 | DrawWire(spriteBatch, pinWire, vis); 70 | } 71 | } 72 | 73 | // Draw trailing wire to mouse 74 | if (modPlayer.ConnectingPin == null) return; 75 | 76 | var placingPoints = modPlayer.PlacingWire.GetPoints(); 77 | 78 | if (placingPoints.Count > 1) 79 | { 80 | for (var i = 0; i < placingPoints.Count - 1; i++) 81 | { 82 | DrawLine( 83 | spriteBatch, 84 | placingPoints[i].ToWorldCoordinates() - Main.screenPosition - Helpers.Drift, 85 | placingPoints[i + 1].ToWorldCoordinates() - Main.screenPosition - Helpers.Drift, 86 | GetWireColor(modPlayer.ConnectingPin) 87 | ); 88 | } 89 | } 90 | 91 | DrawLine( 92 | spriteBatch, 93 | placingPoints.Last().ToWorldCoordinates() - Main.screenPosition - Helpers.Drift, 94 | Main.MouseScreen, 95 | GetWireColor(modPlayer.ConnectingPin) 96 | ); 97 | } 98 | 99 | private static void DrawWire(SpriteBatch spriteBatch, Wire wire, float visibility = WireVisibility) 100 | { 101 | var points = wire.GetPoints(); 102 | 103 | for (var i = 0; i < points.Count - 1; i++) 104 | { 105 | var start = points[i].ToWorldCoordinates() - Main.screenPosition - Helpers.Drift; 106 | var end = points[i + 1].ToWorldCoordinates() - Main.screenPosition - Helpers.Drift; 107 | 108 | if (start.X < end.X) 109 | { 110 | start.X -= (Thickness); 111 | start.Y -= (Thickness / 2); 112 | end.Y -= (Thickness / 2); 113 | } 114 | else if (start.X > end.X) 115 | { 116 | start.Y += (Thickness / 2); 117 | end.X -= (Thickness); 118 | end.Y += (Thickness / 2); 119 | } 120 | 121 | if (start.Y < end.Y) 122 | { 123 | start.Y -= (Thickness / 2); 124 | end.Y += (Thickness / 2); 125 | } 126 | else if (start.Y > end.Y) 127 | { 128 | start.X -= (Thickness); 129 | start.Y += (Thickness / 2); 130 | end.X -= (Thickness); 131 | end.Y -= (Thickness / 2); 132 | } 133 | 134 | start.X += (Thickness / 2); 135 | end.X += (Thickness / 2); 136 | 137 | DrawLine(spriteBatch, start, end, GetWireColor(wire.StartPin.Type == "Out" ? wire.StartPin : wire.EndPin) * visibility); 138 | } 139 | } 140 | 141 | private static void DrawLine(SpriteBatch spriteBatch, Vector2 start, Vector2 end, Color color) 142 | { 143 | var edge = end - start; 144 | 145 | WireRect.Width = (int)edge.Length(); 146 | WireRect.X = (int)start.X; 147 | WireRect.Y = (int)start.Y; 148 | 149 | spriteBatch.Draw(Main.magicPixel, WireRect, null, color, (float)Math.Atan2(edge.Y, edge.X), Vector2.Zero, SpriteEffects.None, 1f); 150 | } 151 | 152 | private static Color GetWireColor(Pin pin) 153 | { 154 | switch (pin.DataType) 155 | { 156 | case "bool": 157 | if (pin.GetValue() == "0") return Color.Red; 158 | if (pin.GetValue() == "1") return Color.Green; 159 | break; 160 | case "int": 161 | return Color.Yellow; 162 | case "string": 163 | return Color.Blue; 164 | } 165 | 166 | return Color.White; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /UI/Elements/DragableUIPanel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Terraria; 3 | using Terraria.GameContent.UI.Elements; 4 | using Terraria.UI; 5 | 6 | namespace WireMod.UI.Elements 7 | { 8 | public class DragableUIPanel : UIPanel 9 | { 10 | private Vector2 _offset; 11 | public bool Dragging; 12 | 13 | public override void MouseDown(UIMouseEvent evt) 14 | { 15 | base.MouseDown(evt); 16 | this.DragStart(evt); 17 | } 18 | 19 | public override void MouseUp(UIMouseEvent evt) 20 | { 21 | base.MouseUp(evt); 22 | this.DragEnd(evt); 23 | } 24 | 25 | private void DragStart(UIMouseEvent evt) 26 | { 27 | this._offset = new Vector2(evt.MousePosition.X - this.Left.Pixels, evt.MousePosition.Y - this.Top.Pixels); 28 | this.Dragging = true; 29 | } 30 | 31 | private void DragEnd(UIMouseEvent evt) 32 | { 33 | var end = evt.MousePosition; 34 | this.Dragging = false; 35 | 36 | this.Left.Set(end.X - this._offset.X, 0f); 37 | this.Top.Set(end.Y - this._offset.Y, 0f); 38 | 39 | Recalculate(); 40 | } 41 | 42 | public override void Update(GameTime gameTime) 43 | { 44 | base.Update(gameTime); 45 | 46 | if (this.ContainsPoint(Main.MouseScreen)) 47 | { 48 | Main.LocalPlayer.mouseInterface = true; 49 | } 50 | 51 | if (this.Dragging) 52 | { 53 | Left.Set(Main.mouseX - this._offset.X, 0f); 54 | Top.Set(Main.mouseY - this._offset.Y, 0f); 55 | Recalculate(); 56 | } 57 | 58 | var parentSpace = this.Parent.GetDimensions().ToRectangle(); 59 | if (this.GetDimensions().ToRectangle().Intersects(parentSpace)) return; 60 | 61 | this.Left.Pixels = Utils.Clamp(this.Left.Pixels, 0, parentSpace.Right - this.Width.Pixels); 62 | this.Top.Pixels = Utils.Clamp(this.Top.Pixels, 0, parentSpace.Bottom - this.Height.Pixels); 63 | 64 | Recalculate(); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /UI/Elements/UIClickableButton.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Terraria.GameContent.UI.Elements; 3 | using Terraria.UI; 4 | 5 | namespace WireMod.UI.Elements 6 | { 7 | public class UIClickableButton : UIElement 8 | { 9 | private object _text; 10 | private UIText _uiText; 11 | private readonly MouseEvent _clickAction; 12 | 13 | public string Text 14 | { 15 | get => this._uiText?.Text ?? string.Empty; 16 | set => this._text = value; 17 | } 18 | 19 | public UIClickableButton(object text, MouseEvent clickAction) 20 | { 21 | this._text = text?.ToString() ?? string.Empty; 22 | this._clickAction = clickAction; 23 | } 24 | 25 | public override void OnInitialize() 26 | { 27 | var uiPanel = new UIPanel 28 | { 29 | Width = StyleDimension.Fill, 30 | Height = StyleDimension.Fill 31 | }; 32 | 33 | this.Append(uiPanel); 34 | 35 | this._uiText = new UIText(""); 36 | this._uiText.VAlign = this._uiText.HAlign = 0.5f; 37 | uiPanel.Append(this._uiText); 38 | 39 | uiPanel.OnClick += this._clickAction; 40 | } 41 | 42 | public override void Update(GameTime gameTime) 43 | { 44 | if (this._text == null) return; 45 | 46 | this._uiText.SetText(_text.ToString()); 47 | this._text = null; 48 | 49 | Recalculate(); 50 | 51 | this.MinWidth.Set(this._uiText.MinWidth.Pixels + 20, 0f); 52 | this.MinHeight.Set(this._uiText.MinHeight.Pixels + 20, 0f); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /UI/Elements/UIInputTextField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | using Microsoft.Xna.Framework.Graphics; 4 | using Terraria; 5 | using Terraria.GameInput; 6 | using Terraria.UI; 7 | 8 | namespace WireMod.UI.Elements 9 | { 10 | internal class UIInputTextField : UIElement 11 | { 12 | public delegate void EventHandler(object sender, EventArgs e); 13 | public event EventHandler OnTextChange; 14 | public event EventHandler OnSave; 15 | public event EventHandler OnCancel; 16 | 17 | private readonly string _hintText; 18 | private string _currentString; 19 | private int _textBlinkerCount; 20 | 21 | public string Text 22 | { 23 | get => _currentString; 24 | set 25 | { 26 | if (_currentString == value) return; 27 | _currentString = value; 28 | this.OnTextChange?.Invoke(this, EventArgs.Empty); 29 | } 30 | } 31 | 32 | public UIInputTextField(string value, string hintText) 33 | { 34 | this._currentString = value; 35 | this._hintText = hintText; 36 | } 37 | 38 | protected override void DrawSelf(SpriteBatch spriteBatch) 39 | { 40 | Main.blockInput = true; 41 | PlayerInput.WritingText = true; 42 | Main.instance.HandleIME(); 43 | 44 | var inputText = Main.GetInputText(_currentString); 45 | 46 | if (inputText != _currentString) 47 | { 48 | this._currentString = inputText; 49 | this.OnTextChange?.Invoke(this, EventArgs.Empty); 50 | } 51 | 52 | var dimensions = this.GetDimensions(); 53 | 54 | if (this._currentString.Length == 0) 55 | { 56 | Utils.DrawBorderString(spriteBatch, this._hintText, new Vector2(dimensions.X, dimensions.Y), Color.Gray); 57 | } 58 | else 59 | { 60 | var text = _currentString; 61 | 62 | if (++_textBlinkerCount / 20 % 2 == 0) 63 | { 64 | text += "|"; 65 | } 66 | 67 | Utils.DrawBorderString(spriteBatch, text, new Vector2(dimensions.X, dimensions.Y), Color.White); 68 | } 69 | 70 | if (Main.inputTextEnter) 71 | { 72 | this.OnSave?.Invoke(this, EventArgs.Empty); 73 | } 74 | 75 | if (Main.inputTextEscape) 76 | { 77 | this.OnCancel?.Invoke(this, EventArgs.Empty); 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /UI/HoverDebuggerUI.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Terraria; 6 | using Terraria.UI; 7 | using Terraria.UI.Chat; 8 | using WireMod.Devices; 9 | 10 | namespace WireMod.UI 11 | { 12 | internal class HoverDebuggerUI : UIState 13 | { 14 | public static bool Visible { get; set; } 15 | 16 | public readonly Device Device; 17 | public readonly Pin Pin; 18 | 19 | private readonly List<(string Line, Color Color, float Size)> _lines; 20 | 21 | public HoverDebuggerUI(Device device, Pin pin = null) 22 | { 23 | this.Device = device; 24 | this.Pin = pin; 25 | this._lines = device.Debug(pin); 26 | } 27 | 28 | protected override void DrawSelf(SpriteBatch spriteBatch) 29 | { 30 | base.DrawSelf(spriteBatch); 31 | 32 | if (this._lines == null) return; 33 | 34 | const int padding = 10; 35 | const int border = 2; 36 | 37 | var width = (int)this._lines.Max(t => (int)ChatManager.GetStringSize(Main.fontMouseText, t.Line, Vector2.One).X * t.Size) + padding * 2; 38 | var height = (int)this._lines.ToList().Sum(t => Main.fontMouseText.MeasureString(t.Line).Y) + padding * 2; 39 | 40 | var x = (Main.screenWidth - width) / 2; 41 | var y = Main.screenHeight - height - 10; 42 | 43 | spriteBatch.Draw(Main.magicPixel, new Rectangle(x, y, width + border * 2, height + border * 2), null, Color.Black, 0f, new Vector2(0, 0), SpriteEffects.None, 0f); 44 | spriteBatch.Draw(Main.magicPixel, new Rectangle(x + border, y + border, width, height), null, Color.White, 0f, new Vector2(0, 0), SpriteEffects.None, 0f); 45 | 46 | var i = 0; 47 | foreach ((string line, Color color, float size) in this._lines) 48 | { 49 | var lineHeight = Main.fontMouseText.MeasureString(line).Y; 50 | Utils.DrawBorderStringFourWay(spriteBatch, Main.fontMouseText, line, x + padding, y + padding + (lineHeight * i) + padding, color, Color.White, new Vector2(0.3f), size); 51 | i++; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UI/UserInputUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Xna.Framework; 3 | using Terraria; 4 | using Terraria.UI; 5 | using WireMod.UI.Elements; 6 | 7 | namespace WireMod.UI 8 | { 9 | internal class UserInputUI : UIState 10 | { 11 | public static bool Visible { get; set; } 12 | 13 | public delegate void EventHandler(object sender, EventArgs e); 14 | public event EventHandler OnSave; 15 | 16 | public string Value { get; set; } 17 | 18 | private DragableUIPanel _panel; 19 | public float OffsetX = 0f; 20 | public float OffsetY = -200f; 21 | public float PanelWidth = 200f; 22 | public float PanelHeight = 100f; 23 | 24 | public UserInputUI(string value) 25 | { 26 | this.Value = value; 27 | } 28 | 29 | public override void OnInitialize() 30 | { 31 | this._panel = new DragableUIPanel(); 32 | this._panel.Left.Set(((Main.screenWidth - this.PanelWidth) / 2) + this.OffsetX, 0f); 33 | this._panel.Top.Set(((Main.screenHeight - this.PanelHeight) / 2) + this.OffsetY, 0f); 34 | this._panel.Width.Set(this.PanelWidth, 0f); 35 | this._panel.Height.Set(this.PanelHeight, 0f); 36 | this._panel.BorderColor = new Color(0, 0, 0, 0); 37 | 38 | var textbox = new UIInputTextField(this.Value, "Enter a value"); 39 | textbox.OnTextChange += (s, e) => { this.Value = textbox.Text; }; 40 | textbox.OnSave += (sender, args) => this.Save(); 41 | textbox.OnCancel += (sender, args) => this.Close(); 42 | 43 | var button = new UIClickableButton("Save", (evt, element) => this.Save()); 44 | 45 | this._panel.Append(textbox); 46 | 47 | button.Top.Set(50f, 0f); 48 | 49 | this._panel.Append(button); 50 | 51 | this.Append(this._panel); 52 | } 53 | 54 | private void Save() 55 | { 56 | this.OnSave?.Invoke(this, EventArgs.Empty); 57 | this.Close(); 58 | } 59 | 60 | private void Close() 61 | { 62 | Main.blockInput = false; 63 | WireMod.Instance.UserInputUserInterface.SetState(null); 64 | Visible = false; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /WireMod.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Terraria; 6 | using Terraria.DataStructures; 7 | using Terraria.ID; 8 | using Terraria.ModLoader; 9 | using Terraria.UI; 10 | using WireMod.Devices; 11 | using WireMod.UI; 12 | 13 | namespace WireMod 14 | { 15 | internal class WireMod : Mod 16 | { 17 | internal static WireMod Instance; 18 | internal const bool Debug = true; 19 | internal const float SmallText = 0.75f; 20 | internal static DevicePacketHandler PacketHandler = new DevicePacketHandler(); 21 | 22 | internal UserInterface ElectronicsManualUserInterface; 23 | internal UserInterface ElectronicsVisionUserInterface; 24 | internal UserInterface DebuggerUserInterface; 25 | internal UserInterface DebuggerHoverUserInterface; 26 | internal UserInterface UserInputUserInterface; 27 | 28 | internal ElectronicsManualUI ElectronicsManualUI = new ElectronicsManualUI(); 29 | internal ElectronicsVisionUI ElectronicsVisionUI = new ElectronicsVisionUI(); 30 | 31 | public WireMod() 32 | { 33 | Instance = this; 34 | } 35 | 36 | public override void Load() 37 | { 38 | Terraria.ModLoader.IO.TagSerializer.AddSerializer(new DeviceSerializer()); 39 | 40 | this.AddGlobalTile("IndestructibleTile", new WireModGlobalTile()); 41 | 42 | if (Main.netMode == NetmodeID.Server) return; 43 | 44 | this.ElectronicsManualUserInterface = new UserInterface(); 45 | this.ElectronicsVisionUserInterface = new UserInterface(); 46 | this.DebuggerUserInterface = new UserInterface(); 47 | this.DebuggerHoverUserInterface = new UserInterface(); 48 | this.UserInputUserInterface = new UserInterface(); 49 | 50 | this.ElectronicsManualUI.Activate(); 51 | this.ElectronicsManualUserInterface.SetState(this.ElectronicsManualUI); 52 | 53 | this.ElectronicsVisionUI.Activate(); 54 | this.ElectronicsVisionUserInterface.SetState(this.ElectronicsVisionUI); 55 | } 56 | 57 | public override void UpdateUI(GameTime gameTime) 58 | { 59 | foreach (var device in Devices) device.Update(gameTime); 60 | 61 | // TODO: Fix this bullshit 62 | //foreach (var device in Devices.Where(d => d.Pins["Out"].Count > 0)) device.Pins["Out"][0].GetValue(); 63 | 64 | if (Main.LocalPlayer.name != "") 65 | { 66 | if (Main.LocalPlayer.HeldItem.Name != "Electronics Manual") 67 | { 68 | // Reset 69 | ElectronicsManualUI.Visible = false; 70 | ElectronicsVisionUI.Visible = false; 71 | DebuggerUI.Visible = false; 72 | 73 | var modPlayer = Main.LocalPlayer.GetModPlayer(); 74 | modPlayer.ToolCategoryMode = 0; 75 | modPlayer.ToolMode = 0; 76 | } 77 | } 78 | 79 | if (Main.netMode == NetmodeID.Server) return; 80 | 81 | if (DebuggerUI.Visible) 82 | { 83 | this.DebuggerUserInterface.Update(gameTime); 84 | } 85 | 86 | if (HoverDebuggerUI.Visible) 87 | { 88 | this.DebuggerHoverUserInterface.Update(gameTime); 89 | } 90 | 91 | if (ElectronicsManualUI.Visible) 92 | { 93 | this.ElectronicsManualUserInterface.Update(gameTime); 94 | } 95 | 96 | if (ElectronicsVisionUI.Visible) 97 | { 98 | this.ElectronicsVisionUserInterface.Update(gameTime); 99 | } 100 | 101 | if (UserInputUI.Visible) 102 | { 103 | this.UserInputUserInterface.Update(gameTime); 104 | } 105 | } 106 | 107 | public override void ModifyInterfaceLayers(List layers) 108 | { 109 | if (Main.netMode == NetmodeID.Server) return; 110 | 111 | var mouseTextIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Mouse Text")); 112 | if (mouseTextIndex != -1) 113 | { 114 | layers.Insert(mouseTextIndex - 1, new LegacyGameInterfaceLayer( 115 | "WireMod: Devices", 116 | delegate 117 | { 118 | if (ElectronicsVisionUI.Visible) 119 | { 120 | this.ElectronicsVisionUserInterface?.Draw(Main.spriteBatch, new GameTime()); 121 | } 122 | 123 | if (ElectronicsManualUI.Visible) 124 | { 125 | this.ElectronicsManualUserInterface?.Draw(Main.spriteBatch, new GameTime()); 126 | //ElectronicsManualUI.Visible = false; 127 | } 128 | 129 | if (DebuggerUI.Visible) 130 | { 131 | this.DebuggerUserInterface?.Draw(Main.spriteBatch, new GameTime()); 132 | } 133 | 134 | if (HoverDebuggerUI.Visible) 135 | { 136 | this.DebuggerHoverUserInterface?.Draw(Main.spriteBatch, new GameTime()); 137 | HoverDebuggerUI.Visible = false; 138 | } 139 | 140 | if (UserInputUI.Visible) 141 | { 142 | this.UserInputUserInterface?.Draw(Main.spriteBatch, new GameTime()); 143 | } 144 | 145 | return true; 146 | }, 147 | InterfaceScaleType.UI) 148 | ); 149 | } 150 | } 151 | 152 | public override void HandlePacket(BinaryReader reader, int from) => PacketHandler.HandlePacket(reader, from); 153 | 154 | 155 | public (float X, float Y) GetMouseWorldPosition() 156 | { 157 | return ( 158 | Main.mouseX + Main.screenPosition.X, 159 | Main.mouseY + Main.screenPosition.Y 160 | ); 161 | } 162 | 163 | public (int X, int Y) GetMouseTilePosition() 164 | { 165 | var (x, y) = this.GetMouseWorldPosition(); 166 | 167 | return ( 168 | (int)(x / 16), 169 | (int)(y / 16) 170 | ); 171 | } 172 | 173 | public static List Devices = new List(); 174 | public static List Pins = new List(); 175 | 176 | #region Device Functions 177 | public static Device GetDevice(int x, int y) 178 | { 179 | return Devices.FirstOrDefault(d => d.LocationRect.Intersects(new Rectangle(x, y, 1, 1))); 180 | } 181 | 182 | public static Pin GetDevicePin(Point16 location) => GetDevicePin(location.X, location.Y); 183 | public static Pin GetDevicePin(int x, int y) 184 | { 185 | return Pins.FirstOrDefault(p => p.Location.X == x && p.Location.Y == y); 186 | } 187 | 188 | public static bool CanPlace(Device device, int x, int y) 189 | { 190 | // Check for devices and pins in the new device destination area 191 | return !Devices.Any(d => d.LocationRect.Intersects(new Rectangle( 192 | x - device.Origin.X, 193 | y - device.Origin.Y, 194 | device.Width, 195 | device.Height 196 | ))); 197 | } 198 | 199 | public static void PlaceDevice(Device device, int x, int y) 200 | { 201 | // Check if the target area is clear of other devices 202 | if (!CanPlace(device, x, y)) return; 203 | 204 | // Add to arrays 205 | device.SetLocation(x - device.Origin.X, y - device.Origin.Y); 206 | device.SetPins(); 207 | Devices.Add(device); 208 | 209 | foreach (var pinDesign in device.PinLayout) 210 | { 211 | var pin = device.Pins[pinDesign.Type][pinDesign.Num]; 212 | pin.Location = new Point16(device.LocationRect.X + pinDesign.Offset.X, device.LocationRect.Y + pinDesign.Offset.Y); 213 | Pins.Add(pin); 214 | } 215 | 216 | device.OnPlace(); 217 | } 218 | 219 | public static void RemoveDevice(int x, int y) 220 | { 221 | var device = Devices.FirstOrDefault(d => d.LocationRect.Contains(x, y)); 222 | 223 | if (device == null) return; 224 | 225 | RemoveDevice(device); 226 | } 227 | public static void RemoveDevice(Device device) 228 | { 229 | device.OnKill(); 230 | 231 | // Disconnect from connected pins 232 | foreach (var pin in device.Pins.Values.SelectMany(p => p.Values)) 233 | { 234 | pin.Disconnect(); 235 | Pins.Remove(pin); 236 | } 237 | 238 | Devices.Remove(device); 239 | } 240 | #endregion 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /WireMod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WireMod 6 | net45 7 | x86 8 | latest 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /WireMod.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True 11 | True 12 | True 13 | -------------------------------------------------------------------------------- /WireMod.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Program 5 | C:\Program Files %28x86%29\Steam\steamapps\common\Terraria\Terraria.exe 6 | C:\Program Files %28x86%29\Steam\steamapps\common\Terraria\ 7 | 8 | 9 | -------------------------------------------------------------------------------- /WireMod.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2042 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMod", "WireMod.csproj", "{8298EAB6-0586-4BDA-9483-83624B66B13A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8298EAB6-0586-4BDA-9483-83624B66B13A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8298EAB6-0586-4BDA-9483-83624B66B13A}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8298EAB6-0586-4BDA-9483-83624B66B13A}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8298EAB6-0586-4BDA-9483-83624B66B13A}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6F5253C4-80F6-4FCF-B486-B9776D3A9388} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /WireModGlobalTile.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.Xna.Framework; 3 | using Terraria.DataStructures; 4 | using Terraria.ModLoader; 5 | using WireMod.Devices; 6 | 7 | namespace WireMod 8 | { 9 | internal class WireModGlobalTile : GlobalTile 10 | { 11 | public override bool CanPlace(int i, int j, int type) 12 | { 13 | var protect = WireMod.Devices.Any(d => d is ProtectArea dev && (((TileArea)dev.GetProtectPlaceArea())?.Contains(new Vector2(i, j)) ?? false)); 14 | 15 | return !protect && base.CanPlace(i, j, type); 16 | } 17 | 18 | public override bool CanKillTile(int i, int j, int type, ref bool blockDamaged) 19 | { 20 | var protect = WireMod.Devices.Any(d => d is ProtectArea dev && (((TileArea)dev.GetProtectDestroyArea())?.Contains(new Vector2(i, j)) ?? false)); 21 | 22 | return !protect && base.CanKillTile(i, j, type, ref blockDamaged); 23 | } 24 | 25 | public override void HitWire(int i, int j, int type) 26 | { 27 | WireMod.GetDevice(i, j)?.OnHitWire(WireMod.GetDevicePin(i, j)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WireModPlayer.cs: -------------------------------------------------------------------------------- 1 | using Terraria; 2 | using Terraria.ID; 3 | using Terraria.ModLoader; 4 | using Terraria.ModLoader.IO; 5 | using WireMod.Devices; 6 | 7 | namespace WireMod 8 | { 9 | public class WireModPlayer : ModPlayer 10 | { 11 | public Device PlacingDevice; 12 | public Pin ConnectingPin; 13 | public Wire PlacingWire; 14 | 15 | public bool ShowPreview; 16 | public int ToolCategoryMode = 0; 17 | public int ToolMode = 0; 18 | 19 | public override void OnEnterWorld(Player p) 20 | { 21 | base.OnEnterWorld(p); 22 | 23 | // Send sync request 24 | if (Main.netMode == NetmodeID.MultiplayerClient) 25 | { 26 | WireMod.PacketHandler.SendRequest(256, Main.myPlayer); 27 | } 28 | } 29 | 30 | public override TagCompound Save() 31 | { 32 | return new TagCompound 33 | { 34 | ["menuPosX"] = WireMod.Instance.ElectronicsManualUI.Panel.Left.Pixels, 35 | ["menuPosY"] = WireMod.Instance.ElectronicsManualUI.Panel.Top.Pixels, 36 | }; 37 | } 38 | public override void Load(TagCompound tag) 39 | { 40 | WireMod.Instance.ElectronicsManualUI.Panel.Left.Set(tag.GetFloat("menuPosX"), 0f); 41 | WireMod.Instance.ElectronicsManualUI.Panel.Top.Set(tag.GetFloat("menuPosY"), 0f); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /WireModWorld.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Terraria.DataStructures; 4 | using Terraria.ModLoader; 5 | using Terraria.ModLoader.IO; 6 | using WireMod.Devices; 7 | 8 | namespace WireMod 9 | { 10 | public class WireModWorld : ModWorld 11 | { 12 | public override TagCompound Save() 13 | { 14 | var wires = WireMod.Pins.Where(p => p.Type == "In" && p.IsConnected()).Select(p => new TagCompound 15 | { 16 | ["src"] = p.Location, 17 | ["dest"] = ((PinIn)p).ConnectedPin.Location, 18 | ["points"] = p.GetWire(((PinIn)p).ConnectedPin).GetPoints(p, false), 19 | }).ToList(); 20 | 21 | return new TagCompound 22 | { 23 | ["devices"] = WireMod.Devices, 24 | ["wires"] = wires 25 | }; 26 | } 27 | public override void Load(TagCompound tag) 28 | { 29 | foreach (var device in tag.Get>("devices")) 30 | { 31 | if (WireMod.Debug) mod.Logger.Info($"Loading device \"{device.Name}\": X {device.LocationRect.X}, Y: {device.LocationRect.Y}"); 32 | 33 | WireMod.PlaceDevice(device, device.LocationRect.X, device.LocationRect.Y); 34 | } 35 | 36 | foreach (var conn in tag.GetList("wires")) 37 | { 38 | var src = WireMod.GetDevicePin(conn.Get("src")); 39 | var dest = WireMod.GetDevicePin(conn.Get("dest")); 40 | if (src == null || dest == null) continue; 41 | 42 | var points = conn.GetList("points").ToList(); 43 | var wire = new Wire(src, dest, points); 44 | 45 | src.Connect(dest, wire); 46 | dest.Connect(src, wire); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /build.txt: -------------------------------------------------------------------------------- 1 | author = matlomax 2 | version = 0.9.5 3 | displayName = WireMod 4 | homepage = https://github.com/MatLomax/WireMod/wiki -------------------------------------------------------------------------------- /description.txt: -------------------------------------------------------------------------------- 1 | Wires, revisited! 2 | 3 | Still in Beta, so expect bugs, and expect for the UI to kinda suck. 4 | 5 | See the project wiki for detailed information on what this mod does. -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatLomax/WireMod/f096495cbf70224a8ddf187aab513d76f5bd2716/icon.png --------------------------------------------------------------------------------