From f20b3a48a14f34def41d823fabb476981ef7055e Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Wed, 6 Nov 2024 21:00:56 +0100 Subject: next commit --- Towers/Components/AttackComponent.gd | 77 ++++++++++++++++++ Towers/Components/BurnComponent.gd | 2 +- Towers/Components/FrostComponent.gd | 2 +- Towers/Components/RangeComponent.gd | 52 ++++++++++++ Towers/Components/TowerComponent.gd | 48 ++++++++--- Towers/Tower.gd | 154 +++++++++++++++++++---------------- Towers/Tower.tscn | 15 +++- 7 files changed, 266 insertions(+), 84 deletions(-) create mode 100644 Towers/Components/AttackComponent.gd create mode 100644 Towers/Components/RangeComponent.gd (limited to 'Towers') diff --git a/Towers/Components/AttackComponent.gd b/Towers/Components/AttackComponent.gd new file mode 100644 index 0000000..c7660cf --- /dev/null +++ b/Towers/Components/AttackComponent.gd @@ -0,0 +1,77 @@ +class_name AttackTowerComponent +extends TowerComponent + + +var power := 1 + +var speed_base := 1.0 +var speed := 1 + +var shoot_cooldown := Timer.new() +var shoot_sound := AudioStreamPlayer2D.new() + +@export var range_component: RangeTowerComponent: + get(): + return current_tower.components[ComponentType.Range] + + +func _init() -> void: + type = ComponentType.Attack + set_sprite(preload("res://Towers/Components/Assets/attack-component.png")) + + update_power() + update_speed() + level_changed.connect(update_power) + level_changed.connect(update_speed) + + +func update_power(): + power = ceil(level / 2.0) + +func update_speed(): + speed = level - ceil(level / 2.0) + shoot_cooldown.wait_time = speed_base / speed + +func update_range(): + shoot_sound.max_distance = range_component.range * Client.current_stage.map.tile_set.tile_size.x + + +func on_add(tower: Tower): + super.on_add(tower) + + shoot_cooldown.one_shot = true + tower.add_child(shoot_cooldown) + + shoot_sound.stream = preload("res://Towers/Assets/shoot.ogg") + tower.add_child(shoot_sound) + + update_range() + range_component.range_changed.connect(update_range) + + +func process(_delta: float): + if shoot_cooldown.is_stopped() and not range_component.units_in_range.is_empty(): + shoot() + shoot_cooldown.start() + + +func shoot(): + var target = range_component.units_in_range[0] + + for component in current_tower.components.values(): + if component.has_method("on_shoot"): + component.on_shoot(target) + + shoot_fx.rpc() + + if is_melee_range(): + target.set_hp(target.hp - 1) + else: # TODO + target.set_hp(target.hp - 1) + +@rpc("authority", "call_local") +func shoot_fx(): + shoot_sound.play() + +func is_melee_range(): + return range_component.range <= (Client.current_stage.map.tile_set.tile_size.x * 2) diff --git a/Towers/Components/BurnComponent.gd b/Towers/Components/BurnComponent.gd index 96c24f7..d708d4f 100644 --- a/Towers/Components/BurnComponent.gd +++ b/Towers/Components/BurnComponent.gd @@ -2,7 +2,7 @@ extends TowerComponent func _init() -> void: - name = "Burn" + type = ComponentType.Burn set_sprite(preload("res://Towers/Components/Assets/burn-component.png")) diff --git a/Towers/Components/FrostComponent.gd b/Towers/Components/FrostComponent.gd index 473baf6..2443b70 100644 --- a/Towers/Components/FrostComponent.gd +++ b/Towers/Components/FrostComponent.gd @@ -2,7 +2,7 @@ extends TowerComponent func _init() -> void: - name = "Frost" + type = ComponentType.Frost set_sprite(preload("res://Towers/Components/Assets/frost-component.png")) diff --git a/Towers/Components/RangeComponent.gd b/Towers/Components/RangeComponent.gd new file mode 100644 index 0000000..a2f7ae7 --- /dev/null +++ b/Towers/Components/RangeComponent.gd @@ -0,0 +1,52 @@ +class_name RangeTowerComponent +extends TowerComponent + + +signal range_changed +@warning_ignore("shadowed_global_identifier") +var range := 0: + set(value): + range = value + range_changed.emit() + +var area := Area2D.new() +var collision_shape := CollisionShape2D.new() +var shape := CircleShape2D.new() + +var units_in_range: Array[Unit] + + +func _init() -> void: + type = ComponentType.Range + set_sprite(preload("res://Towers/Components/Assets/range-component.png")) + + update_range() + level_changed.connect(update_range) + + +func update_range(): + var tile_size = Client.current_stage.map.tile_set.tile_size.x + @warning_ignore("integer_division") + range = (tile_size * 2) + ((tile_size / 2) * level) + shape.radius = range + + +func on_add(tower: Tower): + super.on_add(tower) + + area.set_collision_layer_value(1, false) + area.set_collision_mask_value(1, true) + + area.body_entered.connect(func(body: Node2D): + units_in_range.append(body) + ) + area.body_exited.connect(func(body: Node2D): + units_in_range.erase(body) + ) + + area.position = tower.get_node("CenterAnchor").position + + collision_shape.shape = shape + area.add_child(collision_shape) + + tower.add_child(area) diff --git a/Towers/Components/TowerComponent.gd b/Towers/Components/TowerComponent.gd index 609f88c..7ca766a 100644 --- a/Towers/Components/TowerComponent.gd +++ b/Towers/Components/TowerComponent.gd @@ -1,30 +1,58 @@ class_name TowerComponent -extends Resource +extends Node -var name: String -var sprite: Sprite2D = Sprite2D.new() -var level: int = 1 +var current_tower: Tower + +enum ComponentType { + Range, + Attack, + Speed, + Frost, + Burn, + Poison, +} + +var type: ComponentType +var sprite: TextureRect = TextureRect.new() + +var id: String: + get(): + return get_type_name() + +signal level_changed +var level: int = 1: + set(value): + level = value + level_changed.emit() func set_sprite(texture: Texture2D): sprite.texture = texture sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST - sprite.centered = false - sprite.scale = Vector2(2, 2) - sprite.name = name + #sprite.centered = false + sprite.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + #sprite.scale = Vector2(2, 2) + sprite.name = get_type_name() + + +func get_type_name(component_type: ComponentType = type): + return ComponentType.keys()[component_type] + +func on_add(tower: Tower): + current_tower = tower class NetworkData extends Resource: - var name: String + var type: ComponentType var level: int func to_network_data() -> NetworkData: var data = NetworkData.new() - data.name = name + data.type = type data.level = level return data @@ -35,7 +63,7 @@ func update_with_network_data(_data: NetworkData): static func from_network_data(data: NetworkData) -> TowerComponent: var component = preload("res://Towers/Components/TowerComponent.gd").new() - component.name = data.name + component.type = data.type component.level = data.level return component diff --git a/Towers/Tower.gd b/Towers/Tower.gd index 187cd93..1d856f3 100644 --- a/Towers/Tower.gd +++ b/Towers/Tower.gd @@ -40,7 +40,7 @@ var is_highlighted := false: is_highlighted = value queue_redraw() -var mobs_in_range: Array = [] +#var mobs_in_range: Array = [] #var selection_area: Area2D @@ -54,22 +54,22 @@ signal selection_group_id_changed(previous_id: String) # rpc owner id @export var owner_id = 1 -@export var attack_range: int = 32: - set(value): - attack_range = value - $Range/CollisionShape2D.shape.radius = attack_range - $SoundShoot.max_distance = attack_range * Client.current_stage.map.tile_set.tile_size.x +#@export var attack_range: int = 32: + #set(value): + #attack_range = value + #$Range/CollisionShape2D.shape.radius = attack_range + #$SoundShoot.max_distance = attack_range * Client.current_stage.map.tile_set.tile_size.x -@export var attack_power: int = 1 +#@export var attack_power: int = 1 -var attack_speed_base: float = 2.0 -@export var attack_speed: int = 1: - set(value): - attack_speed = value - %ShootCooldown.wait_time = attack_speed_base / attack_speed +#var attack_speed_base: float = 2.0 +#@export var attack_speed: int = 1: + #set(value): + #attack_speed = value + #%ShootCooldown.wait_time = attack_speed_base / attack_speed signal components_changed -@export var components: Array[TowerComponent] = [] +@export var components: Dictionary func _init(): @@ -79,6 +79,9 @@ func _init(): func _ready(): $AnimatedSprite2D.play() + for component in [RangeTowerComponent.new(), AttackTowerComponent.new()]: + add_component(component) + redraw_components() components_changed.connect(func(): selection_group_id = get_group_id() @@ -92,7 +95,7 @@ func _draw(): if Client.state is StateDefault: draw_circle( Vector2(Client.current_stage.map.tile_set.tile_size) / scale, - 8 + attack_range, + 8 + components.get(TowerComponent.ComponentType.Range).range, Color(1, 1, 1, 0.5), false, 1.0 @@ -112,11 +115,15 @@ func _draw(): ) -func _process(_delta: float) -> void: +func _process(delta: float) -> void: if multiplayer.is_server(): - if $ShootCooldown.is_stopped() and not mobs_in_range.is_empty(): - shoot() - $ShootCooldown.start() + for component in components.values(): + if component.has_method("process"): + component.process(delta) + + #if $ShootCooldown.is_stopped() and not mobs_in_range.is_empty(): + #shoot() + #$ShootCooldown.start() #if selection_area and is_instance_valid(selection_area): #var bodies = selection_area.get_overlapping_bodies() @@ -139,11 +146,11 @@ func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int): Client.remove_tower(self) -func _on_range_body_entered(body: Node2D) -> void: - mobs_in_range.append(body) - -func _on_range_body_exited(body: Node2D) -> void: - mobs_in_range.erase(body) +#func _on_range_body_entered(body: Node2D) -> void: + #mobs_in_range.append(body) +# +#func _on_range_body_exited(body: Node2D) -> void: + #mobs_in_range.erase(body) func _on_selectable_area_hover_enter() -> void: @@ -168,49 +175,56 @@ func _on_selectable_area_select_primary(event: InputEvent) -> void: func add_component(component: TowerComponent): - components.append(component) + components[component.type] = component + component.on_add(self) + var sprite = component.sprite - $ComponentsAnchor.add_child(sprite) + %ComponentsAnchor.add_child(sprite) redraw_components() + components_changed.emit() -func remove_component(component_name: String): - for component in components: - if component.name == component_name: - components.erase(component) - $ComponentsAnchor.remove_child($ComponentsAnchor.get_node(NodePath(component.name))) - break +func remove_component(component: TowerComponent): + components.erase(component.type) + %ComponentsAnchor.remove_child(%ComponentsAnchor.get_node(NodePath(component.id))) + #for component in components: + #if component.name == component_name: + #components.erase(component) + #$ComponentsAnchor.remove_child($ComponentsAnchor.get_node(NodePath(component.name))) + #break redraw_components() components_changed.emit() func redraw_components(): - for idx in range(components.size()): - var component = components[idx] - var sprite = $ComponentsAnchor.get_node(NodePath(component.name)) + var keys = components.keys() + for idx in range(keys.size()): + var key = keys[idx] + var component: TowerComponent = components[key] + var sprite = %ComponentsAnchor.get_node(NodePath(component.id)) sprite.position.y = (idx + 1) * -16 -func is_melee_range(): - return attack_range <= (Client.current_stage.map.tile_set.tile_size.x * 2) - - -func shoot(): - var target = mobs_in_range[0] as Unit - - for component in components: - if component.has_method("on_shoot"): - component.on_shoot(target) - - shoot_fx.rpc() - - if is_melee_range(): - target.set_hp(target.hp - 1) - else: # TODO - target.set_hp(target.hp - 1) - -@rpc("authority", "call_local") -func shoot_fx(): - $SoundShoot.play() +#func is_melee_range(): + #return attack_range <= (Client.current_stage.map.tile_set.tile_size.x * 2) + + +#func shoot(): + #var target = mobs_in_range[0] as Unit + # + #for component in components: + #if component.has_method("on_shoot"): + #component.on_shoot(target) + # + #shoot_fx.rpc() + # + #if is_melee_range(): + #target.set_hp(target.hp - 1) + #else: # TODO + #target.set_hp(target.hp - 1) +# +#@rpc("authority", "call_local") +#func shoot_fx(): + #$SoundShoot.play() @@ -227,12 +241,12 @@ func get_region(): func get_group_id() -> String: var string := "" - string += str(attack_range) - string += str(attack_power) - string += str(attack_speed) + #string += str(attack_range) + #string += str(attack_power) + #string += str(attack_speed) - for component in components: - string += component.name + for component: TowerComponent in components.values(): + string += component.id string += str(component.level) return string.md5_text() @@ -263,15 +277,15 @@ func to_network_data() -> NetworkData: data.owner_id = owner_id data.position = global_position - data.attack_range = attack_range - data.attack_power = attack_power - data.attack_speed = attack_speed - data.components = components.map(func(item: TowerComponent): + #data.attack_range = attack_range + #data.attack_power = attack_power + #data.attack_speed = attack_speed + data.components = components.values().map(func(item: TowerComponent): return inst_to_dict(item.to_network_data()) ) data.sprite_modulate = $AnimatedSprite2D.modulate - data.components_anchor_modulate = $ComponentsAnchor.modulate + data.components_anchor_modulate = %ComponentsAnchor.modulate # IMPROVEMENT: check against last update and only set changed values @@ -279,7 +293,7 @@ func to_network_data() -> NetworkData: func update_with_network_data(data: NetworkData): for component in components.duplicate(): - remove_component(component.name) + remove_component(component.id) for component_data in data.components: var component_network_data: TowerComponent.NetworkData = dict_to_inst(component_data) @@ -294,11 +308,11 @@ static func from_network_data(data: NetworkData) -> Tower: tower.owner_id = data.owner_id tower.global_position = data.position - tower.attack_range = data.attack_range - tower.attack_power = data.attack_power - tower.attack_speed = data.attack_speed + #tower.attack_range = data.attack_range + #tower.attack_power = data.attack_power + #tower.attack_speed = data.attack_speed tower.get_node("AnimatedSprite2D").modulate = data.sprite_modulate - tower.get_node("ComponentsAnchor").modulate = data.components_anchor_modulate + tower.get_node("%ComponentsAnchor").modulate = data.components_anchor_modulate return tower diff --git a/Towers/Tower.tscn b/Towers/Tower.tscn index 25388f6..0bd4579 100644 --- a/Towers/Tower.tscn +++ b/Towers/Tower.tscn @@ -93,6 +93,7 @@ shape = SubResource("RectangleShape2D_atm5x") [node name="Range" type="Area2D" parent="."] unique_name_in_owner = true +visible = false collision_layer = 0 [node name="CollisionShape2D" type="CollisionShape2D" parent="Range"] @@ -109,13 +110,23 @@ one_shot = true position = Vector2(16, 16) shape = SubResource("RectangleShape2D_312i7") -[node name="ComponentsAnchor" type="Marker2D" parent="."] -position = Vector2(0, 32) +[node name="ComponentsAnchor" type="HFlowContainer" parent="."] +unique_name_in_owner = true +offset_right = 32.0 +offset_bottom = 32.0 +size_flags_vertical = 3 +theme_override_constants/h_separation = 0 +theme_override_constants/v_separation = 0 +reverse_fill = true [node name="GroundAnchor" type="Marker2D" parent="."] position = Vector2(16, 29) +[node name="CenterAnchor" type="Marker2D" parent="."] +position = Vector2(16, 16) + [node name="SoundShoot" type="AudioStreamPlayer2D" parent="."] +visible = false stream = ExtResource("5_i05ow") [connection signal="input_event" from="." to="." method="_on_input_event"] -- cgit v1.2.3