Merge pull request 'event_bus' (#4) from Trugath/starcheese:event_bus into master

Reviewed-on: kanium/starcheese#4
This commit is contained in:
AllfatherHatt 2024-10-23 14:59:08 +02:00
commit 44e0c54c6f
25 changed files with 502 additions and 123 deletions

View File

@ -1,43 +1,41 @@
extends StaticBody2D 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 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(): func _ready():
current_health = max_health 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 on_damage_event(event: DamageEvent) -> EventResult.ResultType:
func take_damage(damage: float): if event.target != self or is_destroyed:
if is_destroyed: # Prevent taking damage if already destroyed return EventResult.ResultType.KEEP_SUBSCRIPTION
return
current_health -= damage # Subtract the damage from current health apply_damage(event.damage_value)
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: if current_health <= 0.0:
break_apart() emit_asteroid_destroyed()
return EventResult.ResultType.DISPOSE_SUBSCRIPTION
# Function to handle when the asteroid is destroyed return EventResult.ResultType.KEEP_SUBSCRIPTION
func break_apart():
is_destroyed = true # Mark the asteroid as destroyed
queue_free() # Remove the asteroid from the scene
print("Asteroid mined!")
# Load the Cheese scene func apply_damage(damage: float) -> void:
var cheese_scene = load("res://Cheese.tscn") current_health = max(current_health - damage, 0)
update_health_label()
# Instance the cheese func emit_asteroid_destroyed() -> void:
var cheese_instance = cheese_scene.instantiate() is_destroyed = true
queue_free()
publish_destroyed_event()
# Set the position where the cheese should spawn (at the asteroid's position) func publish_destroyed_event() -> void:
cheese_instance.global_position = global_position + Vector2(randf() * 50 - 25, randf() * 50 - 25) var destroyed_event = DestroyedEvent.new(self, "asteroid", global_position, "laser")
event_bus.publish(get_node("/root/Main/DestroyedEventScope"), destroyed_event)
# Add the cheese instance to the scene func update_health_label() -> void:
get_parent().add_child(cheese_instance)
# Function to update the health label display
func update_health_label():
$AsteroidHealthLabel.text = str(round(current_health)) + " / " + str(max_health) $AsteroidHealthLabel.text = str(round(current_health)) + " / " + str(max_health)

31
asteroid.tscn Normal file
View File

@ -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")

View File

@ -1,18 +1,12 @@
extends Area2D extends Area2D
func _ready(): func _ready():
pass # Replace with function body. pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta): func _process(delta):
pass pass
func _on_body_entered(body): func _on_body_entered(body):
if body.name == "spaceship": if body.name == "spaceship":
queue_free() # This removes the cheese from the scene queue_free()
print("Cheese collected!") # Replace this with actual game logic print("Cheese collected!")

10
damage_event.gd Normal file
View File

@ -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"

22
destroyed_event.gd Normal file
View File

@ -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
}

View File

@ -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()

2
eventbus/defaultscope.gd Normal file
View File

@ -0,0 +1,2 @@
class_name DefaultScope
extends EventScope

12
eventbus/event.gd Normal file
View File

@ -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

17
eventbus/eventbus.gd Normal file
View File

@ -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

View File

@ -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

7
eventbus/eventresult.gd Normal file
View File

@ -0,0 +1,7 @@
class_name EventResult
extends RefCounted
enum ResultType {
KEEP_SUBSCRIPTION,
DISPOSE_SUBSCRIPTION
}

2
eventbus/eventscope.gd Normal file
View File

@ -0,0 +1,2 @@
class_name EventScope
extends Node

5
eventbus/subscription.gd Normal file
View File

@ -0,0 +1,5 @@
class_name Subscription
extends RefCounted
func dispose() -> void:
pass

10
input_event.gd Normal file
View File

@ -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"

View File

@ -7,12 +7,14 @@ extends Node2D
var hardpoint = null # Reference to the attached hardpoint var hardpoint = null # Reference to the attached hardpoint
var is_firing = false # Track the laser's firing state var is_firing = false # Track the laser's firing state
var current_damage_output: float = 0.0 # Track the current damage output var current_damage_output: float = 0.0 # Track the current damage output
var event_bus: EventBus
func _ready(): func _ready():
# Register the laser to the "weapons" group # Register the laser to the "weapons" group
add_to_group("weapons") add_to_group("weapons")
# Connect to the correct hardpoint when ready # Connect to the correct hardpoint when ready
call_deferred("_connect_to_hardpoint") call_deferred("_connect_to_hardpoint")
event_bus = get_node("/root/Main/MainEventBus")
# Initialize LaserLine2D with two points (origin and target) # Initialize LaserLine2D with two points (origin and target)
$LaserLine2D.clear_points() $LaserLine2D.clear_points()
@ -32,16 +34,16 @@ func _connect_to_hardpoint():
func fire(): func fire():
is_firing = true is_firing = true
_process_laser(true)
current_damage_output = laser_damage current_damage_output = laser_damage
$DamageOutputLabel.text = str(current_damage_output) $DamageOutputLabel.text = str(current_damage_output)
_process_laser(true)
func cease_fire(): func cease_fire():
is_firing = false is_firing = false
_process_laser(false)
current_damage_output = 0.0 current_damage_output = 0.0
$DamageOutputLabel.text = str(current_damage_output) $DamageOutputLabel.text = str(current_damage_output)
_process_laser(false)
func _process(delta: float) -> void: func _process(delta: float) -> void:
if is_firing: if is_firing:
@ -52,27 +54,21 @@ func _process_laser(active: bool):
var raycast = $LaserBeam2D var raycast = $LaserBeam2D
raycast.enabled = active raycast.enabled = active
if active: if active:
raycast.force_raycast_update() raycast.force_raycast_update()
if raycast.is_colliding(): if raycast.is_colliding():
var collision_point = raycast.get_collision_point() var collision_point = raycast.get_collision_point()
$LaserLine2D.set_point_position(0, Vector2.ZERO) $LaserLine2D.set_point_position(0, Vector2.ZERO)
$LaserLine2D.set_point_position(1, to_local(collision_point)) $LaserLine2D.set_point_position(1, to_local(collision_point))
var collider = raycast.get_collider() var collider = raycast.get_collider()
if collider.has_method("take_damage"): if collider:
collider.take_damage(laser_damage) var damage_event = DamageEvent.new(laser_damage, collider)
event_bus.publish(get_node("/root/Main/DamageEventScope"), damage_event)
else: 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: else:
$LaserLine2D.clear_points() $LaserLine2D.clear_points()
$LaserLine2D.add_point(Vector2.ZERO) # Re-add the origin point $LaserLine2D.add_point(Vector2.ZERO)
$LaserLine2D.add_point(Vector2.ZERO) # Reset target point to origin $LaserLine2D.add_point(Vector2.ZERO)

