public void LockChest(DPoint anyChestTileLocation) { ITile chestTile = TerrariaUtils.Tiles[anyChestTileLocation]; if (!chestTile.active() || (chestTile.type != TileID.Containers && chestTile.type != TileID.Containers2)) throw new ArgumentException("Tile is not a chest.", nameof(anyChestTileLocation)); bool isLocked; ChestStyle chestStyle = this.GetChestStyle(chestTile, out isLocked); if (isLocked) throw new InvalidChestStyleException("Chest is already locked.", chestStyle); if (!this.IsChestStyleLockable(chestStyle)) throw new InvalidChestStyleException("Chest has to be a lockable chest.", chestStyle); ObjectMeasureData measureData = this.MeasureObject(anyChestTileLocation); if ( chestStyle == ChestStyle.JungleChest || chestStyle == ChestStyle.CorruptionChest || chestStyle == ChestStyle.CrimsonChest || chestStyle == ChestStyle.HallowedChest || chestStyle == ChestStyle.FrozenChest) { foreach (ITile tile in this.EnumerateObjectTiles(measureData)) tile.frameX += 180; } else { foreach (ITile tile in this.EnumerateObjectTiles(measureData)) tile.frameX += 36; } TSPlayer.All.SendTileSquare(anyChestTileLocation, 4); }
public CircuitProcessor(PluginTrace pluginTrace, CircuitHandler circuitHandler, ObjectMeasureData senderMeasureData) { Contract.Requires<ArgumentNullException>(pluginTrace != null); Contract.Requires<ArgumentNullException>(circuitHandler != null); this.PluginTrace = pluginTrace; this.CircuitHandler = circuitHandler; this.SenderMeasureData = senderMeasureData; // Is sender directly wired? bool isSenderWired; if (this.SenderMeasureData.BlockType == BlockType.DoorOpened) { isSenderWired = false; for (int y = this.SenderMeasureData.OriginTileLocation.Y; y < this.SenderMeasureData.OriginTileLocation.Y + this.SenderMeasureData.Size.Y; y++) { if (TerrariaUtils.Tiles[this.SenderMeasureData.OriginTileLocation.X, y].HasWire()) { isSenderWired = true; break; } } } else { isSenderWired = TerrariaUtils.Tiles.IsObjectWired(this.SenderMeasureData); } this.result = new CircuitProcessingResult { IsAdvancedCircuit = !isSenderWired, SenderLocation = this.SenderMeasureData.OriginTileLocation }; }
public bool IsObjectWired(ObjectMeasureData measureData, WireColor wireColor, out DPoint firstWireLocation) { foreach (DPoint tileLocation in this.EnumerateObjectTileLocations(measureData)) { ITile tile = TerrariaUtils.Tiles[tileLocation]; if (tile.HasWire(wireColor)) { firstWireLocation = tileLocation; return true; } } firstWireLocation = DPoint.Empty; return false; }
public IEnumerable<DPoint> EnumerateObjectTileLocations(ObjectMeasureData measureData) { DPoint origin = measureData.OriginTileLocation; if ( measureData.BlockType == BlockType.DoorOpened && this.GetDoorDirection(measureData.OriginTileLocation) == Direction.Left ) { origin.Offset(-1, 0); } for (int x = origin.X; x < origin.X + measureData.Size.X; x++) for (int y = origin.Y; y < origin.Y + measureData.Size.Y; y++) yield return new DPoint(x, y); }
public IEnumerable<DPoint> EnumerateObjectTileLocations(ObjectMeasureData measureData) { DPoint origin = measureData.OriginTileLocation; if ( (measureData.BlockType == TileID.OpenDoor && this.GetDoorDirection(measureData.OriginTileLocation) == Direction.Left) || (measureData.BlockType == TileID.TrapdoorOpen && this.GetDoorDirection(measureData.OriginTileLocation) == Direction.Down ) ) { origin.Offset(-1, 0); } for (int x = origin.X; x < origin.X + measureData.Size.X; x++) for (int y = origin.Y; y < origin.Y + measureData.Size.Y; y++) yield return new DPoint(x, y); }
public bool ObjectHasActiveState(ObjectMeasureData measureData) { ITile tile = TerrariaUtils.Tiles[measureData.OriginTileLocation]; switch (measureData.BlockType) { case TileID.Switches: return (tile.frameY == 0); case TileID.Timers: case TileID.WaterFountain: return (tile.frameY != 0); case TileID.MusicBoxes: return (tile.frameX != 0); case TileID.Torches: return (tile.frameX < 66); case TileID.HolidayLights: return (tile.frameX < 54); default: return (tile.frameX == 0); } }
public void RegisterUnregisterTimer(TSPlayer triggeringPlayer, ObjectMeasureData measureData, bool register) { bool alreadyRegistered = this.WorldMetadata.ActiveTimers.ContainsKey(measureData.OriginTileLocation); if (register) { if (!alreadyRegistered) { this.WorldMetadata.ActiveTimers.Add( measureData.OriginTileLocation, new ActiveTimerMetadata(AdvancedCircuits.MeasureTimerFrameTime(measureData.OriginTileLocation), triggeringPlayer.Name) ); } } else if (alreadyRegistered) { this.WorldMetadata.ActiveTimers.Remove(measureData.OriginTileLocation); } }
private IEnumerable<ProtectionEntry> EnumProtectionEntriesOnTopOfObject(ObjectMeasureData measureData) { for (int rx = 0; rx < measureData.Size.X; rx++) { DPoint absoluteLocation = measureData.OriginTileLocation.OffsetEx(rx, -1); Tile topTile = TerrariaUtils.Tiles[absoluteLocation]; if ( topTile.type == (int)BlockType.Bottle || topTile.type == (int)BlockType.Chest || topTile.type == (int)BlockType.Candle || topTile.type == (int)BlockType.WaterCandle || topTile.type == (int)BlockType.Book || topTile.type == (int)BlockType.ClayPot || topTile.type == (int)BlockType.Bed || topTile.type == (int)BlockType.SkullLantern || topTile.type == (int)BlockType.TrashCan_UNUSED || topTile.type == (int)BlockType.Candelabra || topTile.type == (int)BlockType.Bowl || topTile.type == (int)BlockType.CrystalBall ) { lock (this.WorldMetadata.Protections) { ProtectionEntry protection; if (this.WorldMetadata.Protections.TryGetValue(absoluteLocation, out protection)) yield return protection; } } } }
public void SetObjectState( ObjectMeasureData measureData, bool activeState, bool sendTileSquare = true ) { #if DEBUG if (this.ObjectHasActiveState(measureData) == activeState) { throw new ArgumentException(string.Format( "The object \"{0}\" does already have the state \"{1}\".", TerrariaUtils.Tiles.GetBlockTypeName(measureData.BlockType), activeState )); } #endif int originX = measureData.OriginTileLocation.X; int originY = measureData.OriginTileLocation.Y; int objectWidth = measureData.Size.X; int objectHeight = measureData.Size.Y; int newFrameXOffset = 0; int newFrameYOffset = 0; if (measureData.BlockType == BlockType.Torch || measureData.BlockType == BlockType.XMasLight) newFrameXOffset = measureData.TextureTileSize.X * 2; if ( measureData.BlockType != BlockType.Switch && measureData.BlockType != BlockType.XSecondTimer && measureData.BlockType != BlockType.WaterFountain ) { int frameXOffset = (objectWidth * measureData.TextureTileSize.X) + newFrameXOffset; if (measureData.BlockType == BlockType.MusicBox) frameXOffset = -frameXOffset; if (activeState) newFrameXOffset = (short)-frameXOffset; else newFrameXOffset = (short)frameXOffset; if (measureData.BlockType == BlockType.BubbleMachine && !activeState) newFrameYOffset = -TerrariaUtils.Tiles[measureData.OriginTileLocation].frameY; } else { int frameYOffset = (objectHeight * measureData.TextureTileSize.Y); if (measureData.BlockType == BlockType.WaterFountain && !activeState) { newFrameYOffset = -TerrariaUtils.Tiles[measureData.OriginTileLocation].frameY; } else { if ( measureData.BlockType == BlockType.XSecondTimer || measureData.BlockType == BlockType.WaterFountain ) activeState = !activeState; if (activeState) newFrameYOffset = (short)-frameYOffset; else newFrameYOffset = (short)frameYOffset; } } for (int tx = 0; tx < objectWidth; tx++) { for (int ty = 0; ty < objectHeight; ty++) { int absoluteX = originX + tx; int absoluteY = originY + ty; TerrariaUtils.Tiles[absoluteX, absoluteY].frameX += (short)newFrameXOffset; TerrariaUtils.Tiles[absoluteX, absoluteY].frameY += (short)newFrameYOffset; } } if (sendTileSquare) TSPlayer.All.SendTileSquareEx(originX, originY, Math.Max(objectWidth, objectHeight)); }
public bool ObjectHasActiveState(ObjectMeasureData measureData) { Tile tile = TerrariaUtils.Tiles[measureData.OriginTileLocation]; switch (measureData.BlockType) { case BlockType.Switch: return (tile.frameY == 0); case BlockType.XSecondTimer: case BlockType.WaterFountain: return (tile.frameY != 0); case BlockType.MusicBox: return (tile.frameX != 0); case BlockType.Torch: return (tile.frameX < 66); case BlockType.XMasLight: return (tile.frameX < 54); default: return (tile.frameX == 0); } }
public IEnumerable<ITile> EnumerateObjectTiles(ObjectMeasureData measureData) { foreach (DPoint tileLocation in this.EnumerateObjectTileLocations(measureData)) yield return TerrariaUtils.Tiles[tileLocation]; }
public static bool IsComponentWiredByPort(ObjectMeasureData measureData) { return AdvancedCircuits.IsComponentWiredByPort(measureData.OriginTileLocation, measureData.Size); }
protected bool SignalComponent(ref ObjectMeasureData measureData, RootBranchProcessData rootBranch, SignalType signal, bool localOnly = false) { int originX = measureData.OriginTileLocation.X; int originY = measureData.OriginTileLocation.Y; switch (measureData.BlockType) { case BlockType.Torch: case BlockType.XMasLight: case BlockType.Candle: case BlockType.PlatinumCandle: case BlockType.ChainLantern: case BlockType.ChineseLantern: case BlockType.Candelabra: case BlockType.PlatinumCandelabra: case BlockType.DiscoBall: case BlockType.TikiTorch: case BlockType.CopperChandelier: case BlockType.SilverChandelier: case BlockType.GoldChandelier: case BlockType.PlatinumChandelier: case BlockType.LampPost: case BlockType.MusicBox: case BlockType.XSecondTimer: case BlockType.WaterFountain: case BlockType.BubbleMachine: { bool currentState = TerrariaUtils.Tiles.ObjectHasActiveState(measureData); bool newState; if (signal == SignalType.Swap) newState = !currentState; else newState = AdvancedCircuits.SignalToBool(signal).Value; if (measureData.BlockType == BlockType.XSecondTimer) { // Directly wired Timers in an Advanced Circuit are not meant to be switched. if (this.IsAdvancedCircuit) return false; if (newState != currentState) this.CircuitHandler.RegisterUnregisterTimer(this.TriggeringPlayer, measureData, newState); } if (newState != currentState) TerrariaUtils.Tiles.SetObjectState(measureData, newState, !localOnly); return true; } case BlockType.ActiveStone: case BlockType.InactiveStone: { bool currentState = (measureData.BlockType == BlockType.ActiveStone); bool newState; if (signal == SignalType.Swap) newState = !currentState; else newState = AdvancedCircuits.SignalToBool(signal).Value; if (newState != currentState) { BlockType newBlockType; if (newState) newBlockType = BlockType.ActiveStone; else newBlockType = BlockType.InactiveStone; TerrariaUtils.Tiles[measureData.OriginTileLocation].type = (byte)newBlockType; WorldGen.SquareTileFrame(originX, originY); TSPlayer.All.SendTileSquareEx(originX, originY, 1); } return true; } case BlockType.DoorClosed: case BlockType.DoorOpened: { if (this.IsAdvancedCircuit) return false; this.OpenDoor(measureData, signal); return true; } case BlockType.InletPump: case BlockType.OutletPump: { if (signal == SignalType.Off) return false; PumpConfig pumpConfig; PaintColor componentPaint = (PaintColor)TerrariaUtils.Tiles[measureData.OriginTileLocation].color(); if ( ( this.CircuitHandler.Config.PumpConfigs.TryGetValue(componentPaint, out pumpConfig) || this.CircuitHandler.Config.PumpConfigs.TryGetValue(PaintColor.None, out pumpConfig) ) && pumpConfig.Cooldown == 0 || WorldGen.checkMech(originX, originY, pumpConfig.Cooldown ) ) { if (this.Result.SignaledPumps > this.CircuitHandler.Config.MaxPumpsPerCircuit) { this.Result.WarnReason = CircuitWarnReason.SignalesTooManyPumps; return true; } if ( pumpConfig.TriggerPermission != null && this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(pumpConfig.TriggerPermission) ) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = measureData.BlockType; return true; } if (measureData.BlockType == BlockType.InletPump) this.SignaledInletPumps.Add(new DPoint(originX, originY)); else this.SignaledOutletPumps.Add(new DPoint(originX, originY)); this.Result.SignaledPumps++; } return true; } case BlockType.DartTrap: { if (signal == SignalType.Off) return false; TrapConfig trapConfig; Tile componentTile = TerrariaUtils.Tiles[measureData.OriginTileLocation]; PaintColor componentPaint = (PaintColor)componentTile.color(); TrapStyle trapStyle = TerrariaUtils.Tiles.GetTrapStyle(componentTile.frameY / 18); TrapConfigKey configKey = new TrapConfigKey(trapStyle, componentPaint); TrapConfigKey defaultKey = new TrapConfigKey(trapStyle, PaintColor.None); if ( ( this.CircuitHandler.Config.TrapConfigs.TryGetValue(configKey, out trapConfig) || this.CircuitHandler.Config.TrapConfigs.TryGetValue(defaultKey, out trapConfig) ) && trapConfig.Cooldown == 0 || WorldGen.checkMech(originX, originY, trapConfig.Cooldown ) ) { if (this.Result.SignaledTraps > this.CircuitHandler.Config.MaxTrapsPerCircuit) { this.Result.WarnReason = CircuitWarnReason.SignalesTooManyTraps; return true; } if ( trapConfig.TriggerPermission != null && this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(trapConfig.TriggerPermission) ) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = BlockType.DartTrap; return true; } int projectileIndex = 1000; for (int i = 0; i < 1000; ++i) { if (!Main.projectile[i].active) { projectileIndex = i; break; } } if (projectileIndex == 1000) return true; bool isFacingLeft = (TerrariaUtils.Tiles[originX, originY].frameX == 0); float projectileAngle = trapConfig.ProjectileAngle; if (isFacingLeft) projectileAngle += 180f; projectileAngle += CircuitProcessor.Random.Next(-(int)trapConfig.ProjectileAngleVariance, (int)trapConfig.ProjectileAngleVariance + 1); Vector2 normalizedPolarOffset = new Vector2( (float)Math.Cos(Math.PI * projectileAngle / 180f), (float)Math.Sin(Math.PI * projectileAngle / 180f) ); Projectile projectile = Main.projectile[projectileIndex]; projectile.SetDefaults(trapConfig.ProjectileType); Vector2 projectileSpawn = new Vector2( (originX * TerrariaUtils.TileSize + (trapConfig.ProjectileOffset * normalizedPolarOffset.X)), (originY * TerrariaUtils.TileSize + (trapConfig.ProjectileOffset * normalizedPolarOffset.Y)) ); projectileSpawn = projectileSpawn.Add(new Vector2( TerrariaUtils.TileSize / 2 - projectile.width / 2, TerrariaUtils.TileSize / 2 - projectile.height / 2 )); projectile.position.X = projectileSpawn.X; projectile.position.Y = projectileSpawn.Y; projectile.owner = Main.myPlayer; projectile.velocity.X = (trapConfig.ProjectileSpeed * normalizedPolarOffset.X); projectile.velocity.Y = (trapConfig.ProjectileSpeed * normalizedPolarOffset.Y); projectile.damage = trapConfig.ProjectileDamage; projectile.knockBack = trapConfig.ProjectileKnockback; projectile.identity = projectileIndex; projectile.timeLeft = trapConfig.ProjectileLifeTime; projectile.wet = Collision.WetCollision(projectile.position, projectile.width, projectile.height); TSPlayer.All.SendData(PacketTypes.ProjectileNew, string.Empty, projectileIndex); this.Result.SignaledTraps++; } return true; } case BlockType.Explosives: { if (signal == SignalType.Off) return false; WorldGen.KillTile(originX, originY, false, false, true); TSPlayer.All.SendTileSquareEx(originX, originY, 1); Projectile.NewProjectile((originX * 16 + 8), (originY * 16 + 8), 0f, 0f, 108, 250, 10f, Main.myPlayer); return true; } case BlockType.Statue: { if (signal == SignalType.Off) return false; StatueStyle statueStyle = TerrariaUtils.Tiles.GetStatueStyle(TerrariaUtils.Tiles[measureData.OriginTileLocation]); StatueConfig statueConfig; if ( this.CircuitHandler.Config.StatueConfigs.TryGetValue(statueStyle, out statueConfig) && statueConfig.Actions.Count > 0 && ( statueConfig.Cooldown == 0 || WorldGen.checkMech(originX, originY, statueConfig.Cooldown) ) ) { if (this.Result.SignaledStatues > this.CircuitHandler.Config.MaxStatuesPerCircuit) { this.Result.WarnReason = CircuitWarnReason.SignalesTooManyStatues; return true; } if ( statueConfig.TriggerPermission != null && this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(statueConfig.TriggerPermission) ) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = BlockType.Statue; return true; } if (statueConfig.PlayerCheckRange > 0) { bool isPlayerInRange = false; foreach (TSPlayer player in TShock.Players) { if (player == null || !player.Active) continue; if (Math.Sqrt(Math.Pow(player.TileX - originX, 2) + Math.Pow(player.TileY - originY, 2)) <= statueConfig.PlayerCheckRange) { isPlayerInRange = true; break; } } if (!isPlayerInRange) return true; } if (statueConfig.ActionsProcessingMethod == ActionListProcessingMethod.ExecuteAll) { foreach (NullStatueAction action in statueConfig.Actions) this.ExecuteStatueAction(measureData.OriginTileLocation, action); } else { NullStatueAction randomAction = statueConfig.Actions[CircuitProcessor.Random.Next(0, statueConfig.Actions.Count)]; this.ExecuteStatueAction(measureData.OriginTileLocation, randomAction); } this.Result.SignaledStatues++; } return true; } case BlockType.Sign: { if (!this.IsAdvancedCircuit || signal == SignalType.Off || this.TriggeringPlayer == TSPlayer.Server) return false; if (this.IsTriggeredPassively && !this.TriggeringPlayer.Group.HasPermission(AdvancedCircuitsPlugin.PassiveTriggerSign_Permission)) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = BlockType.Sign; return false; } string signText = Main.sign[Sign.ReadSign(originX, originY)].text; if (signText == null) return false; if ( this.CircuitHandler.PluginCooperationHandler.IsSignCommandsAvailable && this.CircuitHandler.PluginCooperationHandler.SignCommands_CheckIsSignCommand(signText) ) { if (!this.TriggeringPlayer.Group.HasPermission(AdvancedCircuitsPlugin.TriggerSignCommand_Permission)) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = BlockType.Sign; return false; } this.CircuitHandler.PluginCooperationHandler.SignCommands_ExecuteSignCommand( this.TriggeringPlayer, measureData.OriginTileLocation, signText ); return true; } if (!WorldGen.checkMech(originX, originY, 300)) return true; string fullText = this.CircuitHandler.Config.SignConfig.ReadPrefix + signText; int lineStartIndex = 0; int lineLength = 0; for (int i = 0; i < fullText.Length; i++) { if (lineLength == 60 || fullText[i] == '\n' || (i == fullText.Length - 1 && lineLength > 0)) { if (fullText[i] == '\n') { if (lineLength > 0) this.TriggeringPlayer.SendInfoMessage(fullText.Substring(lineStartIndex, i - lineStartIndex)); else this.TriggeringPlayer.SendInfoMessage(string.Empty); lineStartIndex = i + 1; } else if (i == fullText.Length - 1) { this.TriggeringPlayer.SendInfoMessage(fullText.Substring(lineStartIndex, i - lineStartIndex + 1)); lineStartIndex = i; } else { this.TriggeringPlayer.SendInfoMessage(fullText.Substring(lineStartIndex, i - lineStartIndex)); lineStartIndex = i; } lineLength = 0; continue; } lineLength++; } return true; } case BlockType.Boulder: { if (!this.IsAdvancedCircuit || signal == SignalType.Off) return false; WorldGen.KillTile(originX, originY, false, false, true); TSPlayer.All.SendTileSquareEx(originX, originY, 2); return true; } case BlockType.LandMine: { if (signal == SignalType.Off) return false; WorldGen.ExplodeMine(originX, originY); return true; } case BlockType.Rocket: { if (signal == SignalType.Off) return false; WorldGen.LaunchRocket(originX, originY); return true; } case BlockType.Teleporter: { if (signal == SignalType.Off) return false; if (TerrariaUtils.Tiles[measureData.OriginTileLocation].wall == (int)WallType.LihzahrdBrickWall && !(originY <= Main.worldSurface || NPC.downedPlantBoss)) return true; if (this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(AdvancedCircuitsPlugin.TriggerTeleporter_Permission)) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = BlockType.Teleporter; return false; } if (rootBranch.TeleporterLocation == DPoint.Empty) { rootBranch.TeleporterLocation = measureData.OriginTileLocation; } else { WorldGen.teleport[0] = rootBranch.TeleporterLocation.ToXnaVector2(); WorldGen.teleport[1] = measureData.OriginTileLocation.ToXnaVector2(); WorldGen.Teleport(); WorldGen.teleport[0] = WorldGen.teleport[1] = new Vector2(-1f, -1f); rootBranch.TeleporterLocation = DPoint.Empty; } return true; } } return false; }
private bool SignalPortDefiningComponent(RootBranchProcessData rootBranch, ObjectMeasureData measureData, DPoint portLocation, bool signal) { if (!this.IsAdvancedCircuit) throw new InvalidOperationException("This is no advanced circuit."); Tile portTile = TerrariaUtils.Tiles[portLocation]; Tile componentTile = TerrariaUtils.Tiles[measureData.OriginTileLocation]; if (!portTile.HasWire(rootBranch.WireColor) || componentTile.HasWire()) return false; if (portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_NOTGate && measureData.BlockType != AdvancedCircuits.BlockType_NOTGate) signal = !signal; else if (portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_XORGate && measureData.BlockType != AdvancedCircuits.BlockType_XORGate) signal = false; List<DPoint> componentPorts = null; bool outputSignal = signal; DPoint componentLocation = measureData.OriginTileLocation; PaintColor componentPaint = (PaintColor)TerrariaUtils.Tiles[measureData.OriginTileLocation].color(); BlockActivatorMetadata blockActivatorToRegister = null; DPoint blockActivatorLocationToRegister = DPoint.Empty; BlockActivatorMode blockActivatorModeToRegister = BlockActivatorMode.Default; switch (measureData.BlockType) { case BlockType.DoorOpened: case BlockType.DoorClosed: { for (int y = measureData.OriginTileLocation.Y; y < measureData.OriginTileLocation.Y + measureData.Size.Y; y++) if (TerrariaUtils.Tiles[measureData.OriginTileLocation.X, y].HasWire()) return false; if (measureData.BlockType == BlockType.DoorOpened) { // Extra check needed if a port of the door is really hit. This is because doors define ports differently than // other components if they are opened. componentPorts = new List<DPoint>(AdvancedCircuits.EnumerateComponentPortLocations(measureData)); if (!componentPorts.Contains(portLocation)) return false; } this.OpenDoor(measureData, AdvancedCircuits.BoolToSignal(signal)); break; } case BlockType.XSecondTimer: { if (!portTile.active() || portTile.type != (int)AdvancedCircuits.BlockType_InputPort) return false; bool currentState = (TerrariaUtils.Tiles.ObjectHasActiveState(measureData)); if (currentState != signal) TerrariaUtils.Tiles.SetObjectState(measureData, signal); if (currentState != signal) this.CircuitHandler.RegisterUnregisterTimer(this.TriggeringPlayer, measureData, signal); else if (signal) this.CircuitHandler.ResetTimer(measureData); return true; } case BlockType.Switch: case BlockType.Lever: { if (measureData.BlockType == BlockType.Lever && TerrariaUtils.Tiles.IsObjectWired(measureData)) return false; bool isInputPort = (portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_InputPort); bool currentState = (TerrariaUtils.Tiles.ObjectHasActiveState(measureData)); if (isInputPort) { if (currentState != signal) TerrariaUtils.Tiles.SetObjectState(measureData, signal); return true; } else { switch (componentPaint) { default: if (currentState == signal) return true; TerrariaUtils.Tiles.SetObjectState(measureData, signal); break; case AdvancedCircuits.Paint_Switch_ToggleAndForward: if (currentState != signal) TerrariaUtils.Tiles.SetObjectState(measureData, signal); break; case AdvancedCircuits.Paint_Switch_ForwardIfEqual: if (currentState != signal) return true; break; case AdvancedCircuits.Paint_Switch_ForwardIfEqualByChance: if (currentState != signal) return true; if (CircuitProcessor.Random.Next(0, 2) == 0) outputSignal = !signal; break; case AdvancedCircuits.Paint_Switch_ForwardByChance: if (CircuitProcessor.Random.Next(0, 2) == 0) return true; break; } if (componentPaint != AdvancedCircuits.Paint_Switch_ToggleAndForward) { blockActivatorToRegister = rootBranch.BlockActivator; blockActivatorLocationToRegister = rootBranch.BlockActivatorLocation; blockActivatorModeToRegister = rootBranch.BlockActivatorMode; } } break; } case AdvancedCircuits.BlockType_NOTGate: { if (!portTile.active() || portTile.type != (int)AdvancedCircuits.BlockType_InputPort) return false; outputSignal = !signal; blockActivatorToRegister = rootBranch.BlockActivator; blockActivatorLocationToRegister = rootBranch.BlockActivatorLocation; blockActivatorModeToRegister = rootBranch.BlockActivatorMode; break; } case AdvancedCircuits.BlockType_ANDGate: case AdvancedCircuits.BlockType_ORGate: case AdvancedCircuits.BlockType_XORGate: { if (!portTile.active() || portTile.type != (int)AdvancedCircuits.BlockType_InputPort) return false; GateStateMetadata metadata; switch (componentPaint) { default: if (!this.CircuitHandler.WorldMetadata.GateStates.TryGetValue(componentLocation, out metadata)) { metadata = new GateStateMetadata(); this.CircuitHandler.WorldMetadata.GateStates.Add(componentLocation, metadata); } break; case AdvancedCircuits.Paint_Gate_TemporaryState: if (!temporaryGateStates.TryGetValue(componentLocation, out metadata)) { metadata = new GateStateMetadata(); temporaryGateStates.Add(componentLocation, metadata); } break; } componentPorts = new List<DPoint>(AdvancedCircuits.EnumerateComponentPortLocations(measureData)); int inputPorts = 0; int signaledPorts = 0; bool isInvalid = false; for (int i = 0; i < componentPorts.Count; i++) { DPoint port = componentPorts[i]; portTile = TerrariaUtils.Tiles[port]; if (portTile.HasWire() && portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_InputPort) { inputPorts++; if (port == portLocation) metadata.PortStates[i] = outputSignal; if (metadata.PortStates[i] == null) isInvalid = true; } if (metadata.PortStates[i] != null && metadata.PortStates[i].Value) signaledPorts++; } // Gates will not operate as long as the input port states are not clear. if (isInvalid) return false; switch ((BlockType)componentTile.type) { case AdvancedCircuits.BlockType_ANDGate: outputSignal = (inputPorts == signaledPorts); break; case AdvancedCircuits.BlockType_ORGate: outputSignal = (signaledPorts > 0); break; case AdvancedCircuits.BlockType_XORGate: outputSignal = (signaledPorts != 0 && signaledPorts < inputPorts); break; } blockActivatorToRegister = rootBranch.BlockActivator; blockActivatorLocationToRegister = rootBranch.BlockActivatorLocation; blockActivatorModeToRegister = rootBranch.BlockActivatorMode; break; } case AdvancedCircuits.BlockType_Swapper: { if (!signal) return false; int swapperCounterValue; if (!this.CircuitHandler.WorldMetadata.Swappers.TryGetValue(componentLocation, out swapperCounterValue)) { this.CircuitHandler.WorldMetadata.Swappers.Add(componentLocation, 1); if (componentPaint == PaintColor.None) outputSignal = false; else return true; } else { swapperCounterValue++; bool sendSignal = true; int swapperCounterMax = AdvancedCircuits.SwapperPaintToCount(componentPaint); if (swapperCounterValue == swapperCounterMax) { outputSignal = false; } else if (swapperCounterValue >= swapperCounterMax * 2) { outputSignal = true; swapperCounterValue = 0; } else { sendSignal = false; } this.CircuitHandler.WorldMetadata.Swappers[componentLocation] = swapperCounterValue; if (!sendSignal) return true; } blockActivatorToRegister = rootBranch.BlockActivator; blockActivatorLocationToRegister = rootBranch.BlockActivatorLocation; blockActivatorModeToRegister = rootBranch.BlockActivatorMode; break; } case AdvancedCircuits.BlockType_CrossoverBridge: { switch (AdvancedCircuits.DirectionFromTileLocations(componentLocation, portLocation)) { case Direction.Left: componentPorts = new List<DPoint> { new DPoint(componentLocation.X + 1, componentLocation.Y) }; break; case Direction.Up: componentPorts = new List<DPoint> { new DPoint(componentLocation.X, componentLocation.Y + 1) }; break; case Direction.Right: componentPorts = new List<DPoint> { new DPoint(componentLocation.X - 1, componentLocation.Y) }; break; case Direction.Down: componentPorts = new List<DPoint> { new DPoint(componentLocation.X, componentLocation.Y - 1) }; break; default: throw new ArgumentOutOfRangeException(); } blockActivatorToRegister = rootBranch.BlockActivator; blockActivatorLocationToRegister = rootBranch.BlockActivatorLocation; blockActivatorModeToRegister = rootBranch.BlockActivatorMode; break; } case AdvancedCircuits.BlockType_BlockActivator: { if (!portTile.active() || portTile.type != (int)AdvancedCircuits.BlockType_InputPort) return false; if ( this.CircuitHandler.Config.BlockActivatorConfig.Cooldown > 0 && !WorldGen.checkMech(componentLocation.X, componentLocation.Y, this.CircuitHandler.Config.BlockActivatorConfig.Cooldown )) return false; if ( this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(AdvancedCircuitsPlugin.TriggerBlockActivator_Permission) ) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = AdvancedCircuits.BlockType_BlockActivator; return true; } BlockActivatorMetadata blockActivator; this.CircuitHandler.WorldMetadata.BlockActivators.TryGetValue(componentLocation, out blockActivator); if (signal) { if (blockActivator == null || blockActivator.IsActivated) return true; } else { if (blockActivator == null) { blockActivator = new BlockActivatorMetadata(); this.CircuitHandler.WorldMetadata.BlockActivators.Add(componentLocation, blockActivator); } else { // Will do nothing if already deactivated. if (!blockActivator.IsActivated) return true; blockActivator.RegisteredInactiveBlocks.Clear(); } } blockActivator.IsActivated = signal; if (signal && blockActivator.RegisteredInactiveBlocks.Count == 0) return true; blockActivatorToRegister = blockActivator; blockActivatorLocationToRegister = componentLocation; if (componentPaint == AdvancedCircuits.Paint_BlockActivator_Replace) blockActivatorModeToRegister = BlockActivatorMode.ReplaceBlocks; break; } case AdvancedCircuits.BlockType_WirelessTransmitter: { if (!portTile.active() || portTile.type != (int)AdvancedCircuits.BlockType_InputPort) return false; WirelessTransmitterConfig transmitterConfig; if ( ( this.CircuitHandler.Config.WirelessTransmitterConfigs.TryGetValue(componentPaint, out transmitterConfig) || this.CircuitHandler.Config.WirelessTransmitterConfigs.TryGetValue(PaintColor.None, out transmitterConfig) ) && ( transmitterConfig.Cooldown == 0 || WorldGen.checkMech(componentLocation.X, componentLocation.Y, transmitterConfig.Cooldown) ) ) { if ( transmitterConfig.TriggerPermission != null && this.TriggeringPlayer != TSPlayer.Server && !this.TriggeringPlayer.Group.HasPermission(transmitterConfig.TriggerPermission) ) { this.Result.WarnReason = CircuitWarnReason.InsufficientPermissionToSignalComponent; this.Result.WarnRelatedComponentType = AdvancedCircuits.BlockType_WirelessTransmitter; return true; } string sendingTransmitterOwner; if (!this.CircuitHandler.WorldMetadata.WirelessTransmitters.TryGetValue(componentLocation, out sendingTransmitterOwner)) return false; bool isBroadcasting = (transmitterConfig.Network == 0); double sendingSquareRange = 0; if (transmitterConfig.Range > 0) sendingSquareRange = Math.Pow(transmitterConfig.Range + 1, 2); DPoint portOffset = new DPoint(portLocation.X - componentLocation.X, portLocation.Y - componentLocation.Y); foreach (KeyValuePair<DPoint,string> pair in this.CircuitHandler.WorldMetadata.WirelessTransmitters) { DPoint receivingTransmitterLocation = pair.Key; if (receivingTransmitterLocation == componentLocation) continue; string receivingTransmitterOwner = pair.Value; if (receivingTransmitterOwner != sendingTransmitterOwner) continue; Tile transmitterTile = TerrariaUtils.Tiles[receivingTransmitterLocation]; if (!transmitterTile.active() || transmitterTile.type != (int)AdvancedCircuits.BlockType_WirelessTransmitter || transmitterTile.HasWire()) continue; if ( sendingSquareRange > 0 && sendingSquareRange <= ( Math.Pow(receivingTransmitterLocation.X - componentLocation.X, 2) + Math.Pow(receivingTransmitterLocation.Y - componentLocation.Y, 2) ) ) continue; DPoint outputPortLocation = receivingTransmitterLocation.OffsetEx(portOffset.X, portOffset.Y); Tile outputPortTile = TerrariaUtils.Tiles[outputPortLocation]; if (!outputPortTile.HasWire() || (outputPortTile.active() && outputPortTile.type == (int)AdvancedCircuits.BlockType_InputPort)) continue; if (!isBroadcasting) { PaintColor receiverPaint = (PaintColor)TerrariaUtils.Tiles[receivingTransmitterLocation].color(); WirelessTransmitterConfig receiverConfig; if (!this.CircuitHandler.Config.WirelessTransmitterConfigs.TryGetValue(receiverPaint, out receiverConfig)) receiverConfig = this.CircuitHandler.Config.WirelessTransmitterConfigs[PaintColor.None]; if (receiverConfig.Network != 0 && receiverConfig.Network != transmitterConfig.Network) continue; } bool portOutputSignal = outputSignal; if (outputPortTile.active() && outputPortTile.type == (int)AdvancedCircuits.BlockType_NOTGate) portOutputSignal = !portOutputSignal; else if (outputPortTile.active() && outputPortTile.type == (int)AdvancedCircuits.BlockType_XORGate) portOutputSignal = false; foreach (WireColor wireColor in AdvancedCircuits.EnumerateWireColors()) { if (!outputPortTile.HasWire(wireColor)) continue; this.QueuedRootBranches.Add(new RootBranchProcessData(receivingTransmitterLocation, outputPortLocation, AdvancedCircuits.BoolToSignal(portOutputSignal), wireColor) { BlockActivator = rootBranch.BlockActivator, BlockActivatorLocation = rootBranch.BlockActivatorLocation, BlockActivatorMode = rootBranch.BlockActivatorMode, TeleporterLocation = rootBranch.TeleporterLocation }); } } return true; } break; } default: return false; } if (componentPorts == null) componentPorts = new List<DPoint>(AdvancedCircuits.EnumerateComponentPortLocations(measureData)); for (int i = 0; i < componentPorts.Count; i++) { DPoint port = componentPorts[i]; // A component shouldn't send a signal through a port where it just received one. if (port == portLocation) { componentPorts.RemoveAt(i--); continue; } portTile = TerrariaUtils.Tiles[port]; if (!portTile.HasWire() || (portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_InputPort)) componentPorts.RemoveAt(i--); } foreach (DPoint port in componentPorts) { portTile = TerrariaUtils.Tiles[port]; bool portOutputSignal = outputSignal; if ( portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_NOTGate && measureData.BlockType != AdvancedCircuits.BlockType_NOTGate ) { portOutputSignal = !portOutputSignal; } else if ( portTile.active() && portTile.type == (int)AdvancedCircuits.BlockType_XORGate && measureData.BlockType != AdvancedCircuits.BlockType_XORGate ) { portOutputSignal = false; } foreach (WireColor wireColor in AdvancedCircuits.EnumerateWireColors()) { if (!portTile.HasWire(wireColor)) continue; RootBranchProcessData newRootBranch = new RootBranchProcessData( componentLocation, port, AdvancedCircuits.BoolToSignal(portOutputSignal), wireColor ); if (blockActivatorToRegister != null) { newRootBranch.BlockActivator = blockActivatorToRegister; newRootBranch.BlockActivatorLocation = blockActivatorLocationToRegister; newRootBranch.BlockActivatorMode = blockActivatorModeToRegister; } newRootBranch.TeleporterLocation = rootBranch.TeleporterLocation; this.QueuedRootBranches.Add(newRootBranch); } } return true; }
public void ResetTimer(ObjectMeasureData measureData) { ActiveTimerMetadata activeTimer; if (this.WorldMetadata.ActiveTimers.TryGetValue(measureData.OriginTileLocation, out activeTimer)) activeTimer.FramesLeft = AdvancedCircuits.MeasureTimerFrameTime(measureData.OriginTileLocation); }
public bool IsObjectWired(ObjectMeasureData measureData, WireColor wireColor = WireColor.None) { DPoint dummy; return this.IsObjectWired(measureData, wireColor, out dummy); }
public void SetObjectState( ObjectMeasureData measureData, bool activeState, bool sendTileSquare = true ) { #if DEBUG if (this.ObjectHasActiveState(measureData) == activeState) { throw new ArgumentException( $"The object \"{TerrariaUtils.Tiles.GetBlockTypeName(measureData.BlockType, 0)}\" does already have the state \"{activeState}\"." ); } #endif int originX = measureData.OriginTileLocation.X; int originY = measureData.OriginTileLocation.Y; int objectWidth = measureData.Size.X; int objectHeight = measureData.Size.Y; int newFrameXOffset = 0; int newFrameYOffset = 0; if (measureData.BlockType == TileID.Torches || measureData.BlockType == TileID.HolidayLights) newFrameXOffset = measureData.TextureTileSize.X * 2; if ( measureData.BlockType != TileID.Switches && measureData.BlockType != TileID.Timers && measureData.BlockType != TileID.WaterFountain ) { int frameXOffset = (objectWidth * measureData.TextureTileSize.X) + newFrameXOffset; if (measureData.BlockType == TileID.MusicBoxes) frameXOffset = -frameXOffset; if (activeState) newFrameXOffset = (short)-frameXOffset; else newFrameXOffset = (short)frameXOffset; if (measureData.BlockType == TileID.BubbleMachine && !activeState) newFrameYOffset = -TerrariaUtils.Tiles[measureData.OriginTileLocation].frameY; } else { int frameYOffset = (objectHeight * measureData.TextureTileSize.Y); if (measureData.BlockType == TileID.WaterFountain && !activeState) { newFrameYOffset = -TerrariaUtils.Tiles[measureData.OriginTileLocation].frameY; } else { if ( measureData.BlockType == TileID.Timers || measureData.BlockType == TileID.WaterFountain ) activeState = !activeState; if (activeState) newFrameYOffset = (short)-frameYOffset; else newFrameYOffset = (short)frameYOffset; } } for (int tx = 0; tx < objectWidth; tx++) { for (int ty = 0; ty < objectHeight; ty++) { int absoluteX = originX + tx; int absoluteY = originY + ty; TerrariaUtils.Tiles[absoluteX, absoluteY].frameX += (short)newFrameXOffset; TerrariaUtils.Tiles[absoluteX, absoluteY].frameY += (short)newFrameYOffset; } } if (sendTileSquare) TSPlayer.All.SendTileSquareEx(originX, originY, Math.Max(objectWidth, objectHeight)); }
private void OpenDoor(ObjectMeasureData measureData, SignalType signal) { bool currentState = (measureData.BlockType == BlockType.DoorOpened); bool newState; if (signal == SignalType.Swap) newState = !currentState; else newState = AdvancedCircuits.SignalToBool(signal).Value; if (newState != currentState) { int doorX = measureData.OriginTileLocation.X; int doorY = measureData.OriginTileLocation.Y; if (newState) { // A door will always try to open to the opposite site of the sender's location that triggered the circuit first. int direction = 1; if (doorX < this.SenderMeasureData.OriginTileLocation.X) direction = -1; // If opening it towards one direction doesn't work, try the other. currentState = WorldGen.OpenDoor(doorX, doorY, direction); if (!currentState) { direction *= -1; currentState = WorldGen.OpenDoor(doorX, doorY, direction); } if (currentState) { TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, 0, doorX, doorY, direction); // Because the door changed its sprite, we have to re-measure it now. measureData = TerrariaUtils.Tiles.MeasureObject(measureData.OriginTileLocation); } } else { if (WorldGen.CloseDoor(doorX, doorY, true)) TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, 1, doorX, doorY); } WorldGen.numWire = 0; WorldGen.numNoWire = 0; } }
private void OpenDoor(ObjectMeasureData measureData, SignalType signal) { bool currentState = false; DoorAction action = DoorAction.Invalid; switch (measureData.BlockType) { case BlockType.DoorClosed: currentState = false; action = DoorAction.OpenDoor; break; case BlockType.DoorOpened: currentState = true; action = DoorAction.CloseDoor; break; case BlockType.TallGateClosed: currentState = false; action = DoorAction.OpenTallGate; break; case BlockType.TallGateOpen: currentState = true; action = DoorAction.CloseTallGate; break; case BlockType.TrapdoorClosed: currentState = false; action = DoorAction.OpenTrapdoor; break; case BlockType.TrapdoorOpen: currentState = true; action = DoorAction.CloseTrapdoor; break; } bool newState; if (signal == SignalType.Swap) newState = !currentState; else newState = AdvancedCircuits.SignalToBool(signal).Value; if (newState != currentState) { int doorX = measureData.OriginTileLocation.X; int doorY = measureData.OriginTileLocation.Y; int direction = 1; // Tall gates dont care direction if (action == DoorAction.OpenTallGate || action == DoorAction.CloseTallGate) { if(WorldGen.ShiftTallGate(doorX, doorY, action == DoorAction.CloseTallGate)) TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, (int)action, doorX, doorY); } if (action == DoorAction.OpenDoor) { // A door will always try to open to the opposite site of the sender's location that triggered the circuit first. if (doorX < this.SenderMeasureData.OriginTileLocation.X) direction = -1; // If opening it towards one direction doesn't work, try the other. currentState = WorldGen.OpenDoor(doorX, doorY, direction); if (!currentState) { direction *= -1; currentState = WorldGen.OpenDoor(doorX, doorY, direction); } if (currentState) { TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, (int)action, doorX, doorY, direction); } } else if (action == DoorAction.CloseDoor) { if (WorldGen.CloseDoor(doorX, doorY, true)) { TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, (int)action, doorX, doorY, direction); // Because the door changed its sprite, we have to re-measure it now. measureData = TerrariaUtils.Tiles.MeasureObject(measureData.OriginTileLocation); } } else if (action == DoorAction.OpenTrapdoor) { // A trapdoor will always try to open to the opposite site of the sender's location that triggered the circuit first. // direction: 1 is up, -1 is down; if (doorY < this.SenderMeasureData.OriginTileLocation.Y) direction = -1; // If opening it towards one direction doesn't work, try the other. currentState = WorldGen.ShiftTrapdoor(doorX, doorY, direction == -1); if (!currentState) { direction *= -1; currentState = WorldGen.ShiftTrapdoor(doorX, doorY, direction == -1); } if (currentState) { TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, (int)action, doorX, doorY, direction); // Because the door changed its sprite, we have to re-measure it now. measureData = TerrariaUtils.Tiles.MeasureObject(measureData.OriginTileLocation); } } else if (action == DoorAction.CloseTrapdoor) { if (doorY < this.SenderMeasureData.OriginTileLocation.Y) direction = -1; if (WorldGen.ShiftTrapdoor(doorX, doorY, direction == -1)) { direction *= -1; TSPlayer.All.SendData(PacketTypes.DoorUse, string.Empty, (int)action, doorX, doorY, direction); // Because the door changed its sprite, we have to re-measure it now. measureData = TerrariaUtils.Tiles.MeasureObject(measureData.OriginTileLocation); } } } }
public IEnumerable<Tile> EnumerateObjectTiles(ObjectMeasureData measureData) { foreach (DPoint tileLocation in this.EnumerateObjectTileLocations(measureData)) yield return TerrariaUtils.Tiles[tileLocation]; }
public bool IsObjectWired(ObjectMeasureData measureData, WireColor wireColor, out DPoint firstWireLocation) { foreach (DPoint tileLocation in this.EnumerateObjectTileLocations(measureData)) { Tile tile = TerrariaUtils.Tiles[tileLocation]; if (tile.HasWire(wireColor)) { firstWireLocation = tileLocation; return true; } } firstWireLocation = DPoint.Empty; return false; }
public static DPoint GetPortAdjacentComponentTileLocation(ObjectMeasureData measureData, DPoint portLocation) { DPoint origin = measureData.OriginTileLocation; DPoint size = measureData.Size; if (measureData.BlockType == BlockType.DoorOpened) size = new DPoint(1, 3); if (portLocation.X < origin.X) return new DPoint(origin.X, portLocation.Y); if (portLocation.Y < origin.Y) return new DPoint(portLocation.X, origin.Y); if (portLocation.X >= origin.X + size.X) return new DPoint(origin.X + size.X - 1, portLocation.Y); if (portLocation.Y >= origin.Y + size.Y) return new DPoint(portLocation.X, origin.Y + size.Y - 1); throw new ArgumentException("The given port location referes to no port of this component at all.", "portLocation"); }
public static IEnumerable<DPoint> EnumerateComponentPortLocations(ObjectMeasureData measureData) { DPoint componentOriginLocation = measureData.OriginTileLocation; DPoint componentSize = measureData.Size; if (measureData.BlockType == BlockType.DoorOpened) componentSize = new DPoint(1, 3); return AdvancedCircuits.EnumerateComponentPortLocations(componentOriginLocation, componentSize); }
private IEnumerable<ProtectionEntry> EnumProtectionEntriesOnTopOfObject(ObjectMeasureData measureData) { for (int rx = 0; rx < measureData.Size.X; rx++) { DPoint absoluteLocation = measureData.OriginTileLocation.OffsetEx(rx, -1); Tile topTile = TerrariaUtils.Tiles[absoluteLocation]; TileObjectData topData = TileObjectData.GetTileData(topTile); if (topData != null && (topData.AnchorBottom.type & AnchorType.Table) != 0) { lock (this.WorldMetadata.Protections) { ProtectionEntry protection; if (this.WorldMetadata.Protections.TryGetValue(absoluteLocation, out protection)) yield return protection; } } } }