diff options
| author | Matheus <matheus.guedes.mg.m@gmail.com> | 2025-08-25 17:26:01 -0300 |
|---|---|---|
| committer | Matheus <matheus.guedes.mg.m@gmail.com> | 2025-08-25 17:26:01 -0300 |
| commit | 35660f002898cd2382567696890d3fbc4d21e763 (patch) | |
| tree | 5b69cd8857e60b614ad01344abfb48400d6dd25c | |
| parent | 832fe5e98842123bcc9d0c3245babf172bb10578 (diff) | |
FOV
| -rw-r--r-- | assets/definitions/tiles/floor.tres | 1 | ||||
| -rw-r--r-- | scenes/Game.tscn | 6 | ||||
| -rw-r--r-- | scripts/Game.cs | 12 | ||||
| -rw-r--r-- | scripts/map/FieldOfView.cs | 94 | ||||
| -rw-r--r-- | scripts/map/FieldOfView.cs.uid | 1 | ||||
| -rw-r--r-- | scripts/map/Map.cs | 19 | ||||
| -rw-r--r-- | scripts/map/MapData.cs | 4 | ||||
| -rw-r--r-- | scripts/map/Tile.cs | 25 | ||||
| -rw-r--r-- | scripts/map/TileDefinition.cs | 2 |
9 files changed, 157 insertions, 7 deletions
diff --git a/assets/definitions/tiles/floor.tres b/assets/definitions/tiles/floor.tres index 4cd3e9c..7fa64ea 100644 --- a/assets/definitions/tiles/floor.tres +++ b/assets/definitions/tiles/floor.tres @@ -7,4 +7,5 @@ script = ExtResource("1_snxyj") Texture = ExtResource("1_vvyfi") IsWalkable = true +IsTransparent = true metadata/_custom_type_script = "uid://ba82a33ov6uuo" diff --git a/scenes/Game.tscn b/scenes/Game.tscn index 8948ee9..58690de 100644 --- a/scenes/Game.tscn +++ b/scenes/Game.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=3 uid="uid://u5h6iqyi8wd0"] +[gd_scene load_steps=6 format=3 uid="uid://u5h6iqyi8wd0"] [ext_resource type="Script" uid="uid://dwubb28wt4bhe" path="res://scripts/Game.cs" id="1_cpr0p"] [ext_resource type="Script" uid="uid://fe2h4is11tnw" path="res://scripts/map/Map.cs" id="3_cpr0p"] [ext_resource type="Script" uid="uid://ejqmdbc0524i" path="res://scripts/InputHandler.cs" id="3_s0nni"] [ext_resource type="Script" uid="uid://dwyr067lwqcsj" path="res://scripts/map/DungeonGenerator.cs" id="4_78awf"] +[ext_resource type="Script" uid="uid://bereyrj1s46y5" path="res://scripts/map/FieldOfView.cs" id="5_s0nni"] [node name="Game" type="Node"] script = ExtResource("1_cpr0p") @@ -20,6 +21,9 @@ seed = 989 useSeed = false iterations = 8 +[node name="FieldOfView" type="Node" parent="Map"] +script = ExtResource("5_s0nni") + [node name="Actors" type="Node2D" parent="."] [node name="Camera2D" type="Camera2D" parent="."] 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<ActorDefinition>("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"); + Map = GetNode<Map>("Map"); inputHandler = GetNode<InputHandler>("InputHandler"); actorsNode = GetNode<Node2D>("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<Tile> 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<DungeonGenerator>("Generator"); + fieldOfView = GetNode<FieldOfView>("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<DungeonGenerator>("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; } } |
