diff options
Diffstat (limited to 'Units')
-rw-r--r-- | Units/Unit.gd | 256 | ||||
-rw-r--r-- | Units/Unit.tscn | 75 | ||||
-rw-r--r-- | Units/UnitPathLine.tscn | 6 | ||||
-rw-r--r-- | Units/unit_path_line.gd | 34 |
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 + ) |