diff --git a/RogueEssence/Data/TileData.cs b/RogueEssence/Data/TileData.cs index d7714f76..f5f49aa6 100644 --- a/RogueEssence/Data/TileData.cs +++ b/RogueEssence/Data/TileData.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; using RogueEssence.Dungeon; +using RogueEssence.Dev; using RogueEssence.Content; using RogueElements; +using Newtonsoft.Json; using Microsoft.Xna.Framework; namespace RogueEssence.Data @@ -22,7 +25,8 @@ public enum TriggerType Trap, Switch, Blocker, - Unlockable + Unlockable, + Object } public LocalText Name { get; set; } @@ -52,9 +56,31 @@ public enum TriggerType public TriggerType StepType; public Loc MinimapIcon; public Color MinimapColor; + + /// + /// Allows the tile to be destroyed by certain attacks. + /// + public bool Destructible; + + /// + /// Any supereffective move used against this tile will destroy it. If this is none, any attack will destroy it. Only used if Destructible is true. + /// + [JsonConverter(typeof(ElementListConverter))] + [Dev.DataType(0, DataManager.DataType.Element, false)] + public List EffectiveElements; + + /// + /// The minimum damage needed to destroy the tile. Only used if Destructible is true. + /// + public int PowerNeededToDestroy; public PriorityList LandedOnTiles; public PriorityList InteractWithTiles; + + /// + /// Triggers right before the tile is destroyed. Only used if Destructible is true. + /// + public PriorityList OnTileDestroyed; public TileData() { @@ -62,11 +88,11 @@ public TileData() Desc = new LocalText(); Comment = ""; Anim = new ObjAnimData(); + EffectiveElements = new List(); LandedOnTiles = new PriorityList(); InteractWithTiles = new PriorityList(); + OnTileDestroyed = new PriorityList(); } - - public string GetColoredName() { return String.Format("[color=#00FF00]{0}[color]", Name.ToLocal()); diff --git a/RogueEssence/Dungeon/BaseDungeonScene.cs b/RogueEssence/Dungeon/BaseDungeonScene.cs index 658820b6..be6bf427 100644 --- a/RogueEssence/Dungeon/BaseDungeonScene.cs +++ b/RogueEssence/Dungeon/BaseDungeonScene.cs @@ -237,9 +237,22 @@ protected virtual void DrawItems(SpriteBatch spriteBatch, bool showHiddenItem) { foreach(Loc viewLoc in IterateRelevantDraw(wrapped, wrapSize, item)) { - TerrainTile tile = ZoneManager.Instance.CurrentMap.Tiles[item.TileLoc.X][item.TileLoc.Y].Data; + Tile rootTile = ZoneManager.Instance.CurrentMap.Tiles[item.TileLoc.X][item.TileLoc.Y]; + TerrainTile tile = rootTile.Data; TerrainData terrain = tile.GetData(); - if (terrain.BlockType == TerrainData.Mobility.Impassable || terrain.BlockType == TerrainData.Mobility.Block) + + //Object tiles should hide items beneath them + bool hiddenBehindObject = false; + if (!String.IsNullOrEmpty(rootTile.Effect.ID)) + { + TileData effect = DataManager.Instance.GetTile(rootTile.Effect.ID); + if (effect.StepType == TileData.TriggerType.Object) + { + hiddenBehindObject = true; + } + } + + if (terrain.BlockType == TerrainData.Mobility.Impassable || terrain.BlockType == TerrainData.Mobility.Block || hiddenBehindObject) { if (showHiddenItem) item.Draw(spriteBatch, viewLoc, Color.White * 0.7f); diff --git a/RogueEssence/Dungeon/Characters/CharAction.cs b/RogueEssence/Dungeon/Characters/CharAction.cs index d70c1b4c..674aa3db 100644 --- a/RogueEssence/Dungeon/Characters/CharAction.cs +++ b/RogueEssence/Dungeon/Characters/CharAction.cs @@ -802,7 +802,7 @@ private Loc GetLanding(Loc ownerLoc, Dir8 dir, int mod) for (int ii = 0; ii < modRange; ii++) { targetLoc += addLoc; - if (ZoneManager.Instance.CurrentMap.TileBlocked(targetLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(targetLoc, true)) break; } return targetLoc + HitOffset; @@ -1320,7 +1320,7 @@ public Loc GetFarthestLanding(Character owner, Loc ownerLoc, Dir8 dir, int mod) for (int ii = 0; ii < modRange; ii++) { targetLoc += addLoc; - if (ZoneManager.Instance.CurrentMap.TileBlocked(targetLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(targetLoc, true)) break; } return targetLoc; @@ -1415,14 +1415,14 @@ public override bool IsWide() } - if (ZoneManager.Instance.CurrentMap.TileBlocked(leftLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(leftLoc, true)) sideL[side] = false; - if (ZoneManager.Instance.CurrentMap.TileBlocked(rightLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(rightLoc, true)) sideR[side] = false; } } - if (ZoneManager.Instance.CurrentMap.TileBlocked(targetLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(targetLoc, true)) sideM = false; } return null; diff --git a/RogueEssence/Dungeon/Characters/Hitbox.cs b/RogueEssence/Dungeon/Characters/Hitbox.cs index 3573e81e..6db96cc9 100644 --- a/RogueEssence/Dungeon/Characters/Hitbox.cs +++ b/RogueEssence/Dungeon/Characters/Hitbox.cs @@ -457,7 +457,7 @@ private void preCalculateCoverage(StablePriorityQueue tilesToHit, int tilesToHit.Enqueue(calculateTimeToHit(Origin) + delay, Origin); Loc backup = Origin; - if (MaxRadius > 0 && ZoneManager.Instance.CurrentMap.TileBlocked(Origin, true)) + if (MaxRadius > 0 && ZoneManager.Instance.CurrentMap.TileAttackBlocked(Origin, true)) backup += Dir.Reverse().GetLoc(); Grid.FloodFill(new Rect(Origin - new Loc(MaxRadius), new Loc(MaxRadius * 2 + 1)), @@ -467,7 +467,7 @@ private void preCalculateCoverage(StablePriorityQueue tilesToHit, int return true; if (!IsInSquareHitbox(testLoc, Origin, MaxRadius, HitArea, Dir)) return true; - if (ZoneManager.Instance.CurrentMap.TileBlocked(testLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(testLoc, true)) return true; return false; diff --git a/RogueEssence/Dungeon/DSceneAction.cs b/RogueEssence/Dungeon/DSceneAction.cs index dcb50bf9..5b62eedf 100644 --- a/RogueEssence/Dungeon/DSceneAction.cs +++ b/RogueEssence/Dungeon/DSceneAction.cs @@ -684,7 +684,7 @@ public bool IsTargeted(Loc tile, TileAlignment tileAlignment) return true; TerrainData.Mobility mobility = TerrainData.Mobility.Lava | TerrainData.Mobility.Water | TerrainData.Mobility.Abyss; - if (ZoneManager.Instance.CurrentMap.TileBlocked(tile, mobility)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(tile, mobility)) return true; else return false; diff --git a/RogueEssence/Dungeon/DSceneMap.cs b/RogueEssence/Dungeon/DSceneMap.cs index 42f7b5ba..bbae2284 100644 --- a/RogueEssence/Dungeon/DSceneMap.cs +++ b/RogueEssence/Dungeon/DSceneMap.cs @@ -560,7 +560,7 @@ public IEnumerator ProcessObjectInteract(Character character, if (tile != null && !String.IsNullOrEmpty(tile.Effect.ID)) { TileData entry = DataManager.Instance.GetTile(tile.Effect.ID); - if (entry.StepType == TileData.TriggerType.Blocker || entry.StepType == TileData.TriggerType.Unlockable) + if (entry.StepType == TileData.TriggerType.Blocker || entry.StepType == TileData.TriggerType.Unlockable || entry.StepType == TileData.TriggerType.Object) { yield return CoroutineManager.Instance.StartCoroutine(tile.Effect.LandedOnTile(character)); yield return CoroutineManager.Instance.StartCoroutine(ActivateTraps(character)); @@ -1446,7 +1446,7 @@ public IEnumerator ThrowTo(Character character, Character atta for (int ii = 0; ii < range; ii++) { Loc nextLoc = endLoc + dir.GetLoc(); - if (ZoneManager.Instance.CurrentMap.TileBlocked(nextLoc, true)) + if (ZoneManager.Instance.CurrentMap.TileAttackBlocked(nextLoc, true)) break; endLoc = nextLoc; } diff --git a/RogueEssence/Dungeon/DungeonScene.cs b/RogueEssence/Dungeon/DungeonScene.cs index 1df9fb89..e86a7a62 100644 --- a/RogueEssence/Dungeon/DungeonScene.cs +++ b/RogueEssence/Dungeon/DungeonScene.cs @@ -1049,8 +1049,21 @@ protected override void DrawItems(SpriteBatch spriteBatch, bool showHiddenItem) { foreach (Loc viewLoc in IterateRelevantDraw(wrapped, wrapSize, item)) { - TerrainData terrain = ZoneManager.Instance.CurrentMap.Tiles[item.TileLoc.X][item.TileLoc.Y].Data.GetData(); - if (terrain.BlockType == TerrainData.Mobility.Impassable || terrain.BlockType == TerrainData.Mobility.Block) + Tile rootTile = ZoneManager.Instance.CurrentMap.Tiles[item.TileLoc.X][item.TileLoc.Y]; + TerrainData terrain = rootTile.Data.GetData(); + + //Object tiles should hide items beneath them + bool hiddenBehindObject = false; + if (!String.IsNullOrEmpty(rootTile.Effect.ID)) + { + TileData effect = DataManager.Instance.GetTile(rootTile.Effect.ID); + if (effect.StepType == TileData.TriggerType.Object) + { + hiddenBehindObject = true; + } + } + + if (terrain.BlockType == TerrainData.Mobility.Impassable || terrain.BlockType == TerrainData.Mobility.Block || hiddenBehindObject) { if (showHiddenItem) item.Draw(spriteBatch, viewLoc, Color.White * 0.5f); diff --git a/RogueEssence/Dungeon/GameEffects/BattleContext.cs b/RogueEssence/Dungeon/GameEffects/BattleContext.cs index dde908f2..4e854998 100644 --- a/RogueEssence/Dungeon/GameEffects/BattleContext.cs +++ b/RogueEssence/Dungeon/GameEffects/BattleContext.cs @@ -232,6 +232,28 @@ public IEnumerator ProcessHitLoc(Loc loc) actionContext.Target = charTarget; yield return CoroutineManager.Instance.StartCoroutine(DungeonScene.Instance.HitTarget(actionContext, charTarget));//hit the character } + + Tile tile = ZoneManager.Instance.CurrentMap.GetTile(actionContext.TargetTile); + if (!String.IsNullOrEmpty(tile.Effect.ID)) + { + TileData entry = DataManager.Instance.GetTile(tile.Effect.GetID()); + if (entry != null && entry.Destructible && actionContext.ActionType == BattleActionType.Skill) + { + BattleData data = actionContext.Data; + if (data != null) + { + BasePowerState powerState = data.SkillStates.GetWithDefault(); + //Check if the attack is from the specific element or from no element, and deals enough damage + if ((entry.EffectiveElements.Contains(data.Element) || entry.EffectiveElements.Count == 0) + && powerState != null && powerState.Power >= entry.PowerNeededToDestroy) + { + yield return CoroutineManager.Instance.StartCoroutine(tile.Effect.OnTileDestroyed(charTarget)); + tile.Effect = new EffectTile(tile.Effect.TileLoc); + } + } + } + } + //do thing to tile yield return CoroutineManager.Instance.StartCoroutine(actionContext.User.HitTile(actionContext)); } @@ -241,6 +263,28 @@ public IEnumerator ProcessHitTile(Loc loc) BattleContext actionContext = new BattleContext(this, false); actionContext.TargetTile = loc; + Tile tile = ZoneManager.Instance.CurrentMap.GetTile(actionContext.TargetTile); + if (!String.IsNullOrEmpty(tile.Effect.ID)) + { + TileData entry = DataManager.Instance.GetTile(tile.Effect.GetID()); + if (entry != null && entry.Destructible && actionContext.ActionType == BattleActionType.Skill) + { + BattleData data = actionContext.Data; + Character charTarget = actionContext.User; + if (data != null && charTarget != null) + { + BasePowerState powerState = data.SkillStates.GetWithDefault(); + //Check if the attack is from the specific element or from no element, and deals enough damage + if ((entry.EffectiveElements.Contains(data.Element) || entry.EffectiveElements.Count == 0) + && powerState != null && powerState.Power >= entry.PowerNeededToDestroy) + { + yield return CoroutineManager.Instance.StartCoroutine(tile.Effect.OnTileDestroyed(charTarget)); + tile.Effect = new EffectTile(tile.Effect.TileLoc); + } + } + } + } + //do thing to tile yield return CoroutineManager.Instance.StartCoroutine(actionContext.User.HitTile(actionContext)); } diff --git a/RogueEssence/Dungeon/Maps/BaseMap.cs b/RogueEssence/Dungeon/Maps/BaseMap.cs index 11fc8601..0e9a837a 100644 --- a/RogueEssence/Dungeon/Maps/BaseMap.cs +++ b/RogueEssence/Dungeon/Maps/BaseMap.cs @@ -120,6 +120,42 @@ public int GetItem(Loc loc) } return -1; } + + public bool TileAttackBlocked(Loc loc) + { + return TileAttackBlocked(loc, false); + } + + public bool TileAttackBlocked(Loc loc, bool inclusive) + { + return TileAttackBlocked(loc, inclusive, false); + } + + public bool TileAttackBlocked(Loc loc, bool inclusive, bool diagonal) + { + return TileAttackBlocked(loc, inclusive ? TerrainData.Mobility.All : TerrainData.Mobility.Passable, diagonal); + } + + public bool TileAttackBlocked(Loc loc, TerrainData.Mobility mobility) + { + return TileAttackBlocked(loc, mobility, false); + } + + public bool TileAttackBlocked(Loc loc, TerrainData.Mobility mobility, bool diagonal) + { + Tile tile = Tiles[loc.X][loc.Y]; + if (!String.IsNullOrEmpty(tile.Effect.ID)) + { + TileData effect = DataManager.Instance.GetTile(tile.Effect.ID); + if (effect.StepType == TileData.TriggerType.Object) + { + //Objects should allow attacks through but not movement + return false; + } + } + + return TileBlocked(loc, mobility, diagonal); + } public bool TileBlocked(Loc loc) { @@ -185,7 +221,7 @@ public bool EffectTileBlocked(TileData effect, bool diagonal) //doesn't apply here; for now, assume all effects block diagonal if they block at all //if (diagonal && !effect.BlockDiagonal) // return false; - if (effect.StepType == TileData.TriggerType.Unlockable || effect.StepType == TileData.TriggerType.Blocker) + if (effect.StepType == TileData.TriggerType.Unlockable || effect.StepType == TileData.TriggerType.Blocker || effect.StepType == TileData.TriggerType.Object) return true; return false; diff --git a/RogueEssence/Dungeon/Tiles/EffectTile.cs b/RogueEssence/Dungeon/Tiles/EffectTile.cs index 46f342e6..0a63736d 100644 --- a/RogueEssence/Dungeon/Tiles/EffectTile.cs +++ b/RogueEssence/Dungeon/Tiles/EffectTile.cs @@ -120,6 +120,23 @@ public IEnumerator LandedOnTile(Character character) yield break; } } + + public IEnumerator OnTileDestroyed(Character character) + { + SingleCharContext context = new SingleCharContext(character); + // should the context be extended to the caller? + DungeonScene.EventEnqueueFunction function = (StablePriorityQueue> queue, Priority maxPriority, ref Priority nextPriority) => + { + TileData entry = DataManager.Instance.GetTile(ID); + AddEventsToQueue(queue, maxPriority, ref nextPriority, entry.OnTileDestroyed, context.User); + }; + foreach (EventQueueElement effect in DungeonScene.IterateEvents(function)) + { + yield return CoroutineManager.Instance.StartCoroutine(effect.Event.Apply(effect.Owner, effect.OwnerChar, context)); + if (context.CancelState.Cancel) + yield break; + } + } public void DrawDebug(SpriteBatch spriteBatch, Loc offset) { } public void Draw(SpriteBatch spriteBatch, Loc offset)