diff --git a/debug/debug_highlight_tester.gd b/debug/debug_highlight_tester.gd new file mode 100644 index 0000000..5c4fdcd --- /dev/null +++ b/debug/debug_highlight_tester.gd @@ -0,0 +1,11 @@ +extends Node2D + +@export var grid: Grid + + +func _on_timer_timeout() -> void: + GameplaySignalBus.highlight_empty.emit(grid.raycast_empty(global_position,transform.x)) + rotation_degrees += 5 + +func _process(delta: float) -> void: + global_position = get_global_mouse_position() diff --git a/debug/debug_highlight_tester.gd.uid b/debug/debug_highlight_tester.gd.uid new file mode 100644 index 0000000..56d5b49 --- /dev/null +++ b/debug/debug_highlight_tester.gd.uid @@ -0,0 +1 @@ +uid://c7i44ghoo4kqo diff --git a/project.godot b/project.godot index e9f75fe..cbfa5e6 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,20 @@ run/main_scene="uid://bxt4kgs6px3t8" config/features=PackedStringArray("4.6", "GL Compatibility") config/icon="res://icon.svg" +[autoload] + +GameplaySignalBus="*uid://cqh0t2i1slf8u" + +[debug] + +gdscript/warnings/integer_division=0 + +[display] + +window/size/viewport_width=1920 +window/size/viewport_height=1080 +window/stretch/mode="canvas_items" + [internationalization] locale/translations=PackedStringArray("res://translations/units.ru.translation", "res://translations/units.en.translation") diff --git a/scenes/game.tscn b/scenes/game.tscn index 743d78f..5809808 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -6,6 +6,10 @@ [ext_resource type="Resource" uid="uid://e7kiu3wcf0ff" path="res://cards/resources/fosma.tres" id="3_u5sy4"] [ext_resource type="Resource" uid="uid://djqlx7bmcs5gj" path="res://cards/resources/doh.tres" id="4_gee14"] [ext_resource type="Resource" uid="uid://b6kiwb0marpng" path="res://cards/resources/zlosma.tres" id="5_0tnpc"] +[ext_resource type="Script" uid="uid://rt6mxr30u8tp" path="res://systems/grid.gd" id="7_u5sy4"] +[ext_resource type="Script" uid="uid://bmy5djjmiljhl" path="res://systems/grid_highlighter.gd" id="8_gee14"] +[ext_resource type="Script" uid="uid://c7i44ghoo4kqo" path="res://debug/debug_highlight_tester.gd" id="9_0tnpc"] +[ext_resource type="Texture2D" uid="uid://du3h8ou2sxh6s" path="res://units/fosma/fosma.png" id="10_vtaks"] [node name="Game" type="Node2D" unique_id=619820575] @@ -13,9 +17,14 @@ texture = ExtResource("1_iywne") centered = false +[node name="Fosma" type="Sprite2D" parent="." unique_id=128278442] +position = Vector2(511, 278) +texture = ExtResource("10_vtaks") + [node name="PlayerInterface" type="CanvasLayer" parent="." unique_id=269857956] [node name="Hand" type="Control" parent="PlayerInterface" unique_id=403305026] +visible = false layout_mode = 3 anchors_preset = 12 anchor_top = 1.0 @@ -31,9 +40,10 @@ layout_mode = 0 anchors_preset = 0 anchor_right = 0.0 anchor_bottom = 0.0 -offset_left = 402.0 -offset_right = 515.0 -offset_bottom = 169.0 +offset_left = 733.9999 +offset_top = -168.99997 +offset_right = 846.9999 +offset_bottom = 3.0517578e-05 card_resource = ExtResource("3_u5sy4") [node name="Card2" parent="PlayerInterface/Hand" unique_id=1229293312 instance=ExtResource("2_p57ef")] @@ -41,9 +51,10 @@ layout_mode = 0 anchors_preset = 0 anchor_right = 0.0 anchor_bottom = 0.0 -offset_left = 519.0 -offset_right = 632.0 -offset_bottom = 169.0 +offset_left = 846.9999 +offset_top = -168.99997 +offset_right = 959.99994 +offset_bottom = 3.0517578e-05 card_resource = ExtResource("4_gee14") [node name="Card4" parent="PlayerInterface/Hand" unique_id=1427311772 instance=ExtResource("2_p57ef")] @@ -51,9 +62,10 @@ layout_mode = 0 anchors_preset = 0 anchor_right = 0.0 anchor_bottom = 0.0 -offset_left = 804.0 -offset_right = 917.0 -offset_bottom = 169.0 +offset_left = 959.9999 +offset_top = -168.99997 +offset_right = 1072.9999 +offset_bottom = 3.0517578e-05 card_resource = ExtResource("3_u5sy4") [node name="Card3" parent="PlayerInterface/Hand" unique_id=1907397406 instance=ExtResource("2_p57ef")] @@ -61,7 +73,38 @@ layout_mode = 0 anchors_preset = 0 anchor_right = 0.0 anchor_bottom = 0.0 -offset_left = 636.0 -offset_right = 749.0 -offset_bottom = 169.0 +offset_left = 1072.9998 +offset_top = -168.99997 +offset_right = 1185.9998 +offset_bottom = 3.0517578e-05 card_resource = ExtResource("5_0tnpc") + +[node name="Grid" type="Node2D" parent="." unique_id=1478060666 node_paths=PackedStringArray("right_border_marker")] +position = Vector2(398, 162) +script = ExtResource("7_u5sy4") +rows = 5 +columns = 5 +right_border_marker = NodePath("../GridMarker") +metadata/_custom_type_script = "uid://rt6mxr30u8tp" + +[node name="Highlighter" type="Node2D" parent="Grid" unique_id=69750982] +script = ExtResource("8_gee14") + +[node name="GridMarker" type="Marker2D" parent="." unique_id=1656515901] +position = Vector2(801, 567) + +[node name="Camera2D" type="Camera2D" parent="." unique_id=1988509991] +position = Vector2(0, 142) +anchor_mode = 0 +zoom = Vector2(1.5, 1.5) + +[node name="HighlightTester" type="Node2D" parent="." unique_id=729091083 node_paths=PackedStringArray("grid")] +position = Vector2(602, 362) +script = ExtResource("9_0tnpc") +grid = NodePath("../Grid") + +[node name="Timer" type="Timer" parent="HighlightTester" unique_id=1589504194] +wait_time = 0.1 +autostart = true + +[connection signal="timeout" from="HighlightTester/Timer" to="HighlightTester" method="_on_timer_timeout"] diff --git a/systems/gameplay_signal_bus.gd b/systems/gameplay_signal_bus.gd new file mode 100644 index 0000000..716201d --- /dev/null +++ b/systems/gameplay_signal_bus.gd @@ -0,0 +1,6 @@ +extends Node + +@warning_ignore_start("unused_signal") + +signal highlight_empty(empty: Array[int]) +signal highlight_units(unit: Array[int]) diff --git a/systems/gameplay_signal_bus.gd.uid b/systems/gameplay_signal_bus.gd.uid new file mode 100644 index 0000000..aa9f8dc --- /dev/null +++ b/systems/gameplay_signal_bus.gd.uid @@ -0,0 +1 @@ +uid://cqh0t2i1slf8u diff --git a/systems/grid.gd b/systems/grid.gd new file mode 100644 index 0000000..8a26b15 --- /dev/null +++ b/systems/grid.gd @@ -0,0 +1,169 @@ +extends Node2D + + +## Grid with cells in it +class_name Grid + +## Out of bounds return value +const OUT_OF_BOUNDS: int = -1 + +## Amount of rows (max Y value) +@export var rows: int = 1 +## Amount of columns (max X value) +@export var columns: int = 1 + +## Node that marks right down position of grid +@export var right_border_marker: Node2D + +## Computed size of one cell +@onready var cell_size = (right_border_marker.global_position - global_position) / Vector2(columns,rows) +## Computed rect of grid +@onready var rect: Rect2 = Rect2(global_position,(right_border_marker.global_position - global_position)) + +## Array that holds units +var grid: Array[Unit] = [] + +func _ready() -> void: + grid.resize(rows*columns) + +## Returns true if position is in bounds else returns false. +func is_position_valid(pos: Vector2) -> bool: + return rect.has_point(pos) + +## Turns position into internal array index. Returns -1 if position is incorrect +func to_index(pos: Vector2) -> int: + if is_position_valid(pos) == false: return OUT_OF_BOUNDS + var snapped_position: Vector2 = (pos - global_position)/cell_size + return int(snapped_position.x) + columns * int(snapped_position.y) + +## Turns index into global world position +func from_index(index: int, centered: bool = false) -> Vector2: + if index < 0 or index >= columns * rows: + return Vector2.ZERO + return Vector2(index % columns, index / columns) * cell_size + (cell_size/2.0 if centered else Vector2.ZERO) + +## Snaps position through grid +func snap_position(pos: Vector2, centered: bool = false) -> Vector2: + return global_position + (pos - global_position).snapped(cell_size) + (cell_size / 2) if centered else Vector2.ZERO + +## Returns position as grid snapped local coordinates +func to_snapped(pos: Vector2) -> Vector2: + if is_position_valid(pos) == false: return Vector2.ZERO + var snapped_position: Vector2 = (pos - global_position)/cell_size + return Vector2(floor(snapped_position.x),floor(snapped_position.y)) + +## Tries to set unit at position and returns false if unit already exists or out of bounds +func try_set_unit(pos: Vector2, unit: Unit) -> bool: + var index: int = to_index(pos) + if index == OUT_OF_BOUNDS: + return false + + if grid[index] != null: + return false + + grid[index] = unit + return true + +## Returns true if cell is occupied or out of bounds and false if cell is empty. +func is_occupied(pos: Vector2) -> bool: + var index: int = to_index(pos) + if index == OUT_OF_BOUNDS: + return true + + return grid[index] != null + +## Returns units with given rule predicate.[br] +## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool +## that needs to return true if cell needs to be checked.[br] +## unit - source unit. +func get_units(rule: Callable,unit: Unit) -> Array[Unit]: + var unit_pos = to_snapped(unit.global_position) + var result: Array[Unit] = [] + for i in range(columns * rows): + if rule.call(i%columns,i/columns,int(unit_pos.x),int(unit_pos.y)) and grid[i] != null: + result.append(grid[i]) + + return result + +## WIP[br] +## Returns units with given rule predicate. Checks from source unit with rays and stops only when ray failed.[br] +## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool +## that needs to return true if cell needs to be checked.[br] +## unit - source unit.[br] +## rays - amount of rays to check from unit. +func get_units_stoppable(rule: Callable, unit: Unit, rays: int = 8) -> Array[Unit]: + return [] + +## Returns empty cells with given rule predicate.[br] +## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool +## that needs to return true if cell needs to be checked.[br] +## unit - source unit. +func get_empty(rule: Callable, unit: Unit) -> Array[int]: + var unit_pos = to_snapped(unit.global_position) + var result: Array[int] = [] + for i: int in range(columns * rows): + if rule.call(i%columns,i/columns,int(unit_pos.x),int(unit_pos.y)) and grid[i] == null: + result.append(i) + + return result + +## WIP[br] +## Returns empty with given rule predicate. Checks from source unit with rays and stops only when ray failed.[br] +## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool +## that needs to return true if cell needs to be checked.[br] +## unit - source unit.[br] +## rays - amount of rays to check from unit. +func get_empty_stoppable(rule: Callable, unit: Unit, rays: int = 8) -> Array[int]: + return [] + +## Casts a ray along [code]direction[/code] from [code]from[/code] to find unit. If distance is given, restricts lookup to [code]distance[/code] cells. +func raycast_unit(from: Vector2,direction: Vector2,distance: float = -1) -> Unit: + if not direction.is_normalized(): + direction = direction.normalized() + var result = null + + var to: Vector2 = from + direction * rows * columns + var dv: Vector2 = to.floor() - from.floor() + var step = abs(dv.x) + 1 if abs(dv.x) > abs(dv.y) else abs(dv.y) + 1 + var check_pos = from + + while is_position_valid(check_pos) and distance != 0: + check_pos += dv/step + distance -= 1 + + return result + +## Casts a ray along [code]direction[/code] from [code]from[/code] to find all empty cells. If distance is given, restricts lookup to [code]distance[/code] cells. +func raycast_empty(from: Vector2,direction: Vector2,distance: float = -1) -> Array[int]: + if not direction.is_normalized(): + direction = direction.normalized() + var result: Array[int] = [] + + var dv: Vector2 = (Vector2.ONE/direction).round() + var steps_y: int = int(abs(dv.y)) + var steps_x: int = int(abs(dv.x)) + var check_pos = from.floor() + var change_y: bool = true if abs(dv.y) > abs(dv.x) else false + + while is_position_valid(check_pos) and distance != 0: + if steps_x == 0: + steps_x = int(abs(dv.x)) + change_y = true + elif steps_y == 0: + steps_y = int(abs(dv.y)) + change_y = false + + if change_y: + check_pos += Vector2(0,sign(dv.y)) * cell_size + steps_y-=1 + else: + check_pos += Vector2(sign(dv.x),0) * cell_size + steps_x -= 1 + + var index = to_index(check_pos) + if index == -1: + break + if grid[index] == null: + result.append(index) + + return result diff --git a/systems/grid.gd.uid b/systems/grid.gd.uid new file mode 100644 index 0000000..422a87b --- /dev/null +++ b/systems/grid.gd.uid @@ -0,0 +1 @@ +uid://rt6mxr30u8tp diff --git a/systems/grid_highlighter.gd b/systems/grid_highlighter.gd new file mode 100644 index 0000000..5adbca1 --- /dev/null +++ b/systems/grid_highlighter.gd @@ -0,0 +1,29 @@ +extends Node2D + +@onready var grid: Grid = get_parent() + +const UNIT_COLOR = Color.CHOCOLATE +const EMPTY_COLOR = Color(Color.CORNFLOWER_BLUE,1.0) + +var units: Array[Vector2] +var empty: Array[Vector2] + +func _ready() -> void: + GameplaySignalBus.highlight_empty.connect(on_highlight_empty) + GameplaySignalBus.highlight_units.connect(on_highlight_units) + +func _draw() -> void: + for unit in units: + draw_rect(Rect2(unit,grid.cell_size),UNIT_COLOR) + for empty_cell in empty: + draw_rect(Rect2(empty_cell,grid.cell_size),EMPTY_COLOR) + +func on_highlight_units(new_units: Array[Unit]): + units.clear() + units.assign(new_units.map(func(unit : Unit): return grid.snap_position(unit.global_position))) + queue_redraw() + +func on_highlight_empty(new_empty: Array[int]): + empty.clear() + empty.assign(new_empty.map(func(cell: int): return grid.from_index(cell))) + queue_redraw() diff --git a/systems/grid_highlighter.gd.uid b/systems/grid_highlighter.gd.uid new file mode 100644 index 0000000..56cd298 --- /dev/null +++ b/systems/grid_highlighter.gd.uid @@ -0,0 +1 @@ +uid://bmy5djjmiljhl diff --git a/translations/units.en.translation b/translations/units.en.translation index 7170bd9..8e57892 100644 Binary files a/translations/units.en.translation and b/translations/units.en.translation differ diff --git a/translations/units.ru.translation b/translations/units.ru.translation index 78183af..5cc89d2 100644 Binary files a/translations/units.ru.translation and b/translations/units.ru.translation differ diff --git a/units/unit.gd b/units/unit.gd new file mode 100644 index 0000000..5838e08 --- /dev/null +++ b/units/unit.gd @@ -0,0 +1,5 @@ +extends Node2D + +class_name Unit + +signal on_death diff --git a/units/unit.gd.uid b/units/unit.gd.uid new file mode 100644 index 0000000..ae64109 --- /dev/null +++ b/units/unit.gd.uid @@ -0,0 +1 @@ +uid://db7simgenakts