class_name Tower extends StaticBody2D signal selected signal deselected signal selected_secondary var is_selected := false: set(value): if value: selected.emit() else: deselected.emit() is_highlighted = false is_selected = value queue_redraw() var is_hovered := false: set(value): if value: Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) else: Input.set_default_cursor_shape(Input.CURSOR_ARROW) is_hovered = value queue_redraw() var is_highlighted := false: set(value): is_highlighted = value queue_redraw() signal selection_group_id_changed(previous_id: String) @onready var selection_group_id := get_group_id(): set(value): var previous_id = selection_group_id selection_group_id = value selection_group_id_changed.emit(previous_id) # rpc owner id @export var owner_id = 1 signal components_changed @export var components: Dictionary func _init(): scale = Vector2(Client.current_stage.map.tile_set.tile_size) / Vector2(16, 16) func _ready(): $AnimatedSprite2D.play() for component in [ preload("res://Towers/Components/RangeComponent.tscn").instantiate(), preload("res://Towers/Components/AttackComponent.tscn").instantiate() ]: add_component(component) redraw_components() components_changed.connect(func(): selection_group_id = get_group_id() ) func _draw(): modulate = Color(1, 1, 1) if is_hovered: if Client.state is StateDefault: draw_circle( Vector2(Client.current_stage.map.tile_set.tile_size) / scale, 8 + components.get(TowerComponent.ComponentType.Range).range, Color(1, 1, 1, 0.5), false, 1.0 ) modulate = Color(1.25, 1.25, 1.25) if is_selected: modulate = Color(1.5, 1.5, 1.5) if is_highlighted: var sprite_size = $AnimatedSprite2D.sprite_frames.get_frame_texture("default", 0).get_size() * $AnimatedSprite2D.scale draw_rect( Rect2($AnimatedSprite2D.position, sprite_size), Color(1, 1, 1, 1.0), false, 2.0 / get_viewport().get_camera_2d().zoom.x ) func _process(delta: float) -> void: if multiplayer.is_server(): for component in components.values(): if component.has_method("process"): component.process(delta) func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int): # disable remote select for now if owner_id != multiplayer.get_unique_id(): return if event.is_action_pressed("select_secondary"): selected_secondary.emit() if Client.state is StateBuild: if event.is_action_pressed("builder_cancel"): Client.remove_tower(self) func _on_selectable_area_hover_enter() -> void: is_hovered = true func _on_selectable_area_hover_exit() -> void: is_hovered = false func _on_selectable_area_select_primary(event: InputEvent) -> void: # disable remote select for now if owner_id != multiplayer.get_unique_id(): return if Client.state is StateDefault: if Input.is_action_pressed("select_multiple") and is_selected: is_selected = false else: Client.select_tower(self) if event.is_double_click(): Client.multi_select(3) func add_component(component: TowerComponent): components[component.type] = component add_child(component) component.on_add(self) var sprite = component.sprite.duplicate() %ComponentsAnchor.add_child(sprite) redraw_components() components_changed.emit() func redraw_components(): pass #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 get_region(): var collision_shape := $CollisionShape2D var shape = $CollisionShape2D.shape as RectangleShape2D return Rect2( collision_shape.position, shape.size * scale ) func get_group_id() -> String: var string := "" for component: TowerComponent in components.values(): string += component.id string += str(component.level) return string.md5_text() func _on_tree_exiting() -> void: is_selected = false class NetworkData extends Resource: var name: String var owner_id: int var position: Vector2 var attack_range: int var attack_power: int var attack_speed: int var components: Array var sprite_modulate: Color var components_anchor_modulate: Color func to_network_data() -> NetworkData: var data = NetworkData.new() data.name = name data.owner_id = owner_id data.position = global_position data.components = components.values().map(func(item: TowerComponent): var component_data = inst_to_dict(item.to_network_data()) component_data["path"] = item.get_path() return component_data ) data.sprite_modulate = $AnimatedSprite2D.modulate data.components_anchor_modulate = %ComponentsAnchor.modulate # IMPROVEMENT: check against last update and only set changed values return data func update_with_network_data(data: NetworkData): for component_data in data.components: var component = get_node_or_null(component_data.path) as TowerComponent if not component: add_component(TowerComponent.from_network_data(dict_to_inst(component_data))) else: component.update_with_network_data(dict_to_inst(component_data)) static func from_network_data(data: NetworkData) -> Tower: var tower: Tower = preload("res://Towers/Tower.tscn").instantiate() tower.name = data.name tower.owner_id = data.owner_id tower.global_position = data.position tower.get_node("AnimatedSprite2D").modulate = data.sprite_modulate tower.get_node("%ComponentsAnchor").modulate = data.components_anchor_modulate return tower