summaryrefslogtreecommitdiff
path: root/Towers
diff options
context:
space:
mode:
Diffstat (limited to 'Towers')
-rw-r--r--Towers/Components/AttackComponent.gd77
-rw-r--r--Towers/Components/BurnComponent.gd2
-rw-r--r--Towers/Components/FrostComponent.gd2
-rw-r--r--Towers/Components/RangeComponent.gd52
-rw-r--r--Towers/Components/TowerComponent.gd48
-rw-r--r--Towers/Tower.gd154
-rw-r--r--Towers/Tower.tscn15
7 files changed, 266 insertions, 84 deletions
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"]