diff --git a/asteroid.gd b/asteroid.gd index 3d373af..d4accb5 100644 --- a/asteroid.gd +++ b/asteroid.gd @@ -1,43 +1,41 @@ extends StaticBody2D -@export var max_health: float = 100.0 # Maximum health for the asteroid +@export var max_health: float = 100.0 var current_health: float -var is_destroyed: bool = false # Track if the asteroid has already been destroyed +var is_destroyed: bool = false +var event_bus: EventBus func _ready(): current_health = max_health - update_health_label() # Update the health display when the game starts + event_bus = get_node("/root/Main/MainEventBus") + event_bus.subscribe_to(get_node("/root/Main/DamageEventScope"), "DamageEvent", Callable(self, "on_damage_event")) + add_to_group("asteroids") + update_health_label() -# Function to handle taking damage -func take_damage(damage: float): - if is_destroyed: # Prevent taking damage if already destroyed - return +func on_damage_event(event: DamageEvent) -> EventResult.ResultType: + if event.target != self or is_destroyed: + return EventResult.ResultType.KEEP_SUBSCRIPTION + + apply_damage(event.damage_value) - current_health -= damage # Subtract the damage from current health - current_health = max(current_health, 0) # Ensure health doesn't drop below 0 - update_health_label() # Update the label to reflect new health value - if current_health <= 0.0: - break_apart() - -# Function to handle when the asteroid is destroyed -func break_apart(): - is_destroyed = true # Mark the asteroid as destroyed - queue_free() # Remove the asteroid from the scene - print("Asteroid mined!") + emit_asteroid_destroyed() + return EventResult.ResultType.DISPOSE_SUBSCRIPTION - # Load the Cheese scene - var cheese_scene = load("res://Cheese.tscn") - - # Instance the cheese - var cheese_instance = cheese_scene.instantiate() - - # Set the position where the cheese should spawn (at the asteroid's position) - cheese_instance.global_position = global_position + Vector2(randf() * 50 - 25, randf() * 50 - 25) + return EventResult.ResultType.KEEP_SUBSCRIPTION - # Add the cheese instance to the scene - get_parent().add_child(cheese_instance) +func apply_damage(damage: float) -> void: + current_health = max(current_health - damage, 0) + update_health_label() -# Function to update the health label display -func update_health_label(): +func emit_asteroid_destroyed() -> void: + is_destroyed = true + queue_free() + publish_destroyed_event() + +func publish_destroyed_event() -> void: + var destroyed_event = DestroyedEvent.new(self, "asteroid", global_position, "laser") + event_bus.publish(get_node("/root/Main/DestroyedEventScope"), destroyed_event) + +func update_health_label() -> void: $AsteroidHealthLabel.text = str(round(current_health)) + " / " + str(max_health) diff --git a/asteroid.tscn b/asteroid.tscn new file mode 100644 index 0000000..21b0f73 --- /dev/null +++ b/asteroid.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=4 format=3 uid="uid://dcq5a8tewppk1"] + +[ext_resource type="Script" path="res://asteroid.gd" id="1_spmxj"] +[ext_resource type="Texture2D" uid="uid://buirp1h6onqai" path="res://images/AsteroidBrown.png" id="2_hb7w5"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_w58nc"] +radius = 86.093 + +[node name="asteroid" type="StaticBody2D"] +position = Vector2(564, 144) +collision_mask = 2 +script = ExtResource("1_spmxj") + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(1.00006, -3.99998) +scale = Vector2(1.2, 1.181) +texture = ExtResource("2_hb7w5") + +[node name="AsteroidCollisionPolygon2D" type="CollisionPolygon2D" parent="."] +position = Vector2(-519, -164) +polygon = PackedVector2Array(548, 101, 613, 169, 597, 210, 548, 240, 462, 221, 431, 184, 434, 133, 497, 90) + +[node name="AsteroidHealthLabel" type="Label" parent="."] +offset_left = 105.0 +offset_top = 38.0 +offset_right = 181.0 +offset_bottom = 80.0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 2) +shape = SubResource("CircleShape2D_w58nc") diff --git a/cheeseCollision.gd b/cheeseCollision.gd index ebaadf4..8ad7ea2 100644 --- a/cheeseCollision.gd +++ b/cheeseCollision.gd @@ -1,18 +1,12 @@ extends Area2D - - func _ready(): - pass # Replace with function body. + pass - -# Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): pass - func _on_body_entered(body): if body.name == "spaceship": - queue_free() # This removes the cheese from the scene - print("Cheese collected!") # Replace this with actual game logic - + queue_free() + print("Cheese collected!") diff --git a/damage_event.gd b/damage_event.gd new file mode 100644 index 0000000..569cab2 --- /dev/null +++ b/damage_event.gd @@ -0,0 +1,10 @@ +class_name DamageEvent +extends Event + +var damage_value: float +var target: Node + +func _init(_damage_value: float, _target: Node): + damage_value = _damage_value + target = _target + type = "DamageEvent" diff --git a/destroyed_event.gd b/destroyed_event.gd new file mode 100644 index 0000000..b163a89 --- /dev/null +++ b/destroyed_event.gd @@ -0,0 +1,22 @@ +class_name DestroyedEvent +extends Event + +var destroyed_object: Node +var object_type: String +var position: Vector2 +var destruction_cause: String + +func _init(_destroyed_object: Node, _object_type: String, _position: Vector2, _destruction_cause: String = ""): + destroyed_object = _destroyed_object + object_type = _object_type + position = _position + destruction_cause = _destruction_cause + type = "DestroyedEvent" + +func get_event_details() -> Dictionary: + return { + "object": destroyed_object, + "type": object_type, + "position": position, + "cause": destruction_cause + } diff --git a/eventbus/defaulteventbus.gd b/eventbus/defaulteventbus.gd new file mode 100644 index 0000000..436e8fc --- /dev/null +++ b/eventbus/defaulteventbus.gd @@ -0,0 +1,43 @@ +class_name DefaultEventBus +extends EventBus + +var subscribers: Dictionary = {} + +func _init(): + subscribers = {} + +func subscribers_of(event_scope: EventScope, event_type: String) -> Array[Subscription]: + var key = [event_scope, event_type] + return subscribers.get(key, []) + +func subscribe_to(event_scope: EventScope, event_type: String, fn: Callable) -> Subscription: + var key = [event_scope, event_type] + var subscriber_set: Array[EventBusSubscription] = subscribers.get(key, [] as Array[EventBusSubscription]) + var subscription = EventBusSubscription.new(key, fn, self) + subscriber_set.append(subscription) + subscribers[key] = subscriber_set + return subscription + +func publish(event_scope: EventScope, event: Event) -> void: + var key = [event_scope, event.get_type()] + var subscriber_set: Array[EventBusSubscription] = subscribers.get(key, [] as Array[EventBusSubscription]).duplicate(true) + for subscriber in subscriber_set: + var result = subscriber.callback.call(event) + if result == EventResult.ResultType.DISPOSE_SUBSCRIPTION: + subscriber.dispose() + +func cancel_scope(event_scope: EventScope) -> void: + var keys_to_cancel: Array = [] + for key in subscribers.keys(): + if key[0] == event_scope: + keys_to_cancel.append(key) + for key in keys_to_cancel: + var subscriber_set: Array[EventBusSubscription] = subscribers[key] + for subscriber in subscriber_set: + subscriber.dispose() + +func dispose() -> void: + for subscriber_set in subscribers.values(): + for subscriber in subscriber_set: + subscriber.dispose() + subscribers.clear() diff --git a/eventbus/defaultscope.gd b/eventbus/defaultscope.gd new file mode 100644 index 0000000..dc0820d --- /dev/null +++ b/eventbus/defaultscope.gd @@ -0,0 +1,2 @@ +class_name DefaultScope +extends EventScope diff --git a/eventbus/event.gd b/eventbus/event.gd new file mode 100644 index 0000000..a96f561 --- /dev/null +++ b/eventbus/event.gd @@ -0,0 +1,12 @@ +class_name Event +extends RefCounted + +var type: String +var source: Variant + +func _init(_source: Variant = null): + source = _source + type = self.get_class() + +func get_type() -> String: + return type diff --git a/eventbus/eventbus.gd b/eventbus/eventbus.gd new file mode 100644 index 0000000..442dd52 --- /dev/null +++ b/eventbus/eventbus.gd @@ -0,0 +1,17 @@ +class_name EventBus +extends Node + +func subscribers_of(_event_scope: EventScope, _event_type: String) -> Array[Subscription]: + return Array() + +func subscribe_to(_event_scope: EventScope, _event_type: String, _fn: Callable) -> Subscription: + return Subscription.new() + +func publish(_event_scope: EventScope, _event: Event) -> void: + pass + +func cancel_scope(_event_scope: EventScope) -> void: + pass + +func dispose() -> void: + pass diff --git a/eventbus/eventbussubscription.gd b/eventbus/eventbussubscription.gd new file mode 100644 index 0000000..28f9068 --- /dev/null +++ b/eventbus/eventbussubscription.gd @@ -0,0 +1,19 @@ +class_name EventBusSubscription +extends Subscription + +var key: Array +var callback: Callable +var bus: DefaultEventBus + +func _init(_key: Array, _callback: Callable, _bus: DefaultEventBus): + key = _key + callback = _callback + bus = _bus + +func dispose() -> void: + var subscriber_set: Array[EventBusSubscription] = bus.subscribers.get(key, []) + subscriber_set.erase(self) + if subscriber_set.is_empty(): + bus.subscribers.erase(key) + else: + bus.subscribers[key] = subscriber_set diff --git a/eventbus/eventresult.gd b/eventbus/eventresult.gd new file mode 100644 index 0000000..5087cfc --- /dev/null +++ b/eventbus/eventresult.gd @@ -0,0 +1,7 @@ +class_name EventResult +extends RefCounted + +enum ResultType { + KEEP_SUBSCRIPTION, + DISPOSE_SUBSCRIPTION +} diff --git a/eventbus/eventscope.gd b/eventbus/eventscope.gd new file mode 100644 index 0000000..395b780 --- /dev/null +++ b/eventbus/eventscope.gd @@ -0,0 +1,2 @@ +class_name EventScope +extends Node diff --git a/eventbus/subscription.gd b/eventbus/subscription.gd new file mode 100644 index 0000000..3472e7f --- /dev/null +++ b/eventbus/subscription.gd @@ -0,0 +1,5 @@ +class_name Subscription +extends RefCounted + +func dispose() -> void: + pass diff --git a/input_event.gd b/input_event.gd new file mode 100644 index 0000000..a5baedf --- /dev/null +++ b/input_event.gd @@ -0,0 +1,10 @@ +class_name KeyboardInputEvent +extends Event + +var action: String +var pressed: bool + +func _init(_action: String, _pressed: bool): + action = _action + pressed = _pressed + type = "KeyboardInputEvent" diff --git a/laser.gd b/laser.gd index 27068be..4b716b4 100644 --- a/laser.gd +++ b/laser.gd @@ -7,12 +7,14 @@ extends Node2D var hardpoint = null # Reference to the attached hardpoint var is_firing = false # Track the laser's firing state var current_damage_output: float = 0.0 # Track the current damage output +var event_bus: EventBus func _ready(): # Register the laser to the "weapons" group add_to_group("weapons") # Connect to the correct hardpoint when ready call_deferred("_connect_to_hardpoint") + event_bus = get_node("/root/Main/MainEventBus") # Initialize LaserLine2D with two points (origin and target) $LaserLine2D.clear_points() @@ -32,16 +34,16 @@ func _connect_to_hardpoint(): func fire(): is_firing = true + current_damage_output = laser_damage + $DamageOutputLabel.text = str(current_damage_output) _process_laser(true) - current_damage_output = laser_damage - $DamageOutputLabel.text = str(current_damage_output) func cease_fire(): is_firing = false + current_damage_output = 0.0 + $DamageOutputLabel.text = str(current_damage_output) _process_laser(false) - current_damage_output = 0.0 - $DamageOutputLabel.text = str(current_damage_output) func _process(delta: float) -> void: if is_firing: @@ -52,27 +54,21 @@ func _process_laser(active: bool): var raycast = $LaserBeam2D raycast.enabled = active - if active: - raycast.force_raycast_update() if raycast.is_colliding(): - var collision_point = raycast.get_collision_point() - $LaserLine2D.set_point_position(0, Vector2.ZERO) - $LaserLine2D.set_point_position(1, to_local(collision_point)) - + $LaserLine2D.set_point_position(0, Vector2.ZERO) + $LaserLine2D.set_point_position(1, to_local(collision_point)) var collider = raycast.get_collider() - if collider.has_method("take_damage"): - collider.take_damage(laser_damage) + if collider: + var damage_event = DamageEvent.new(laser_damage, collider) + event_bus.publish(get_node("/root/Main/DamageEventScope"), damage_event) else: - - $LaserLine2D.set_point_position(0, Vector2.ZERO) - $LaserLine2D.set_point_position(1, Vector2(0, -laser_range)) + $LaserLine2D.set_point_position(1, Vector2(0, -laser_range)) else: - $LaserLine2D.clear_points() - $LaserLine2D.add_point(Vector2.ZERO) # Re-add the origin point - $LaserLine2D.add_point(Vector2.ZERO) # Reset target point to origin + $LaserLine2D.add_point(Vector2.ZERO) + $LaserLine2D.add_point(Vector2.ZERO) diff --git a/main.gd b/main.gd new file mode 100644 index 0000000..ebb22cf --- /dev/null +++ b/main.gd @@ -0,0 +1,80 @@ +extends Node + +func _init() -> void: + var node = WeaponsSystem.new() + node.set_name("WeaponsSystem") + add_child(node) + + node = DefaultEventBus.new() + node.set_name("MainEventBus") + add_child(node) + + node = EventScope.new() + node.set_name("InputEventScope") + add_child(node) + + node = EventScope.new() + node.set_name("DamageEventScope") + add_child(node) + + node = EventScope.new() + node.set_name("DestroyedEventScope") + add_child(node) + + # Instantiate and add the RewardSpawner + node = RewardSpawner.new() + node.set_name("RewardSpawner") + add_child(node) + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + var actions = [ + "ui_fire", + "ui_up", + "ui_down", + "ui_left", + "ui_right", + ] + for action in actions: + if Input.is_action_pressed(action): + get_node("/root/Main/MainEventBus").publish(get_node("/root/Main/InputEventScope"), KeyboardInputEvent.new(action, true)) + elif Input.is_action_just_released(action): + get_node("/root/Main/MainEventBus").publish(get_node("/root/Main/InputEventScope"), KeyboardInputEvent.new(action, false)) + pass + +# Spawn function and random position logic are placed here +func spawn_asteroid(): + var asteroid_scene = preload("res://asteroid.tscn") + var asteroid_instance = asteroid_scene.instantiate() + + # Generate random positions within a defined area + var spawn_position = get_random_position() + + # Check that it's not too close to the player or other asteroids + while !is_valid_position(spawn_position): + spawn_position = get_random_position() + + asteroid_instance.global_position = spawn_position + add_child(asteroid_instance) + +func get_random_position() -> Vector2: + var screen_size = get_viewport().size + return Vector2(randf_range(0, screen_size.x), randf_range(0, screen_size.y)) + +func is_valid_position(spawn_position: Vector2) -> bool: + var player = get_node("spaceship") # Adjust the path to your player node + var player_distance = player.global_position.distance_to(spawn_position) + + if player_distance < 200: + return false + + var asteroids = get_tree().get_nodes_in_group("asteroids") + for asteroid in asteroids: + if asteroid.global_position.distance_to(spawn_position) < 150: + return false + + return true diff --git a/main.tscn b/main.tscn index e2b84ac..122934a 100644 --- a/main.tscn +++ b/main.tscn @@ -1,14 +1,13 @@ -[gd_scene load_steps=13 format=3 uid="uid://8ocp10j32f62"] +[gd_scene load_steps=11 format=3 uid="uid://8ocp10j32f62"] +[ext_resource type="Script" path="res://main.gd" id="1_eedai"] [ext_resource type="Texture2D" uid="uid://y6phkg4twpdm" path="res://images/bg_space_seamless.png" id="1_rpyi5"] [ext_resource type="Texture2D" uid="uid://cran7fr1i2qou" path="res://images/spaceship-placeholder.png" id="2_f2x66"] [ext_resource type="Script" path="res://spaceship.gd" id="3_ttkgl"] [ext_resource type="Script" path="res://laser.gd" id="4_uhf7q"] -[ext_resource type="Script" path="res://weapons_system.gd" id="5_gf6oh"] -[ext_resource type="Script" path="res://asteroid.gd" id="6_n4dsl"] [ext_resource type="Texture2D" uid="uid://bxgw2u7j4b634" path="res://images/laser_turret.png" id="6_qxhyw"] -[ext_resource type="Texture2D" uid="uid://buirp1h6onqai" path="res://images/AsteroidBrown.png" id="7_0tjls"] [ext_resource type="Script" path="res://hardpoint.gd" id="7_6cr6a"] +[ext_resource type="Script" path="res://spawn_asteroid_button.gd" id="9_21dg0"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_3vtwu"] radius = 48.0 @@ -18,10 +17,8 @@ height = 102.0 offsets = PackedFloat32Array(0.961735, 1) colors = PackedColorArray(1, 1, 0, 1, 1, 1, 1, 0.137255) -[sub_resource type="CircleShape2D" id="CircleShape2D_w58nc"] -radius = 88.0057 - [node name="Main" type="Node2D"] +script = ExtResource("1_eedai") [node name="background" type="Sprite2D" parent="."] position = Vector2(955, 537) @@ -29,7 +26,7 @@ scale = Vector2(2, 2) texture = ExtResource("1_rpyi5") [node name="spaceship" type="CharacterBody2D" parent="."] -position = Vector2(908, 835) +position = Vector2(956, 623) collision_layer = 2 motion_mode = 1 script = ExtResource("3_ttkgl") @@ -70,52 +67,14 @@ offset_top = -71.0 offset_right = 104.0 offset_bottom = -20.0 -[node name="WeaponsSystem" type="Node" parent="spaceship"] -script = ExtResource("5_gf6oh") - [node name="HardPoint1" type="Node2D" parent="spaceship"] position = Vector2(0, -46) script = ExtResource("7_6cr6a") -[node name="asteroid" type="StaticBody2D" parent="."] -position = Vector2(564, 144) -collision_mask = 2 -script = ExtResource("6_n4dsl") - -[node name="Sprite2D" type="Sprite2D" parent="asteroid"] -position = Vector2(1.00006, -3.99998) -scale = Vector2(1.2, 1.181) -texture = ExtResource("7_0tjls") - -[node name="AsteroidCollisionPolygon2D" type="CollisionPolygon2D" parent="asteroid"] -position = Vector2(-519, -164) -polygon = PackedVector2Array(548, 101, 613, 169, 597, 210, 548, 240, 462, 221, 431, 184, 434, 133, 497, 90) - -[node name="AsteroidHealthLabel" type="Label" parent="asteroid"] -offset_left = 105.0 -offset_top = 38.0 -offset_right = 181.0 -offset_bottom = 80.0 - -[node name="CollisionShape2D" type="CollisionShape2D" parent="asteroid"] -position = Vector2(-2, 1) -shape = SubResource("CircleShape2D_w58nc") - -[node name="asteroid2" type="StaticBody2D" parent="."] -position = Vector2(1405, 413) -script = ExtResource("6_n4dsl") - -[node name="Sprite2D" type="Sprite2D" parent="asteroid2"] -position = Vector2(1.00006, -3.99998) -scale = Vector2(1.2, 1.181) -texture = ExtResource("7_0tjls") - -[node name="AsteroidCollisionPolygon2D" type="CollisionPolygon2D" parent="asteroid2"] -position = Vector2(-519, -164) -polygon = PackedVector2Array(548, 101, 613, 169, 597, 210, 548, 240, 462, 221, 431, 184, 434, 133, 497, 90) - -[node name="AsteroidHealthLabel" type="Label" parent="asteroid2"] -offset_left = 105.0 -offset_top = 38.0 -offset_right = 181.0 -offset_bottom = 80.0 +[node name="SpawnAsteroidButton" type="Button" parent="."] +offset_left = 72.0 +offset_top = 993.0 +offset_right = 249.0 +offset_bottom = 1049.0 +text = "SPAWN ASTEROID" +script = ExtResource("9_21dg0") diff --git a/project.godot b/project.godot index a8a4f4b..bbc0fb8 100644 --- a/project.godot +++ b/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="Star Cheese - The Final Fromage" run/main_scene="res://main.tscn" -config/features=PackedStringArray("4.2", "Forward Plus") +config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://icon.svg" [display] @@ -24,7 +24,7 @@ window/size/viewport_height=1080 ui_fire={ "deadzone": 0.5, -"events": [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":32,"key_label":0,"unicode":32,"echo":false,"script":null) +"events": [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":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } diff --git a/reward_spawner.gd b/reward_spawner.gd new file mode 100644 index 0000000..57c3e3c --- /dev/null +++ b/reward_spawner.gd @@ -0,0 +1,20 @@ +class_name RewardSpawner +extends Node + +var event_bus: EventBus + +func _ready(): + event_bus = get_node("/root/Main/MainEventBus") + event_bus.subscribe_to(get_node("/root/Main/DestroyedEventScope"), "DestroyedEvent", Callable(self, "on_destroyed")) + +func on_destroyed(event: DestroyedEvent): + if event.object_type == "asteroid": + spawn_cheese(event.position) + +func spawn_cheese(position: Vector2): + var cheese_scene = load("res://cheese.tscn") + var cheese_instance = cheese_scene.instantiate() + + cheese_instance.global_position = position + Vector2(randf() * 50 - 25, randf() * 50 - 25) + + get_parent().add_child(cheese_instance) diff --git a/spaceship.gd b/spaceship.gd index 0a67e16..c822f60 100644 --- a/spaceship.gd +++ b/spaceship.gd @@ -8,10 +8,12 @@ extends CharacterBody2D signal fire # Signal to start firing signal stop_fire # Signal to stop firing -var weapons_system - +var weapons_system: WeaponsSystem +var event_bus: EventBus func _ready(): - weapons_system = $WeaponsSystem + weapons_system = get_node("/root/Main/WeaponsSystem") + event_bus = get_node("/root/Main/MainEventBus") + event_bus.subscribe_to(get_node("/root/Main/InputEventScope"), "KeyboardInputEvent", Callable(self, "handle_keyboard_input")) func get_spaceship_orientation_vector() -> Vector2: return -global_transform.y @@ -45,13 +47,14 @@ func handle_movement(delta: float) -> void: var collision_normal = collision.get_normal() velocity = velocity.slide(collision_normal) -func handle_weapons_input() -> void: - if Input.is_action_pressed("ui_fire"): - weapons_system.fire_all() - - if Input.is_action_just_released("ui_fire"): - weapons_system.cease_fire_all() - +func handle_keyboard_input(event: KeyboardInputEvent): + if( event.action == "ui_fire" ): + if( event.pressed ): + weapons_system.fire_all() + else: + weapons_system.cease_fire_all() + + pass + func _process(delta: float) -> void: handle_movement(delta) - handle_weapons_input() diff --git a/spawn_asteroid_button.gd b/spawn_asteroid_button.gd new file mode 100644 index 0000000..5a489df --- /dev/null +++ b/spawn_asteroid_button.gd @@ -0,0 +1,29 @@ +extends Button + +@onready var main_script = get_node("/root/Main") + +func _ready(): + # Connect the "pressed" signal to a custom function in this script + self.pressed.connect(self.on_button_pressed) + +# Handle what happens when the button is pressed +func on_button_pressed(): + # Call the main script's spawn_asteroid function + main_script.spawn_asteroid() + + # Disable the button temporarily to prevent immediate re-trigger + self.disabled = true + + self.release_focus() + + # Start a timer to re-enable the button after 0.2 seconds + var timer = Timer.new() + timer.wait_time = 0.2 + timer.one_shot = true + timer.timeout.connect(self.on_timeout) + add_child(timer) + timer.start() + +# Re-enable the button when the timer times out +func on_timeout(): + self.disabled = false diff --git a/tests.tscn b/tests.tscn new file mode 100644 index 0000000..f073280 --- /dev/null +++ b/tests.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dxxijo7vef7or"] + +[ext_resource type="Script" path="res://tests/tests.gd" id="1_dngg8"] + +[node name="tests" type="Node"] +script = ExtResource("1_dngg8") diff --git a/tests/test_eventbus.gd b/tests/test_eventbus.gd new file mode 100644 index 0000000..9b173ac --- /dev/null +++ b/tests/test_eventbus.gd @@ -0,0 +1,105 @@ +# res://tests/test_eventbus.gd +class_name EventBusTests +extends Node + +func _ready(): + run_tests() + +func run_tests(): + test_subscribe_and_publish() + test_dispose_subscription() + test_cancel_scope() + test_multiple_subscribers() + + # Test function: Test subscribing and publishing events +func test_subscribe_and_publish() -> void: + var bus: DefaultEventBus = DefaultEventBus.new() + var scope: DefaultScope = DefaultScope.new() + var event_received: Flag = Flag.new() + + var event_handler = func(_event: Event) -> int: + event_received.value = true + return EventResult.ResultType.KEEP_SUBSCRIPTION + + bus.subscribe_to(scope, "TestEvent", event_handler) + var event: TestEvent = TestEvent.new(self) + bus.publish(scope, event) + + # Assert that the event was received + assert(event_received.value == true, "Event was not received by subscriber") + bus.dispose() + + # Test function: Test disposing of a subscription after handling an event +func test_dispose_subscription() -> void: + var bus: DefaultEventBus = DefaultEventBus.new() + var scope: DefaultScope = DefaultScope.new() + var event_received_times: Counter = Counter.new() + + var event_handler = func(_event: Event) -> int: + event_received_times.value += 1 + return EventResult.ResultType.DISPOSE_SUBSCRIPTION + + bus.subscribe_to(scope, "TestEvent", event_handler) + var event: TestEvent = TestEvent.new(self) + bus.publish(scope, event) + bus.publish(scope, event) # Publish a second time + + # Assert that the event handler was called only once + assert(event_received_times.value == 1, "Event handler should have been called only once due to disposal") + bus.dispose() + + # Test function: Test canceling an event scope +func test_cancel_scope() -> void: + var bus: DefaultEventBus = DefaultEventBus.new() + var scope: DefaultScope = DefaultScope.new() + var event_received: Flag = Flag.new() + + var event_handler = func(_event: Event) -> int: + event_received.value = true + return EventResult.ResultType.KEEP_SUBSCRIPTION + + bus.subscribe_to(scope, "TestEvent", event_handler) + bus.cancel_scope(scope) + var event: TestEvent = TestEvent.new(self) + bus.publish(scope, event) + + # Assert that the event was not received after scope cancellation + assert(event_received.value == false, "Event should not have been received after scope cancellation") + bus.dispose() + + +# Test function: Test multiple subscribers receiving the same event +func test_multiple_subscribers() -> void: + var bus: DefaultEventBus = DefaultEventBus.new() + var scope: DefaultScope = DefaultScope.new() + var event_received_by_handler1: Flag = Flag.new() + var event_received_by_handler2: Flag = Flag.new() + + var event_handler1 = func(_event: Event) -> int: + event_received_by_handler1.value = true + return EventResult.ResultType.KEEP_SUBSCRIPTION + + var event_handler2 = func(_event: Event) -> int: + event_received_by_handler2.value = true + return EventResult.ResultType.KEEP_SUBSCRIPTION + + bus.subscribe_to(scope, "TestEvent", event_handler1) + bus.subscribe_to(scope, "TestEvent", event_handler2) + var event: TestEvent = TestEvent.new(self) + bus.publish(scope, event) + + # Assert that both handlers received the event + assert(event_received_by_handler1.value == true, "First handler did not receive event") + assert(event_received_by_handler2.value == true, "Second handler did not receive event") + bus.dispose() + +class Flag: + var value = false + +class Counter: + var value = 0 + +class TestEvent extends Event: + func _init(_source: Variant = null) -> void: + super(_source) + type = "TestEvent" diff --git a/tests/tests.gd b/tests/tests.gd new file mode 100644 index 0000000..d53b6a2 --- /dev/null +++ b/tests/tests.gd @@ -0,0 +1,8 @@ +extends Node + +func _ready() -> void: + EventBusTests.run_tests() + get_tree().quit() + +func _process(_delta: float) -> void: + pass diff --git a/weapons_system.gd b/weapons_system.gd index 4050259..23e70e2 100644 --- a/weapons_system.gd +++ b/weapons_system.gd @@ -1,3 +1,4 @@ +class_name WeaponsSystem extends Node var weapons = []