From 35660f002898cd2382567696890d3fbc4d21e763 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 25 Aug 2025 17:26:01 -0300 Subject: FOV --- scripts/Game.cs | 12 ++++-- scripts/map/FieldOfView.cs | 94 ++++++++++++++++++++++++++++++++++++++++++ scripts/map/FieldOfView.cs.uid | 1 + scripts/map/Map.cs | 19 ++++++++- scripts/map/MapData.cs | 4 ++ scripts/map/Tile.cs | 25 +++++++++++ scripts/map/TileDefinition.cs | 2 + 7 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 scripts/map/FieldOfView.cs create mode 100644 scripts/map/FieldOfView.cs.uid (limited to 'scripts') diff --git a/scripts/Game.cs b/scripts/Game.cs index 06ef018..29a0666 100644 --- a/scripts/Game.cs +++ b/scripts/Game.cs @@ -6,13 +6,13 @@ public partial class Game : Node { private static readonly ActorDefinition skeletonDefinition = GD.Load("res://assets/definitions/actor/Skeleton.tres"); private Player player; private Node2D actorsNode; - public MapData Map_Data { get; private set; } + private Map Map; private InputHandler inputHandler; public override void _Ready() { base._Ready(); - Map Map = GetNode("Map"); + Map = GetNode("Map"); inputHandler = GetNode("InputHandler"); actorsNode = GetNode("Actors"); @@ -27,7 +27,7 @@ public partial class Game : Node { Map.Generate(player); - Map_Data = Map.Map_Data; + Map.UpdateFOV(player.GridPosition); } public override void _PhysicsProcess(double delta) { @@ -36,13 +36,17 @@ public partial class Game : Node { Action action = inputHandler.GetAction(player); if (action != null) { + Vector2I previousPlayerPos = player.GridPosition; action.Perform(); HandleEnemyTurns(); + if (player.GridPosition != previousPlayerPos) { + Map.UpdateFOV(player.GridPosition); + } } } private void HandleEnemyTurns() { - foreach (Actor actor in Map_Data.Actors) { + foreach (Actor actor in Map.Map_Data.Actors) { if (actor is Player) continue; GD.Print($"O {actor.ActorName} foi cuckado e não tem como agir."); diff --git a/scripts/map/FieldOfView.cs b/scripts/map/FieldOfView.cs new file mode 100644 index 0000000..eee1ae9 --- /dev/null +++ b/scripts/map/FieldOfView.cs @@ -0,0 +1,94 @@ +using Godot; + +// Copiado e adaptado deste cara aqui: https://www.roguebasin.com/index.php?title=C%2B%2B_shadowcasting_implementation e deste também https://selinadev.github.io/08-rogueliketutorial-04/ + +public partial class FieldOfView : Node { + + private Godot.Collections.Array fov = []; + + private static int[,] multipliers = new int[4,8]{ + {1, 0, 0, -1, -1, 0, 0, 1}, + {0, 1, -1, 0, 0, -1, 1, 0}, + {0, 1, 1, 0, 0, -1, -1, 0}, + {1, 0, 0, 1, -1, 0, 0, -1} + }; + private void CastLight(MapData data, Vector2I pos, int radius, int row, float startSlope, float endSlope, int xx, int xy, int yx, int yy) { + if (startSlope < endSlope) { + return; + } + + float nextStartSlope = startSlope; + for (int i = row; i <= radius; i++) + { + bool blocked = false; + for (int dx = -i, dy = -i; dx <= 0; dx++) + { + float lSlope = (float)((dx - 0.5) / (dy + 0.5)); + float rSlope = (float)((dx + 0.5) / (dy - 0.5)); + + if (startSlope < rSlope) + { + continue; + } + else if (endSlope > lSlope) + { + break; + } + + int sax = dx * xx + dy * xy; + int say = dx * yx + dy * yy; + + if ((sax < 0 && int.Abs(sax) > pos.X) || (say < 0 && int.Abs(say) > pos.Y)) { + continue; + } + int ax = pos.X + sax; + int ay = pos.Y + say; + + if (ax >= data.Width || ay >= data.Height) { + continue; + } + + Tile currentTile = data.GetTile(ax, ay); + int radius2 = radius * radius; + if ((dx * dx + dy * dy) < radius2) { + currentTile.IsInView = true; + fov.Add(currentTile); + } + + if (blocked) { + if (!currentTile.IsTransparent) { + nextStartSlope = rSlope; + continue; + } else { + blocked = false; + startSlope = nextStartSlope; + } + } else if (!currentTile.IsTransparent) { + blocked = true; + nextStartSlope = rSlope; + CastLight(data, pos, radius, i + 1, startSlope, lSlope, xx, xy, yx, yy); + } + } + if (blocked) { + break; + } + } + } + + private void ClearFOV() { + foreach (Tile tile in fov) { + tile.IsInView = false; + } + fov.Clear(); + } + + public void UpdateFOV(MapData data, Vector2I position, int radius) { + ClearFOV(); + Tile start = data.GetTile(position); + start.IsInView = true; + fov.Add(start); + for (int i = 0; i < 8; i++) { + CastLight(data, position, radius, 1, 1.0f, 0.0f, multipliers[0, i], multipliers[1, i], multipliers[2, i], multipliers[3, i]); + } + } +} \ No newline at end of file diff --git a/scripts/map/FieldOfView.cs.uid b/scripts/map/FieldOfView.cs.uid new file mode 100644 index 0000000..a173ff3 --- /dev/null +++ b/scripts/map/FieldOfView.cs.uid @@ -0,0 +1 @@ +uid://bereyrj1s46y5 diff --git a/scripts/map/Map.cs b/scripts/map/Map.cs index 683ae45..41bd7f8 100644 --- a/scripts/map/Map.cs +++ b/scripts/map/Map.cs @@ -5,8 +5,21 @@ public partial class Map : Node2D { public MapData Map_Data { get; private set; } + [Export] + private int fovRadius = 12; + DungeonGenerator generator; + FieldOfView fieldOfView; + + public override void _Ready() + { + base._Ready(); + + generator = GetNode("Generator"); + fieldOfView = GetNode("FieldOfView"); + } + private void PlaceTiles() { foreach (Tile tile in Map_Data.Tiles) { AddChild(tile); @@ -15,8 +28,6 @@ public partial class Map : Node2D public void Generate(Player player) { - generator = GetNode("Generator"); - Map_Data = generator.GenerateDungeon(player); Map_Data.InsertActor(player); @@ -25,4 +36,8 @@ public partial class Map : Node2D PlaceTiles(); } + + public void UpdateFOV(Vector2I pos) { + fieldOfView.UpdateFOV(Map_Data, pos, fovRadius); + } } diff --git a/scripts/map/MapData.cs b/scripts/map/MapData.cs index c580aa8..1aba35d 100644 --- a/scripts/map/MapData.cs +++ b/scripts/map/MapData.cs @@ -60,6 +60,10 @@ public partial class MapData : RefCounted return Tiles[index]; } + public Tile GetTile(int x, int y) { + return GetTile(new Vector2I(x, y)); + } + public Actor GetBlockingActorAtPosition(Vector2I pos) { foreach (Actor actor in Actors) { if (actor.GridPosition == pos && actor.BlocksMovement) { diff --git a/scripts/map/Tile.cs b/scripts/map/Tile.cs index 865cbbd..e050701 100644 --- a/scripts/map/Tile.cs +++ b/scripts/map/Tile.cs @@ -6,10 +6,34 @@ public partial class Tile : Sprite2D private TileDefinition definition; public bool IsWalkable { get; private set; } + public bool IsTransparent { get; private set; } + + private bool isExplored = false; + public bool IsExplored { + get => this.isExplored; + set { + isExplored = value; + if (IsExplored && !Visible) { + Visible = true; + } + } + } + + private bool isInView = false; + public bool IsInView { + get => this.isInView; + set { + this.isInView = value; + if (IsInView && !IsExplored) { + IsExplored = true; + } + } + } public Tile(Vector2I pos, TileDefinition definition) { Centered = false; + Visible = false; Position = Grid.GridToWorld(pos); SetDefinition(definition); } @@ -18,5 +42,6 @@ public partial class Tile : Sprite2D this.definition = definition; Texture = definition.Texture; IsWalkable = definition.IsWalkable; + IsTransparent = definition.IsTransparent; } } diff --git a/scripts/map/TileDefinition.cs b/scripts/map/TileDefinition.cs index 548fda7..fbd14a1 100644 --- a/scripts/map/TileDefinition.cs +++ b/scripts/map/TileDefinition.cs @@ -11,4 +11,6 @@ public partial class TileDefinition : Resource [ExportCategory("Mechanics")] [Export] public bool IsWalkable { get; set; } + [Export] + public bool IsTransparent { get; set; } } -- cgit v1.2.3