diff options
-rw-r--r-- | Game/States/Build/StateBuild.gd | 10 | ||||
-rw-r--r-- | Readme.md | 17 | ||||
-rw-r--r-- | Stages/Stage.gd | 2 | ||||
-rw-r--r-- | Stages/Wintermaul/HUD.gd | 13 | ||||
-rw-r--r-- | Stages/Wintermaul/hud_tower.gd | 380 | ||||
-rw-r--r-- | Stages/Wintermaul/hud_tower.tscn | 60 | ||||
-rw-r--r-- | Stages/Wintermaul/wintermaul.gd | 2 | ||||
-rw-r--r-- | Towers/Components/AttackComponent.gd | 77 | ||||
-rw-r--r-- | Towers/Components/BurnComponent.gd | 2 | ||||
-rw-r--r-- | Towers/Components/FrostComponent.gd | 2 | ||||
-rw-r--r-- | Towers/Components/RangeComponent.gd | 52 | ||||
-rw-r--r-- | Towers/Components/TowerComponent.gd | 48 | ||||
-rw-r--r-- | Towers/Tower.gd | 154 | ||||
-rw-r--r-- | Towers/Tower.tscn | 15 | ||||
-rw-r--r-- | UI/ContainContainer.tscn | 6 | ||||
-rw-r--r-- | UI/contain_container.gd | 10 | ||||
-rw-r--r-- | project.godot | 1 |
17 files changed, 622 insertions, 229 deletions
diff --git a/Game/States/Build/StateBuild.gd b/Game/States/Build/StateBuild.gd index e7f122d..ea06671 100644 --- a/Game/States/Build/StateBuild.gd +++ b/Game/States/Build/StateBuild.gd @@ -19,8 +19,9 @@ func _state_exit(): func _state_input(event: InputEvent): if event.is_action_pressed("builder_tower_select"): - if current_builder_element and current_builder_element.can_build(): + get_viewport().set_input_as_handled() + var placed_tower = current_builder_element.element.instantiate() as Tower placed_tower.global_position = current_builder_element.global_position @@ -41,9 +42,12 @@ func _state_input(event: InputEvent): set_state("StateDefault") if event.is_action_pressed("builder_cancel") and current_builder_element: + get_viewport().set_input_as_handled() + current_builder_element.queue_free() current_builder_element = null -func _state_unhandled_input(_event: InputEvent) -> void: - pass +func _state_unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + set_state("StateDefault") @@ -4,9 +4,24 @@ Give them abilities/skills Splash yes/no + splash range - could only be available at tower upgrade level 2+ exp system for towers?? - transfer xp to other selected tower that is over max_xp usable -Add Components to Towers Pre-Build Tower Configurations +Poison Effect: +Stackable. +Tick abhängig von unit current speed bei tick reset. +Damage at tick abhängig von current speed at tick. +Amplified speed reduction effects? wäre interessant, ja + +Burn und Frost = nicht stackable +dadurch hat Poison effect merit + +Range Component, Speed Component, Attack Component, +Burn Component, Frost Component, Poison Component, +Splash Damage Component = trifft mehrere auf einmal in der Trefferregion. + trifft damit nicht mehr das ziel direkt evtl sondern nur den boden wo das anvisierte ziel war/ist. + on impact area get\_overlapping und damage und effekte applyen + !abhängig von positionsdistanz zum impact! effekte normal applyen + Credits: diff --git a/Stages/Stage.gd b/Stages/Stage.gd index 7cdd2f8..9484305 100644 --- a/Stages/Stage.gd +++ b/Stages/Stage.gd @@ -42,7 +42,7 @@ func place_tower(_remote_data: Dictionary) -> void: func _place_tower(parent: Node2D, tower: Tower): var player: Player = Network.get_player(tower.owner_id) tower.get_node("AnimatedSprite2D").modulate = player.get_color() - tower.get_node("ComponentsAnchor").modulate = player.get_color() + tower.get_node("%ComponentsAnchor").modulate = player.get_color() fill_tower_region(tower, true) diff --git a/Stages/Wintermaul/HUD.gd b/Stages/Wintermaul/HUD.gd index 5567f77..be2dacb 100644 --- a/Stages/Wintermaul/HUD.gd +++ b/Stages/Wintermaul/HUD.gd @@ -180,12 +180,15 @@ func get_ordered_group_keys() -> Array: var group_b = selection_groups[b] var node_a = group_a[0] var node_b = group_b[0] + var level_a = 0 + var level_b = 0 - return ( - (node_a.attack_range / 8) + node_a.attack_power + node_a.attack_speed - > - (node_b.attack_range / 8) + node_b.attack_power + node_b.attack_speed - ) + for component in node_a.components.values(): + level_a += component.level + for component in node_b.components.values(): + level_b += component.level + + return level_a > level_b ) keys.sort_custom(func(a, b): var group_a = selection_groups[a] diff --git a/Stages/Wintermaul/hud_tower.gd b/Stages/Wintermaul/hud_tower.gd index ab23441..24a7a8d 100644 --- a/Stages/Wintermaul/hud_tower.gd +++ b/Stages/Wintermaul/hud_tower.gd @@ -17,9 +17,9 @@ func set_current_tower(tower: Tower): redraw_components() current_tower.components_changed.connect(redraw_components) - %Data.text = "Range: %s - Power: %s - Speed: %s" % [ - current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed - ] + #%Data.text = "Range: %s - Power: %s - Speed: %s" % [ + #current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed + #] func redraw_components(): @@ -27,10 +27,9 @@ func redraw_components(): node.modulate = Color(1.0, 1.0, 1.0, 0.5) node.get_node("Level").text = str(0) - for idx in range(current_tower.components.size()): - var component = current_tower.components[idx] - %Components.get_node(component.name).modulate = Color(1.0, 1.0, 1.0, 1.0) - %Components.get_node(component.name).get_node("Level").text = ": %s" % [component.level] + for component: TowerComponent in current_tower.components.values(): + %Components.get_node(component.id).modulate = Color(1.0, 1.0, 1.0, 1.0) + %Components.get_node(component.id).get_node("Level").text = ": %s" % [component.level] func get_selected_towers() -> Array: @@ -40,87 +39,137 @@ func get_selected_towers() -> Array: return hud.selection_groups[hud.selected_group].duplicate() -func get_component(tower: Tower, component_name: String): - var component: TowerComponent - for idx in range(tower.components.size()): - if tower.components[idx].name == component_name: - component = tower.components[idx] - break - - return component - - -func _on_range_plus_pressed() -> void: - var towers = get_selected_towers() - - var cost = Client.current_stage.price_map.tower.attack_range.call(current_tower.attack_range, towers.size()) - if Client.player.money < cost: - Client.current_stage.add_status_message("Not enough money to upgrade") - return - - for tower: Tower in towers: - tower.attack_range += 8 - tower.selection_group_id = tower.get_group_id() - Client.update_tower(tower.get_path(), tower.to_network_data()) - - Network.update_player.rpc(Client.player.id, { - "money": -cost, - }) - - %Data.text = "Range: %s - Power: %s - Speed: %s" % [ - current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed - ] - - -func _on_power_plus_pressed() -> void: - var towers = get_selected_towers() - - var cost = Client.current_stage.price_map.tower.attack_power.call(current_tower.attack_power, towers.size()) - if Client.player.money < cost: - Client.current_stage.add_status_message("Not enough money to upgrade") - return - - for tower: Tower in towers: - tower.attack_power += 1 - tower.selection_group_id = tower.get_group_id() - Client.update_tower(tower.get_path(), tower.to_network_data()) - - Network.update_player.rpc(Client.player.id, { - "money": -cost, - }) - - %Data.text = "Range: %s - Power: %s - Speed: %s" % [ - current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed - ] +#func get_component(tower: Tower, component_name: String): + #var component: TowerComponent + #for idx in range(tower.components.size()): + #if tower.components[idx].name == component_name: + #component = tower.components[idx] + #break + # + #return component + + +#func _on_range_plus_pressed() -> void: + #var towers = get_selected_towers() + # + #var cost = Client.current_stage.price_map.tower.attack_range.call(current_tower.attack_range, towers.size()) + #if Client.player.money < cost: + #Client.current_stage.add_status_message("Not enough money to upgrade") + #return + # + #for tower: Tower in towers: + #tower.attack_range += 8 + #tower.selection_group_id = tower.get_group_id() + #Client.update_tower(tower.get_path(), tower.to_network_data()) + # + #Network.update_player.rpc(Client.player.id, { + #"money": -cost, + #}) + # + ##%Data.text = "Range: %s - Power: %s - Speed: %s" % [ + ##current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed + ##] +# +# +#func _on_power_plus_pressed() -> void: + #var towers = get_selected_towers() + # + #var cost = Client.current_stage.price_map.tower.attack_power.call(current_tower.attack_power, towers.size()) + #if Client.player.money < cost: + #Client.current_stage.add_status_message("Not enough money to upgrade") + #return + # + #for tower: Tower in towers: + #tower.attack_power += 1 + #tower.selection_group_id = tower.get_group_id() + #Client.update_tower(tower.get_path(), tower.to_network_data()) + # + #Network.update_player.rpc(Client.player.id, { + #"money": -cost, + #}) + # + ##%Data.text = "Range: %s - Power: %s - Speed: %s" % [ + ##current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed + ##] +# +# +#func _on_speed_plus_pressed() -> void: + #var towers = get_selected_towers() + # + #var cost = Client.current_stage.price_map.tower.attack_speed.call(current_tower.attack_speed, towers.size()) + #if Client.player.money < cost: + #Client.current_stage.add_status_message("Not enough money to upgrade") + #return + # + #for tower: Tower in towers: + #tower.attack_speed += 1 + #tower.selection_group_id = tower.get_group_id() + #Client.update_tower(tower.get_path(), tower.to_network_data()) + # + #Network.update_player.rpc(Client.player.id, { + #"money": -cost, + #}) + # + ##%Data.text = "Range: %s - Power: %s - Speed: %s" % [ + ##current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed + ##] + + +func _on_range_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + get_viewport().set_input_as_handled() + + if current_tower.components.has(TowerComponent.ComponentType.Range): + Client.current_stage.add_status_message("Component already added") + return + + var towers = get_selected_towers() + + var cost = 100 * towers.size() + if Client.player.money < cost: + Client.current_stage.add_status_message("Not enough money to add component") + return + + for tower: Tower in towers: + tower.add_component(preload("res://Towers/Components/RangeComponent.gd").new()) + + Client.update_tower(tower.get_path(), tower.to_network_data()) + + Network.update_player.rpc(Client.player.id, { + "money": -cost, + }) -func _on_speed_plus_pressed() -> void: - var towers = get_selected_towers() - - var cost = Client.current_stage.price_map.tower.attack_speed.call(current_tower.attack_speed, towers.size()) - if Client.player.money < cost: - Client.current_stage.add_status_message("Not enough money to upgrade") - return - - for tower: Tower in towers: - tower.attack_speed += 1 - tower.selection_group_id = tower.get_group_id() - Client.update_tower(tower.get_path(), tower.to_network_data()) - - Network.update_player.rpc(Client.player.id, { - "money": -cost, - }) - - %Data.text = "Range: %s - Power: %s - Speed: %s" % [ - current_tower.attack_range, current_tower.attack_power, current_tower.attack_speed - ] +func _on_attack_gui_input(event: InputEvent) -> void: + if event.is_action_pressed("select"): + get_viewport().set_input_as_handled() + + if current_tower.components.has(TowerComponent.ComponentType.Attack): + Client.current_stage.add_status_message("Component already added") + return + + var towers = get_selected_towers() + + var cost = 100 * towers.size() + if Client.player.money < cost: + Client.current_stage.add_status_message("Not enough money to add component") + return + + for tower: Tower in towers: + tower.add_component(preload("res://Towers/Components/AttackComponent.gd").new()) + + Client.update_tower(tower.get_path(), tower.to_network_data()) + + Network.update_player.rpc(Client.player.id, { + "money": -cost, + }) func _on_frost_gui_input(event: InputEvent) -> void: if event.is_action_pressed("select"): get_viewport().set_input_as_handled() - if get_component(current_tower, "Frost"): + if current_tower.components.has(TowerComponent.ComponentType.Frost): Client.current_stage.add_status_message("Component already added") return @@ -132,16 +181,7 @@ func _on_frost_gui_input(event: InputEvent) -> void: return for tower: Tower in towers: - var found = false - for idx in range(tower.components.size()): - var component = tower.components[idx] - if component.name == "Frost": - found = true - tower.remove_component(component.name) - break - - if not found: - tower.add_component(preload("res://Towers/Components/FrostComponent.gd").new()) + tower.add_component(preload("res://Towers/Components/FrostComponent.gd").new()) Client.update_tower(tower.get_path(), tower.to_network_data()) @@ -154,7 +194,7 @@ func _on_burn_gui_input(event: InputEvent) -> void: if event.is_action_pressed("select"): get_viewport().set_input_as_handled() - if get_component(current_tower, "Burn"): + if current_tower.components.has(TowerComponent.ComponentType.Burn): Client.current_stage.add_status_message("Component already added") return @@ -166,16 +206,7 @@ func _on_burn_gui_input(event: InputEvent) -> void: return for tower: Tower in towers: - var found = false - for idx in range(tower.components.size()): - var component = tower.components[idx] - if component.name == "Burn": - found = true - tower.remove_component(component.name) - break - - if not found: - tower.add_component(preload("res://Towers/Components/BurnComponent.gd").new()) + tower.add_component(preload("res://Towers/Components/BurnComponent.gd").new()) Client.update_tower(tower.get_path(), tower.to_network_data()) @@ -184,14 +215,62 @@ func _on_burn_gui_input(event: InputEvent) -> void: }) +func _on_range_level_up_pressed() -> void: + var towers = get_selected_towers() + + var component: TowerComponent = current_tower.components.get(TowerComponent.ComponentType.Range) + + if not component: + return + + var cost = 25 * component.level * towers.size() + if Client.player.money < cost: + Client.current_stage.add_status_message("Not enough money to level up") + return + + for tower: Tower in towers: + var tower_component: TowerComponent = tower.components.get(TowerComponent.ComponentType.Range) + + tower_component.level += 1 + tower.components_changed.emit() + + Client.update_tower(tower.get_path(), tower.to_network_data()) + + Network.update_player.rpc(Client.player.id, { + "money": -cost, + }) + + +func _on_attack_level_up_pressed() -> void: + var towers = get_selected_towers() + + var component: TowerComponent = current_tower.components.get(TowerComponent.ComponentType.Attack) + + if not component: + return + + var cost = 10 * component.level * towers.size() + if Client.player.money < cost: + Client.current_stage.add_status_message("Not enough money to level up") + return + + for tower: Tower in towers: + var tower_component: TowerComponent = tower.components.get(TowerComponent.ComponentType.Attack) + + tower_component.level += 1 + tower.components_changed.emit() + + Client.update_tower(tower.get_path(), tower.to_network_data()) + + Network.update_player.rpc(Client.player.id, { + "money": -cost, + }) + + func _on_frost_level_up_pressed() -> void: var towers = get_selected_towers() - var component: TowerComponent - for idx in range(current_tower.components.size()): - component = current_tower.components[idx] - if component.name == "Frost": - break + var component: TowerComponent = current_tower.components.get(TowerComponent.ComponentType.Frost) if not component: return @@ -202,14 +281,11 @@ func _on_frost_level_up_pressed() -> void: return for tower: Tower in towers: - var tower_component: TowerComponent - for idx in range(tower.components.size()): - tower_component = tower.components[idx] - if tower_component.name == "Frost": - break + var tower_component: TowerComponent = tower.components.get(TowerComponent.ComponentType.Frost) tower_component.level += 1 tower.components_changed.emit() + Client.update_tower(tower.get_path(), tower.to_network_data()) Network.update_player.rpc(Client.player.id, { @@ -220,12 +296,7 @@ func _on_frost_level_up_pressed() -> void: func _on_burn_level_up_pressed() -> void: var towers = get_selected_towers() - var component: TowerComponent - for idx in range(current_tower.components.size()): - component = current_tower.components[idx] - if component.name == "Burn": - break - + var component: TowerComponent = current_tower.components.get(TowerComponent.ComponentType.Burn) if not component: return @@ -235,14 +306,11 @@ func _on_burn_level_up_pressed() -> void: return for tower: Tower in towers: - var tower_component: TowerComponent - for idx in range(tower.components.size()): - tower_component = tower.components[idx] - if tower_component.name == "Burn": - break + var tower_component: TowerComponent = tower.components.get(TowerComponent.ComponentType.Burn) tower_component.level += 1 tower.components_changed.emit() + Client.update_tower(tower.get_path(), tower.to_network_data()) Network.update_player.rpc(Client.player.id, { @@ -250,28 +318,70 @@ func _on_burn_level_up_pressed() -> void: }) -func _on_range_plus_mouse_entered() -> void: +#func _on_range_plus_mouse_entered() -> void: + #var towers = get_selected_towers() + #var cost = Client.current_stage.price_map.tower.attack_range.call(current_tower.attack_range, towers.size()) + #%RangePlus.tooltip_text = "Cost: " + str(cost) +# +#func _on_power_plus_mouse_entered() -> void: + #var towers = get_selected_towers() + #var cost = Client.current_stage.price_map.tower.attack_power.call(current_tower.attack_power, towers.size()) + #%PowerPlus.tooltip_text = "Cost: " + str(cost) +# +#func _on_speed_plus_mouse_entered() -> void: + #var towers = get_selected_towers() + #var cost = Client.current_stage.price_map.tower.attack_speed.call(current_tower.attack_speed, towers.size()) + #%SpeedPlus.tooltip_text = "Cost: " + str(cost) + +func _on_range_mouse_entered() -> void: + if current_tower.components.has(TowerComponent.ComponentType.Range): + $Components/Range.tooltip_text = "" + return + var towers = get_selected_towers() - var cost = Client.current_stage.price_map.tower.attack_range.call(current_tower.attack_range, towers.size()) - %RangePlus.tooltip_text = "Cost: " + str(cost) + var cost = 100 * towers.size() + $Components/Range.tooltip_text = "Cost: " + str(cost) -func _on_power_plus_mouse_entered() -> void: +func _on_range_level_up_mouse_entered() -> void: + var component = current_tower.components.get(TowerComponent.ComponentType.Range) + if not component: + $Components/Range/RangeLevelUp.tooltip_text = "" + return + + var towers = get_selected_towers() + var cost = 25 * component.level * towers.size() + $Components/Range/RangeLevelUp.tooltip_text = "Cost: " + str(cost) + +func _on_attack_mouse_entered() -> void: + if current_tower.components.has(TowerComponent.ComponentType.Attack): + $Components/Range.tooltip_text = "" + return + var towers = get_selected_towers() - var cost = Client.current_stage.price_map.tower.attack_power.call(current_tower.attack_power, towers.size()) - %PowerPlus.tooltip_text = "Cost: " + str(cost) + var cost = 100 * towers.size() + $Components/Attack.tooltip_text = "Cost: " + str(cost) -func _on_speed_plus_mouse_entered() -> void: +func _on_attack_level_up_mouse_entered() -> void: + var component = current_tower.components.get(TowerComponent.ComponentType.Attack) + if not component: + $Components/Attack/AttackLevelUp.tooltip_text = "" + return + var towers = get_selected_towers() - var cost = Client.current_stage.price_map.tower.attack_speed.call(current_tower.attack_speed, towers.size()) - %SpeedPlus.tooltip_text = "Cost: " + str(cost) + var cost = 10 * component.level * towers.size() + $Components/Attack/AttackLevelUp.tooltip_text = "Cost: " + str(cost) func _on_frost_mouse_entered() -> void: + if current_tower.components.has(TowerComponent.ComponentType.Frost): + $Components/Range.tooltip_text = "" + return + var towers = get_selected_towers() var cost = 100 * towers.size() $Components/Frost.tooltip_text = "Cost: " + str(cost) func _on_frost_level_up_mouse_entered() -> void: - var component = get_component(current_tower, "Frost") + var component = current_tower.components.get(TowerComponent.ComponentType.Frost) if not component: $Components/Frost/FrostLevelUp.tooltip_text = "" return @@ -281,12 +391,16 @@ func _on_frost_level_up_mouse_entered() -> void: $Components/Frost/FrostLevelUp.tooltip_text = "Cost: " + str(cost) func _on_burn_mouse_entered() -> void: + if current_tower.components.has(TowerComponent.ComponentType.Burn): + $Components/Range.tooltip_text = "" + return + var towers = get_selected_towers() var cost = 100 * towers.size() $Components/Burn.tooltip_text = "Cost: " + str(cost) func _on_burn_level_up_mouse_entered() -> void: - var component = get_component(current_tower, "Burn") + var component = current_tower.components.get(TowerComponent.ComponentType.Burn) if not component: $Components/Burn/BurnLevelUp.tooltip_text = "" return diff --git a/Stages/Wintermaul/hud_tower.tscn b/Stages/Wintermaul/hud_tower.tscn index 533c6b9..4a23f10 100644 --- a/Stages/Wintermaul/hud_tower.tscn +++ b/Stages/Wintermaul/hud_tower.tscn @@ -1,8 +1,10 @@ -[gd_scene load_steps=4 format=3 uid="uid://ei0ai7om3mkw"] +[gd_scene load_steps=6 format=3 uid="uid://ei0ai7om3mkw"] [ext_resource type="Texture2D" uid="uid://ba3dmlce1wv2p" path="res://Towers/Components/Assets/frost-component.png" id="1_1rlop"] [ext_resource type="Script" path="res://Stages/Wintermaul/hud_tower.gd" id="1_aog4m"] +[ext_resource type="Texture2D" uid="uid://dx07y4scyi5a1" path="res://Towers/Components/Assets/range-component.png" id="2_epopi"] [ext_resource type="Texture2D" uid="uid://2djpswd6sgng" path="res://Towers/Components/Assets/burn-component.png" id="2_gcd2r"] +[ext_resource type="Texture2D" uid="uid://gbknvb38euuq" path="res://Towers/Components/Assets/attack-component.png" id="3_xygpi"] [node name="HUDTower" type="VBoxContainer"] offset_left = 64.0 @@ -12,10 +14,12 @@ script = ExtResource("1_aog4m") [node name="Data" type="Label" parent="."] unique_name_in_owner = true +visible = false layout_mode = 2 text = "Data" [node name="HBoxContainer" type="HBoxContainer" parent="."] +visible = false layout_mode = 2 [node name="RangePlus" type="Button" parent="HBoxContainer"] @@ -40,6 +44,52 @@ text = "+Speed" unique_name_in_owner = true layout_mode = 2 +[node name="Range" type="HBoxContainer" parent="Components"] +layout_mode = 2 +mouse_default_cursor_shape = 2 + +[node name="TextureRect" type="TextureRect" parent="Components/Range"] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +texture = ExtResource("2_epopi") +stretch_mode = 5 + +[node name="Label" type="Label" parent="Components/Range"] +layout_mode = 2 +text = "Range" + +[node name="Level" type="Label" parent="Components/Range"] +layout_mode = 2 +text = ": 1" + +[node name="RangeLevelUp" type="Button" parent="Components/Range"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "+Level" + +[node name="Attack" type="HBoxContainer" parent="Components"] +layout_mode = 2 +mouse_default_cursor_shape = 2 + +[node name="TextureRect" type="TextureRect" parent="Components/Attack"] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +texture = ExtResource("3_xygpi") +stretch_mode = 5 + +[node name="Label" type="Label" parent="Components/Attack"] +layout_mode = 2 +text = "Attack" + +[node name="Level" type="Label" parent="Components/Attack"] +layout_mode = 2 +text = ": 1" + +[node name="AttackLevelUp" type="Button" parent="Components/Attack"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "+Level" + [node name="Frost" type="HBoxContainer" parent="Components"] layout_mode = 2 mouse_default_cursor_shape = 2 @@ -92,6 +142,14 @@ text = "+Level" [connection signal="pressed" from="HBoxContainer/PowerPlus" to="." method="_on_power_plus_pressed"] [connection signal="mouse_entered" from="HBoxContainer/SpeedPlus" to="." method="_on_speed_plus_mouse_entered"] [connection signal="pressed" from="HBoxContainer/SpeedPlus" to="." method="_on_speed_plus_pressed"] +[connection signal="gui_input" from="Components/Range" to="." method="_on_range_gui_input"] +[connection signal="mouse_entered" from="Components/Range" to="." method="_on_range_mouse_entered"] +[connection signal="mouse_entered" from="Components/Range/RangeLevelUp" to="." method="_on_range_level_up_mouse_entered"] +[connection signal="pressed" from="Components/Range/RangeLevelUp" to="." method="_on_range_level_up_pressed"] +[connection signal="gui_input" from="Components/Attack" to="." method="_on_attack_gui_input"] +[connection signal="mouse_entered" from="Components/Attack" to="." method="_on_attack_mouse_entered"] +[connection signal="mouse_entered" from="Components/Attack/AttackLevelUp" to="." method="_on_attack_level_up_mouse_entered"] +[connection signal="pressed" from="Components/Attack/AttackLevelUp" to="." method="_on_attack_level_up_pressed"] [connection signal="gui_input" from="Components/Frost" to="." method="_on_frost_gui_input"] [connection signal="mouse_entered" from="Components/Frost" to="." method="_on_frost_mouse_entered"] [connection signal="mouse_entered" from="Components/Frost/FrostLevelUp" to="." method="_on_frost_level_up_mouse_entered"] diff --git a/Stages/Wintermaul/wintermaul.gd b/Stages/Wintermaul/wintermaul.gd index 95d914a..90abf6b 100644 --- a/Stages/Wintermaul/wintermaul.gd +++ b/Stages/Wintermaul/wintermaul.gd @@ -59,7 +59,7 @@ func _ready(): if multiplayer.is_server(): for player in Network.get_players(): Network.update_player.rpc(player.id, { - "money": 50, + "money": 50 * 10000, "income": 5, }) 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"] diff --git a/UI/ContainContainer.tscn b/UI/ContainContainer.tscn new file mode 100644 index 0000000..d9120ed --- /dev/null +++ b/UI/ContainContainer.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://pchnqnutfth7"] + +[ext_resource type="Script" path="res://UI/contain_container.gd" id="1_oswre"] + +[node name="ContainContainer" type="Container"] +script = ExtResource("1_oswre") diff --git a/UI/contain_container.gd b/UI/contain_container.gd new file mode 100644 index 0000000..df6e3a8 --- /dev/null +++ b/UI/contain_container.gd @@ -0,0 +1,10 @@ +@tool +class_name ContainContainer +extends Container + + +func _notification(what: int) -> void: + if what == NOTIFICATION_SORT_CHILDREN: + for child in get_children(): + if child is Control: + fit_child_in_rect(child, Rect2(Vector2.ZERO, size)) diff --git a/project.godot b/project.godot index 785dd86..193ce14 100644 --- a/project.godot +++ b/project.godot @@ -63,6 +63,7 @@ builder_tower_select={ builder_cancel={ "deadzone": 0.5, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(152, 20),"global_position":Vector2(156, 61),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } builder_tower_place_keep={ |