summaryrefslogtreecommitdiff
path: root/Units
diff options
context:
space:
mode:
Diffstat (limited to 'Units')
-rw-r--r--Units/Unit.gd256
-rw-r--r--Units/Unit.tscn75
-rw-r--r--Units/UnitPathLine.tscn6
-rw-r--r--Units/unit_path_line.gd34
4 files changed, 371 insertions, 0 deletions
diff --git a/Units/Unit.gd b/Units/Unit.gd
new file mode 100644
index 0000000..6c3b254
--- /dev/null
+++ b/Units/Unit.gd
@@ -0,0 +1,256 @@
+class_name Unit
+extends CharacterBody2D
+
+
+signal selected
+
+static var selected_unit: Unit
+
+var is_selected = false :
+ set(value):
+ if value:
+ Unit.selected_unit = self
+ selected.emit()
+ $Label.visible = true
+ line.visible = true
+ else:
+ if Unit.selected_unit == self:
+ Unit.selected_unit = null
+ $Label.visible = false
+ line.visible = false
+ is_selected = value
+ queue_redraw()
+
+var is_hovered = false :
+ set(value):
+ is_hovered = value
+ queue_redraw()
+
+var target: Node2D :
+ set(value):
+ target = value
+ reset_path()
+var immediate_target: Vector2
+
+var current_path: PackedVector2Array
+var current_path_idx = 0 :
+ set(value):
+ current_path_idx = value
+ if line:
+ line.points = PackedVector2Array(current_path.slice(value))
+
+var previous_path: PackedVector2Array
+var previous_path_idx = 0
+
+var previous_position: Vector2
+var recent_closest_paths: Array[PackedVector2Array]
+
+var stuck_position_accumulator = 0.0
+
+var roaming_mode = false
+
+# rpc owner id
+var owner_id = 1
+
+# unique shared id on the network on all clients
+var network_id
+
+@export var base_speed: float = 100
+@export var speed: float = base_speed
+@export var hp = 50
+
+@onready var line: Line2D = $UnitPathLine.duplicate()
+
+
+func _ready():
+ if not target:
+ target = Client.stage.get_node("%Goal")
+
+ Client.stage.units.add_child(line)
+
+ reset_path()
+ Client.stage.path_grid_changed.connect(func():
+ # TODO: get current position from owner_id rpc
+
+ var has_intersection = Client.array_intersect(current_path, Stage.last_solid_set_points)
+ # bug: when setting two towers in close succession, new tower overwrites old last solid points
+ #print(current_path)
+ #print(Stage.last_solid_set_points)
+ if has_intersection:
+ #print("true")
+ reset_path()
+ current_path_idx = min(1, current_path.size() - 1)
+ )
+
+ %HPBar.init(hp)
+ set_hp(hp)
+
+ $SelectionArea/CollisionShape2D.shape.size = $Sprite2D.texture.get_size() * $Sprite2D.scale
+
+
+func _physics_process(delta):
+ previous_position = global_position
+
+ if not current_path.is_empty():
+ if (global_position - current_path[current_path_idx]).is_zero_approx():
+ current_path_idx += 1
+ if current_path_idx >= current_path.size():
+ reset_path()
+
+ walk_along_path(current_path, current_path_idx, delta)
+
+ if roaming_mode:
+ var collision = get_last_slide_collision()
+ if collision:
+ var tower = collision.get_collider() as Node2D
+ Client.destroy_tower(tower)
+
+ # if unit stuck in tower
+ var position_difference = abs(previous_position - global_position)
+ if position_difference.x < 0.1 and position_difference.y < 0.1:
+ stuck_position_accumulator += delta
+ if stuck_position_accumulator >= 1.0:
+ reset_path()
+ else:
+ stuck_position_accumulator = 0
+
+
+func _draw():
+ if is_selected:
+ draw_circle(
+ Vector2.ZERO,
+ Client.stage.map.tile_set.tile_size.x * 0.75,
+ Color(1, 1, 1, 0.75),
+ false,
+ 1.0
+ )
+ modulate = Color(1.5, 1.5, 1.5)
+ elif is_hovered:
+ draw_circle(
+ Vector2.ZERO,
+ Client.stage.map.tile_set.tile_size.x * 0.75,
+ Color(1, 1, 1, 0.5),
+ false,
+ 1.0
+ )
+ modulate = Color(1.25, 1.25, 1.25)
+ else:
+ modulate = Color(1, 1, 1)
+
+
+func _on_navigation_base_area_entered(area: Area2D):
+ if area.is_in_group("goal"):
+ Client.player.score += 1
+ queue_free()
+ if area.is_in_group("path"):
+ var path_node = area.get_parent()
+ if path_node.path_position == target.path_position:
+ target = path_node.next_node
+
+
+func walk_along_path(path: PackedVector2Array, index: int, delta: float):
+ immediate_target = path[index]
+ var displacement := (path[index]) - global_position
+ var direction := displacement.normalized()
+ var distance := displacement.length()
+
+ var max_speed: float = (distance / delta)
+ velocity = direction * minf(speed, max_speed)
+
+ for effect in get_effects():
+ if effect.has_method("apply_physics"):
+ effect.apply_physics(delta)
+ # todo: changing velocity here doesn't work nicely
+ # todo: because the velocity expects to each the point, so it stutters
+
+ move_and_slide()
+
+
+func set_hp(value):
+ # TODO: rpc on damage
+
+ hp = value
+ %HPBar.set_value(value)
+
+ $Label.text = str(hp)
+
+ if hp <= 0:
+ queue_free()
+
+
+func get_effects():
+ var effects = []
+ for node in get_children():
+ if is_instance_of(node, Effect):
+ effects.append(node)
+
+ return effects
+
+
+func reset_path():
+ roaming_mode = false
+ current_path = get_grid_path()
+
+ if current_path.is_empty():
+ current_path = get_grid_path(true)
+ recent_closest_paths.append(current_path)
+
+ # reached end of partial path
+ if current_path.size() == 1 and current_path[0] == global_position:
+ roaming_mode = true
+ current_path = PackedVector2Array([target.path_position + Vector2(16,16)])
+
+ # iterating between one or more closest paths
+ elif recent_closest_paths.count(current_path) >= 2:
+ roaming_mode = true
+ current_path = PackedVector2Array([target.path_position + Vector2(16,16)])
+ recent_closest_paths = []
+ else:
+ recent_closest_paths = []
+ roaming_mode = false
+
+ current_path_idx = 0
+
+ if line:
+ line.points = PackedVector2Array(current_path)
+
+
+func get_grid_path(partial = false):
+ return Client.stage.path_grid.get_point_path(
+ Client.stage.map.local_to_map(global_position),
+ Client.stage.map.local_to_map(target.path_position),
+ partial
+ )
+
+
+func _on_selection_area_input_event(_viewport: Node, event: InputEvent, _shape_idx: int) -> void:
+ if Client.state is StateDefault:
+ if event.is_action_pressed("select"):
+ if selected_unit:
+ selected_unit.is_selected = false
+ is_selected = true
+ $Label.text = str(hp)
+ add_child(preload("res://Effects/SlowEffect.tscn").instantiate())
+
+
+func _on_selection_area_mouse_entered() -> void:
+ is_hovered = true
+
+func _on_selection_area_mouse_exited() -> void:
+ is_hovered = false
+
+
+func _on_tree_exiting() -> void:
+ is_selected = false
+ line.queue_free()
+
+
+func get_rpc_properties():
+ return {
+ "global_position": null,
+ "target": "res://Stages/Paths/PathNode.tscn",
+ "hp": null,
+ "speed": null,
+ "current_path": null,
+ "current_path_idx": null,
+ }
diff --git a/Units/Unit.tscn b/Units/Unit.tscn
new file mode 100644
index 0000000..312389e
--- /dev/null
+++ b/Units/Unit.tscn
@@ -0,0 +1,75 @@
+[gd_scene load_steps=8 format=3 uid="uid://cslaufgh6ber3"]
+
+[ext_resource type="Script" path="res://Units/Unit.gd" id="1_bbcew"]
+[ext_resource type="Texture2D" uid="uid://dsy7k2v5fhh6v" path="res://Assets/Mobs/angesnow-front.png" id="2_rxqq1"]
+[ext_resource type="PackedScene" uid="uid://bjcrf4o4a80iv" path="res://UI/HPBar.tscn" id="3_e777u"]
+[ext_resource type="PackedScene" uid="uid://cifs0kcy5r0x2" path="res://Units/UnitPathLine.tscn" id="4_r0qfv"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_1cqix"]
+size = Vector2(3, 3)
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_iaxxs"]
+size = Vector2(8, 8)
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_o5ax3"]
+size = Vector2(16, 16)
+
+[node name="Unit" type="CharacterBody2D"]
+y_sort_enabled = true
+collision_mask = 4
+input_pickable = true
+script = ExtResource("1_bbcew")
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+texture_filter = 1
+scale = Vector2(0.5, 0.5)
+texture = ExtResource("2_rxqq1")
+region_rect = Rect2(480, 880, 96, 64)
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("RectangleShape2D_1cqix")
+
+[node name="NavigationBase" type="Area2D" parent="." groups=["mob_navigation_base"]]
+collision_mask = 2
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="NavigationBase"]
+shape = SubResource("RectangleShape2D_iaxxs")
+
+[node name="HPBar" parent="." instance=ExtResource("3_e777u")]
+unique_name_in_owner = true
+offset_left = -10.0
+offset_top = -11.0
+offset_right = 10.0
+offset_bottom = -9.0
+
+[node name="Label" type="Label" parent="."]
+visible = false
+offset_left = -9.0
+offset_top = 12.0
+offset_right = 10.0
+offset_bottom = 24.0
+theme_override_font_sizes/font_size = 8
+text = "1000"
+horizontal_alignment = 1
+
+[node name="SelectionArea" type="Area2D" parent="."]
+collision_layer = 16
+collision_mask = 0
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="SelectionArea"]
+shape = SubResource("RectangleShape2D_o5ax3")
+
+[node name="UnitPathLine" parent="." instance=ExtResource("4_r0qfv")]
+visible = false
+width = 1.0
+default_color = Color(1, 1, 1, 0.392157)
+target_circle_radius = 4.0
+
+[connection signal="input_event" from="." to="." method="_on_input_event"]
+[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
+[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
+[connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"]
+[connection signal="area_entered" from="NavigationBase" to="." method="_on_navigation_base_area_entered"]
+[connection signal="input_event" from="SelectionArea" to="." method="_on_selection_area_input_event"]
+[connection signal="mouse_entered" from="SelectionArea" to="." method="_on_selection_area_mouse_entered"]
+[connection signal="mouse_exited" from="SelectionArea" to="." method="_on_selection_area_mouse_exited"]
diff --git a/Units/UnitPathLine.tscn b/Units/UnitPathLine.tscn
new file mode 100644
index 0000000..62458c4
--- /dev/null
+++ b/Units/UnitPathLine.tscn
@@ -0,0 +1,6 @@
+[gd_scene load_steps=2 format=3 uid="uid://cifs0kcy5r0x2"]
+
+[ext_resource type="Script" path="res://Units/unit_path_line.gd" id="1_qbhs7"]
+
+[node name="UnitPathLine" type="Line2D"]
+script = ExtResource("1_qbhs7")
diff --git a/Units/unit_path_line.gd b/Units/unit_path_line.gd
new file mode 100644
index 0000000..e7bd4bc
--- /dev/null
+++ b/Units/unit_path_line.gd
@@ -0,0 +1,34 @@
+extends Line2D
+
+
+signal points_changed
+
+@export_group("Target Circle", "target_circle")
+@export_custom(PROPERTY_HINT_NONE, "suffix:px") var target_circle_radius: float = 0.0
+@export var target_circle_color: Color = Color(0,0,0,0)
+
+@onready var last_points = points
+
+
+func _ready():
+ points_changed.connect(func():
+ queue_redraw()
+ )
+
+ if target_circle_color == Color(0,0,0,0):
+ target_circle_color = default_color
+
+
+func _process(_delta):
+ if last_points == points:
+ points_changed.emit()
+
+ last_points = points
+
+
+func _draw():
+ draw_circle(
+ points[points.size() - 1] - global_position,
+ target_circle_radius,
+ default_color
+ )