80
main.gd Normal file
View File

@ -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

View File

@ -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://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="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://spaceship.gd" id="3_ttkgl"]
[ext_resource type="Script" path="res://laser.gd" id="4_uhf7q"] [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://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://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"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_3vtwu"]
radius = 48.0 radius = 48.0
@ -18,10 +17,8 @@ height = 102.0
offsets = PackedFloat32Array(0.961735, 1) offsets = PackedFloat32Array(0.961735, 1)
colors = PackedColorArray(1, 1, 0, 1, 1, 1, 1, 0.137255) 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"] [node name="Main" type="Node2D"]
script = ExtResource("1_eedai")
[node name="background" type="Sprite2D" parent="."] [node name="background" type="Sprite2D" parent="."]
position = Vector2(955, 537) position = Vector2(955, 537)
@ -29,7 +26,7 @@ scale = Vector2(2, 2)
texture = ExtResource("1_rpyi5") texture = ExtResource("1_rpyi5")
[node name="spaceship" type="CharacterBody2D" parent="."] [node name="spaceship" type="CharacterBody2D" parent="."]
position = Vector2(908, 835) position = Vector2(956, 623)
collision_layer = 2 collision_layer = 2
motion_mode = 1 motion_mode = 1
script = ExtResource("3_ttkgl") script = ExtResource("3_ttkgl")
@ -70,52 +67,14 @@ offset_top = -71.0
offset_right = 104.0 offset_right = 104.0
offset_bottom = -20.0 offset_bottom = -20.0
[node name="WeaponsSystem" type="Node" parent="spaceship"]
script = ExtResource("5_gf6oh")
[node name="HardPoint1" type="Node2D" parent="spaceship"] [node name="HardPoint1" type="Node2D" parent="spaceship"]
position = Vector2(0, -46) position = Vector2(0, -46)
script = ExtResource("7_6cr6a") script = ExtResource("7_6cr6a")
[node name="asteroid" type="StaticBody2D" parent="."] [node name="SpawnAsteroidButton" type="Button" parent="."]
position = Vector2(564, 144) offset_left = 72.0
collision_mask = 2 offset_top = 993.0
script = ExtResource("6_n4dsl") offset_right = 249.0
offset_bottom = 1049.0
[node name="Sprite2D" type="Sprite2D" parent="asteroid"] text = "SPAWN ASTEROID"
position = Vector2(1.00006, -3.99998) script = ExtResource("9_21dg0")
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

View File

@ -12,7 +12,7 @@ config_version=5
config/name="Star Cheese - The Final Fromage" config/name="Star Cheese - The Final Fromage"
run/main_scene="res://main.tscn" 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" config/icon="res://icon.svg"
[display] [display]
@ -24,7 +24,7 @@ window/size/viewport_height=1080
ui_fire={ ui_fire={
"deadzone": 0.5, "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)
] ]
} }

20
reward_spawner.gd Normal file
View File

@ -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)

View File

@ -8,10 +8,12 @@ extends CharacterBody2D
signal fire # Signal to start firing signal fire # Signal to start firing
signal stop_fire # Signal to stop firing signal stop_fire # Signal to stop firing
var weapons_system var weapons_system: WeaponsSystem
var event_bus: EventBus
func _ready(): 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: func get_spaceship_orientation_vector() -> Vector2:
return -global_transform.y return -global_transform.y
@ -45,13 +47,14 @@ func handle_movement(delta: float) -> void:
var collision_normal = collision.get_normal() var collision_normal = collision.get_normal()
velocity = velocity.slide(collision_normal) velocity = velocity.slide(collision_normal)
func handle_weapons_input() -> void: func handle_keyboard_input(event: KeyboardInputEvent):
if Input.is_action_pressed("ui_fire"): if( event.action == "ui_fire" ):
if( event.pressed ):
weapons_system.fire_all() weapons_system.fire_all()
else:
if Input.is_action_just_released("ui_fire"):
weapons_system.cease_fire_all() weapons_system.cease_fire_all()
pass
func _process(delta: float) -> void: func _process(delta: float) -> void:
handle_movement(delta) handle_movement(delta)
handle_weapons_input()

29
spawn_asteroid_button.gd Normal file
View File

@ -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

6
tests.tscn Normal file
View File

@ -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")

105
tests/test_eventbus.gd Normal file
View File

@ -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"

8
tests/tests.gd Normal file
View File

@ -0,0 +1,8 @@
extends Node
func _ready() -> void:
EventBusTests.run_tests()
get_tree().quit()
func _process(_delta: float) -> void:
pass

View File

@ -1,3 +1,4 @@
class_name WeaponsSystem
extends Node extends Node
var weapons = [] var weapons = []