### Setup Mana System with Throttled Regeneration Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Displays mana, implements mana regeneration with throttling, and starts regeneration in a separate process. Utilizes `subscribe`, `throttle_last`, and `Subject`. Assumes `mana` is an observable property. ```gdscript func _setup_mana_system() -> void: # Mana display _bag.add(mana.subscribe(func(mp): $UI/ManaBar.value = mp $UI/ManaLabel.text = "MP: %.0f" % mp )) # Mana regeneration (throttled updates) var regen_timer := Subject.new() _bag.add(regen_timer) _bag.add(regen_timer \ .throttle_last(1.0) \ .subscribe(func(_x): if mana.value < 50.0: mana.value = min(50.0, mana.value + 5.0) ) ) # Start regen in separate process _start_mana_regen(regen_timer) ``` -------------------------------- ### Player State Integration Example in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt A comprehensive example demonstrating a player system using various reactive patterns like ReactiveProperty and BehaviourSubject, along with operators and disposal strategies in Godot. This snippet focuses on defining player attributes and event subjects. ```gdscript extends CharacterBody2D class_name Player # Reactive properties for player state @onready var health := ReactiveProperty.new(100.0) @onready var mana := ReactiveProperty.new(50.0) @onready var level := ReactiveProperty.new(1) @onready var experience := ReactiveProperty.new(0) @onready var combat_state := BehaviourSubject.new("idle") # Event subjects @onready var damage_taken := Subject.new() @onready var ability_used := Subject.new() @onready var item_collected := Subject.new() ``` -------------------------------- ### Setup Level System with Experience Threshold Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Manages player leveling based on experience points, displaying the current level. It uses `select`, `where`, and `subscribe` to react to experience changes and trigger level updates. Assumes `experience`, `level` are observable properties. ```gdscript func _setup_level_system() -> void: # Level up when experience crosses threshold _bag.add(experience \ .select(func(xp): return xp / 100) \ .where(func(new_level): return new_level > level.value) \ .subscribe(func(new_level): level.value = new_level _on_level_up(new_level) ) ) # Level display _bag.add(level.subscribe(func(lvl): $UI/LevelLabel.text = "Level: %d" % lvl )) ``` -------------------------------- ### Setup Input Handling with Button Signals Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Converts UI button press signals into observables, applying debounce and mana checks before triggering actions. Uses `Observable.from_signal`, `debounce`, and `where`. Assumes `mana` is an observable property. ```gdscript func _setup_input_handling() -> void: # Convert button signals to observables var attack_btn := Observable.from_signal($UI/AttackButton.pressed) var heal_btn := Observable.from_signal($UI/HealButton.pressed) # Attack button with mana check _bag.add(attack_btn \ .where(func(_x): return mana.value >= 10.0) \ .subscribe(func(_x): _cast_attack()) ) # Heal button with debounce to prevent spam _bag.add(heal_btn \ .debounce(0.5) \ .where(func(_x): return mana.value >= 15.0) \ .subscribe(func(_x): _cast_heal()) ) ``` -------------------------------- ### Start Mana Regeneration Timer Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Initializes and starts a timer to periodically emit events for mana regeneration. This function creates a `Timer` node, connects its `timeout` signal to a `Subject`, and adds it to the scene tree. Assumes `Subject` and `Unit` are available. ```gdscript func _start_mana_regen(regen_subject: Subject) -> void: var timer := Timer.new() timer.wait_time = 0.1 timer.timeout.connect(func(): regen_subject.on_next(Unit.default)) add_child(timer) timer.start() ``` -------------------------------- ### Setup Health System with Observables Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Monitors health changes, displays HP, handles critical health warnings, and manages player death. Uses `subscribe`, `where`, `take`, and `throttle_last` operators. Assumes `health`, `damage_taken`, and `health` are observable properties. ```gdscript func _setup_health_system() -> void: # Monitor health changes with visual feedback _bag.add(health.subscribe(func(hp): $UI/HealthBar.value = hp $UI/HealthLabel.text = "HP: %.0f" % hp )) # Critical health warning (one-time) _bag.add(health \ .where(func(hp): return hp <= 20.0) \ .take(1) \ .subscribe(func(_hp): $UI/CriticalWarning.show() $AudioManager.play("low_health_warning") ) ) # Death handling _bag.add(health \ .where(func(hp): return hp <= 0.0) \ .take(1) \ .subscribe(func(_hp): _on_player_death()) ) # Process damage events with debounce for damage numbers _bag.add(damage_taken \ .subscribe(func(amount): health.value -= amount _show_damage_number(amount) ) ) ``` -------------------------------- ### Setup Combat System with Event Merging Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Combines damage taken and ability used events into a single observable stream for logging and state updates. Uses `Observable.merge`, `select`, and `subscribe`. Assumes `damage_taken` and `ability_used` are observable events. ```gdscript func _setup_combat_system() -> void: # Merge all combat-related events var all_combat := Observable.merge( damage_taken.select(func(dmg): return {"type": "damage", "value": dmg}), ability_used.select(func(ability): return {"type": "ability", "name": ability}) ) # Log all combat events _bag.add(all_combat.subscribe(func(event): print("Combat event: ", event) )) # Update combat state _bag.add(combat_state.subscribe(func(state): print("Combat state changed: ", state) match state: "combat": $AnimationPlayer.play("combat_stance") "idle": $AnimationPlayer.play("idle") )) ``` -------------------------------- ### BehaviourSubject Initialization and Subscription in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Demonstrates the functionality of BehaviourSubject, which requires an initial value and emits its current value upon subscription. The example shows subscribing, updating the value, and how new subscribers receive the latest value immediately. It also includes disposing of the BehaviourSubject. ```gdscript var status := BehaviourSubject.new("idle") # Subscribe - immediately gets current value status.subscribe(func(x): print("Status: " + x)) # Update status status.on_next("loading") status.on_next("complete") # New subscriber gets the latest value immediately status.subscribe(func(x): print("New subscriber: " + x)) # Dispose status.dispose() ``` -------------------------------- ### Manage ReactiveProperty Lifecycle and Value (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Shows basic usage of ReactiveProperty in GDScript, including getting its current value, subscribing to changes, updating the value, and disposing of the property to free resources. This is fundamental for reactive data flow. ```gdscript var health := ReactiveProperty.new(100.0) # Gets the value print(health.value) # Subscribe to health changes health.subscribe(func(x): print(x)) # Update health health.value = 50.0 # Dispose health.dispose() ``` -------------------------------- ### Implement Two-Way Bindable Properties with ReactiveProperty - GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Shows how to use ReactiveProperty for two-way bindable properties in Godot, enabling automatic change notifications. This example demonstrates subscribing to health changes, using operators like `where` and `take` for conditional subscriptions, updating property values, and reading current values. It also covers automatic disposal when nodes are removed from the tree. ```gdscript extends Node2D @onready var player_health := ReactiveProperty.new(100.0) @onready var player_mana := ReactiveProperty.new(50.0) func _ready() -> void: # Subscribe to health changes player_health.subscribe(func(hp): $HealthLabel.text = "HP: %.0f" % hp ) # Immediately called with current value (100.0) # Subscribe with operator chain player_health \ .where(func(hp): return hp <= 20.0) \ .take(1) \ .subscribe(func(_hp): print("Critical health!") $LowHealthWarning.show() ) # Update values triggers notifications player_health.value -= 15.0 # Subscribers notified player_mana.value += 10.0 # Read current value if player_health.value < 50.0: print("Health is low: ", player_health.value) # Auto-dispose on tree exit player_health.add_to(self) player_mana.add_to(self) func take_damage(amount: float) -> void: player_health.value -= amount ``` -------------------------------- ### Skip - Ignore First N Values in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt The 'skip' operator ignores the first N emissions from a signal and starts emitting values only after that count is reached. This is practical for skipping initial or warmup periods. ```gdscript extends Node func _ready() -> void: var updates := Subject.new() # Ignore first 2 updates (warmup period) updates \ .skip(2) \ .subscribe(func(value): print("Processing: ", value)) updates.on_next("Warmup 1") # Skipped updates.on_next("Warmup 2") # Skipped updates.on_next("Real 1") # Processing: Real 1 updates.on_next("Real 2") # Processing: Real 2 # Practical: skip initial value from ReactiveProperty var player_score := ReactiveProperty.new(0) player_score \ .skip(1) \ .subscribe(func(score): print("Score changed to: ", score) _save_score(score) ) # First subscription call (with value 0) is skipped player_score.value = 100 # Score changed to: 100 func _save_score(score: int) -> void: print("Saving score: ", score) ``` -------------------------------- ### Ignoring Stream Values with Subject Subscription in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Shows how to subscribe to a Subject when the emitted value is not needed. This is achieved by using a parameter-less function in the subscribe method, effectively ignoring the stream values. Practical examples for button clicks are provided. ```gdscript var subject := Subject.new() var subscription := subject.subscribe(func(): print("Hello, World!")) # No argument subject.on_next(Unit.default) # Practical examples of ignoring stream values button_clicks.subscribe(func(): print("Button was clicked!")) var click_count = 0 button_clicks.subscribe(func(): click_count += 1) ``` -------------------------------- ### Manage Stateful Observables with BehaviourSubject - GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Illustrates the use of BehaviourSubject to manage stateful observables in Godot. New subscribers immediately receive the last emitted value. This example shows creating a BehaviourSubject, subscribing to state changes, updating the state, and how late subscribers receive the most current value. It also demonstrates reading the current value synchronously and auto-disposing when the node exits the tree. ```gdscript extends Node @onready var game_state := BehaviourSubject.new("menu") func _ready() -> void: # New subscribers immediately receive current value game_state.subscribe(func(state): print("Game state: ", state) ) # Prints: "Game state: menu" # Update state game_state.on_next("playing") # All subscribers notified game_state.on_next("paused") # Late subscriber gets latest value immediately game_state.subscribe(func(state): print("Late subscriber sees: ", state) ) # Prints: "Late subscriber sees: paused" # Read current value synchronously print("Current: ", game_state.value) # Prints: "Current: paused" # Auto-dispose when node exits tree game_state.add_to(self) ``` -------------------------------- ### Encapsulate Properties with ReadOnlyReactiveProperty - GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Demonstrates how to use ReadOnlyReactiveProperty in Godot for providing safe, read-only access to mutable properties. This pattern enhances encapsulation by hiding write operations from external code. The example shows defining internal `ReactiveProperty` instances and exposing them as `ReadOnlyReactiveProperty` through getters, allowing external subscriptions and reads while preventing direct modification. ```gdscript class_name PlayerStats extends Node # Private mutable property var _health: ReactiveProperty = ReactiveProperty.new(100) var _armor: ReactiveProperty = ReactiveProperty.new(50) # Public read-only access var health: ReadOnlyReactiveProperty: get: return _health var armor: ReadOnlyReactiveProperty: get: return _armor func _ready() -> void: # External code can subscribe and read health.subscribe(func(hp): print("HP changed: ", hp)) print("Current HP: ", health.current_value) func take_damage(amount: int) -> void: # Only internal code can modify var damage := max(0, amount - _armor.current_value / 10) _health.value -= damage func heal(amount: int) -> void: _health.value = min(100, _health.value + amount) ``` -------------------------------- ### Basic Subject Emission and Subscription in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Illustrates the basic usage of the Subject class for implementing the observer pattern. It shows how to create a Subject, subscribe to it, emit values (including the Unit type for signals without data), and unsubscribe. It also demonstrates how to dispose of the Subject itself. ```gdscript var subject := Subject.new() var subscription := subject.subscribe(func(_x): print("Hello, World!")) # Emit values subject.on_next(Unit.default) # Explicit Unit subject.on_next() # Same as above - Unit.default is used automatically subject.on_next("data") # Emit actual data # Unsubscribe subscription.dispose() # Dispose subject subject.dispose() ``` -------------------------------- ### Save and Load Reactive Properties with ConfigFile in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Demonstrates saving ReactiveProperty and BehaviourSubject instances to a ConfigFile and then loading them back. This allows for game state persistence. The loaded properties retain their values and subscription capabilities. ```gdscript extends Node const SAVE_PATH := "user://game_data.cfg" func _ready() -> void: _test_save_and_load() func _test_save_and_load() -> void: # Create and configure reactive properties var player_health := ReactiveProperty.new(75) var player_level := ReactiveProperty.new(5) var game_status := BehaviourSubject.new("playing") # Save to config file var config := ConfigFile.new() config.set_value("player", "health", player_health) config.set_value("player", "level", player_level) config.set_value("game", "status", game_status) var err := config.save(SAVE_PATH) if err == OK: print("Game data saved successfully") # Load from config file var load_config := ConfigFile.new() err = load_config.load(SAVE_PATH) if err == OK: var loaded_health: ReactiveProperty = load_config.get_value("player", "health") var loaded_level: ReactiveProperty = load_config.get_value("player", "level") var loaded_status: BehaviourSubject = load_config.get_value("game", "status") # Values are restored, subscriptions work normally loaded_health.subscribe(func(hp): print("Loaded health: ", hp) ) # Prints: Loaded health: 75 loaded_level.subscribe(func(lvl): print("Loaded level: ", lvl) ) # Prints: Loaded level: 5 print("Current status: ", loaded_status.value) # playing ``` -------------------------------- ### Subscribe and Manage Reactive Properties in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Demonstrates subscribing to a ReactiveProperty, applying operators like 'where' and 'take', and managing subscriptions by adding them to a Node for automatic disposal. It shows how to update the property's value and handle updates. ```gdscript extends Node2D @onready var health := ReactiveProperty.new(100.0) func _ready() -> void: # Subscribe reactive property var d1 := health.subscribe(_update_label) # Subscribe reactive property with operator var d2 := health \ .where(func(x): return x <= 0.0) \ .take(1) \ .subscribe(func(_x): print("Dead")) # Dispose when this node exiting tree for d in [health, d1, d2]: d.add_to(self) func _update_label(value: float) -> void: print("Health: %s" % value) func take_damage(damage: float) -> void: # Update reactive property value health.value -= damage ``` -------------------------------- ### Utility Functions for Game Logic Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Provides helper functions for displaying damage numbers and processing incoming damage. `_show_damage_number` simulates visual feedback, while `take_damage` emits a damage event. ```gdscript func _show_damage_number(amount: float) -> void: print("Damage: %.0f" % amount) func take_damage(amount: float) -> void: damage_taken.on_next(amount) func gain_experience(amount: int) -> void: experience.value += amount ``` -------------------------------- ### GDScript Command Line Tools Source: https://github.com/minami110/godot-signal-extensions/blob/main/CLAUDE.md Provides essential command-line tools for interacting with GDScript projects. These commands facilitate testing, file management (moving, renaming, deleting), and code validation, ensuring code quality and project maintainability. ```bash /gdscript-test-skill ``` ```bash /gdscript-file-manager-skill ``` ```bash /gdscript-validate-skill ``` -------------------------------- ### Emit Manual Events with Subject - GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Demonstrates how to create and use a Subject for manual event emission in Godot. This includes subscribing to events, emitting values, handling parameter-less callbacks, and disposing of subscriptions. Subject acts as both an observable and an observer, allowing direct control over data flow. ```gdscript extends Node func _ready() -> void: # Create a subject var click_subject := Subject.new() # Subscribe to events var subscription := click_subject.subscribe(func(msg): print("Received: ", msg) ) # Emit values manually click_subject.on_next("Button clicked!") click_subject.on_next("Another event") # Parameter-less callbacks ignore emitted values var counter := 0 click_subject.subscribe(func(): counter += 1) click_subject.on_next() # Emits Unit.default # Cleanup subscription.dispose() click_subject.dispose() ``` -------------------------------- ### Player Death and Level Up Handlers Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Handles player death by setting the combat state and logs level-up events, restoring health and mana. These functions represent game state transitions triggered by observable events. ```gdscript func _on_player_death() -> void: print("Player died!") combat_state.on_next("dead") # All subscriptions auto-disposed on tree exit func _on_level_up(new_level: int) -> void: print("Level up! Now level: ", new_level) health.value = 100.0 # Restore health on level up mana.value = 50.0 ``` -------------------------------- ### Serialize ReactiveProperty and BehaviourSubject with ConfigFile (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Demonstrates how to save and load the current values of ReactiveProperty and BehaviourSubject using Godot's ConfigFile in GDScript. This enables persistent storage of reactive observable states. ```gdscript # Saving ReactiveProperty and BehaviourSubject to ConfigFile var health := ReactiveProperty.new(100) var status := BehaviourSubject.new("idle") var config := ConfigFile.new() config.set_value("player", "health", health) config.set_value("player", "status", status) config.save("user://player_data.cfg") # Loading from ConfigFile var config := ConfigFile.new() config.load("user://player_data.cfg") var loaded_health: ReactiveProperty = config.get_value("player", "health") var loaded_status: BehaviourSubject = config.get_value("player", "status") # Subscribe to loaded observables loaded_health.subscribe(func(value): print("Loaded health: ", value)) loaded_status.subscribe(func(value): print("Loaded status: ", value)) ``` -------------------------------- ### Manual Subscription Disposal - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Demonstrates manual disposal of signal subscriptions when controlling the lifecycle explicitly. It involves storing the subscription and calling its `dispose()` method when cleanup is needed, typically in `_exit_tree`. ```gdscript extends Node var _subject: Subject var _subscription: Disposable func _ready() -> void: _subject = Subject.new() _subscription = _subject.subscribe(func(x): print(x)) _subject.on_next("Hello") func _exit_tree() -> void: # Manual cleanup required _subscription.dispose() _subject.dispose() ``` -------------------------------- ### Take - Emit First N Values in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt The 'take' operator emits only the first N values from a signal and then automatically completes the subscription. This is useful for scenarios requiring a one-time action or initialization. ```gdscript extends Node func _ready() -> void: var messages := Subject.new() # Only receive first 3 messages messages \ .take(3) \ .subscribe(func(msg): print("Received: ", msg)) messages.on_next("First") # Received: First messages.on_next("Second") # Received: Second messages.on_next("Third") # Received: Third messages.on_next("Fourth") # Not received (auto-completed) # Practical: one-time initialization var game_ready := Subject.new() game_ready \ .take(1) \ .subscribe(func(_x): _load_player_data() _initialize_world() ) func _load_player_data() -> void: print("Loading player data...") func _initialize_world() -> void: print("Initializing world...") ``` -------------------------------- ### Merge and Wait for First Emission - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Combines multiple observables using `merge()` and then awaits the first emission from any of the sources using `wait()`. This is effective for handling race conditions or waiting for the first signal from a set of possible events, such as button presses. ```gdscript extends Control func _ready() -> void: await _wait_for_any_button() func _wait_for_any_button() -> void: var ok_pressed := Observable.from_signal($OKButton.pressed) var cancel_pressed := Observable.from_signal($CancelButton.pressed) # Wait for whichever button is pressed first var merged := Observable.merge(ok_pressed, cancel_pressed) var result := await merged.wait() # Only one wait() resolves, others auto-disposed if result == null: print("Dialog cancelled") else: print("Dialog confirmed") ``` -------------------------------- ### Observable.from_signal: Godot Signals to Observable Streams Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Converts Godot signals into reactive Observable streams. Supports unlimited arguments using Godot 4.5+ variadic arguments. If the signal has 0 arguments, it is converted to `Unit`. For signals with 2 or more arguments, the values are converted into an Array. ```gdscript Observable \ .from_signal($Button.pressed) \ .subscribe(func(_x): print("pressed")) ``` ```gdscript # Multi-argument signal example signal player_moved(position: Vector2, velocity: Vector2) Observable \ .from_signal(player_moved) \ .subscribe(func(args: Array): var pos = args[0] as Vector2 var vel = args[1] as Vector2 print("Player at ", pos, " moving at ", vel)) ``` -------------------------------- ### Convert Godot Signals to Observable Streams with Observable.from_signal Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Converts Godot signals into reactive Observable streams, supporting signals with any number of arguments. This allows for declarative handling of events like button presses or text changes using reactive patterns. Dependencies include the Observable class. ```gdscript extends Control @onready var submit_button := $SubmitButton @onready var input_field := $LineEdit func _ready() -> void: # Convert button pressed signal (0 arguments) Observable \ .from_signal(submit_button.pressed) \ .subscribe(func(_x): _on_submit()) # Convert text changed signal (1 argument) Observable \ .from_signal(input_field.text_changed) \ .debounce(0.3) \ .subscribe(func(text): _validate_input(text)) # Multi-argument signal var player := $Player Observable \ .from_signal(player.position_changed) \ .subscribe(func(args: Array): var pos := args[0] as Vector2 var velocity := args[1] as Vector2 print("Player at ", pos, " moving ", velocity) ) func _on_submit() -> void: print("Form submitted: ", input_field.text) func _validate_input(text: String) -> void: if text.length() < 3: $ValidationLabel.text = "Too short" else: $ValidationLabel.text = "Valid" ``` -------------------------------- ### Manage Disposable Resources with Node Tree Exit (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Shows how to use the `add_to(self)` method to automatically dispose of Subjects and their subscriptions when a Node exits its tree in GDScript. This integrates disposable management with Godot's node lifecycle. ```gdscript extends Node @onready var _subject := Subject.new() func _ready() -> void: # Will dispose subject when node exiting _subject.add_to(self) # Will dispose subscription when node exiting _subject.subscribe(func(x): print(x)).add_to(self) ``` -------------------------------- ### Manage Multiple Disposables with an Array (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Demonstrates a manual approach to managing multiple disposable objects by adding them to a GDScript Array and then iterating to dispose of them. This is an alternative to using a DisposableBag. ```gdscript var bag: Array = [] subject.add_to(bag) subject.subscribe(func(x): print(x)).add_to(bag) for d in bag: d.dispose() ``` -------------------------------- ### Expose Observable for Safe Event Subscription (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Illustrates how to expose an Observable from a Subject in GDScript. This pattern allows internal emission of events while only providing subscription functionality to external consumers, ensuring controlled event handling. ```gdscript class_name EventManager extends Node # Private: Internal event emission var _button_pressed: Subject = Subject.new() # Public: Only subscription exposed externally var button_pressed: Observable: get: return _button_pressed func _on_button_clicked() -> void: # Internal code can emit events _button_pressed.on_next() ``` -------------------------------- ### DisposableBag for Batch Management - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Utilizes a `DisposableBag` to group and manage multiple disposables collectively. Disposables can be added individually or as a batch. The bag itself can be automatically disposed when added to a Godot Node, ensuring all contained disposables are cleaned up. ```gdscript extends Node2D @onready var _bag := DisposableBag.new() func _ready() -> void: var health := ReactiveProperty.new(100) var mana := ReactiveProperty.new(50) var stamina := ReactiveProperty.new(100) # Add all disposables to bag _bag.add(health, mana, stamina) _bag.add(health.subscribe(func(hp): $HealthBar.value = hp)) _bag.add(mana.subscribe(func(mp): $ManaBar.value = mp)) _bag.add(stamina.subscribe(func(sp): $StaminaBar.value = sp)) # Auto-dispose bag when node exits _bag.add_to(self) # Manual cleanup options: # _bag.clear() # Dispose all items, bag remains usable # _bag.dispose() # Dispose all items, bag becomes unusable ``` -------------------------------- ### Create ReadOnlyReactiveProperty for Safe External Access (GDScript) Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Demonstrates how to create a ReadOnlyReactiveProperty in GDScript to manage internal state while exposing only read and subscription capabilities externally. This prevents unintended modifications from outside the class. ```gdscript class_name PlayerHealth extends Node # Private: Can only be modified internally var _health: ReactiveProperty = ReactiveProperty.new(100) # Public: Exposed as read-only to external consumers var health: ReadOnlyReactiveProperty: get: return _health func take_damage(damage: int) -> void: # Internal code can modify the value _health.value -= damage func _ready() -> void: # External code can only subscribe and read health.subscribe(func(hp): print("Health: ", hp)) print("Current health: ", health.current_value) ``` -------------------------------- ### Manual Signal Subscription Disposal in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/CLAUDE.md Illustrates the manual method for disposing of signal subscriptions in GDScript. While functional, this approach requires explicit calls to `dispose()` and is generally less recommended than automatic disposal for simplifying resource management. ```GDScript subscription.dispose() ``` -------------------------------- ### Player Action Implementations Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Defines functions for casting attacks and heals, updating mana, and triggering associated events. These functions demonstrate direct manipulation of observable properties and emitting custom events. ```gdscript func _cast_attack() -> void: mana.value -= 10.0 ability_used.on_next("fireball") print("Cast Fireball!") func _cast_heal() -> void: mana.value -= 15.0 health.value = min(100.0, health.value + 30.0) ability_used.on_next("heal") print("Cast Heal!") ``` -------------------------------- ### Limit Emissions with take - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `take` operator emits only the first N values from the source observable. After emitting the specified number of values, the subscription automatically completes. The output is an array containing the first N emitted values. ```gdscript subject.take(2).subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) subject.on_next(3) ``` -------------------------------- ### Automatic Signal Subscription Disposal in GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/CLAUDE.md Demonstrates the recommended pattern for automatic disposal of signal subscriptions within Godot. This pattern is crucial for preventing memory leaks and ensuring proper resource management in reactive programming contexts. It leverages the `add_to` method for automatic cleanup. ```GDScript observable.subscribe(callback).add_to(self) ``` -------------------------------- ### Await Observable Emission (wait()) - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Allows asynchronous retrieval of the next observable emission using the `wait()` method. This is useful for synchronizing game logic with asynchronous events or state changes. It suspends execution until an emission occurs. ```gdscript extends Node func _ready() -> void: await _wait_for_player_ready() await _wait_for_first_input() func _wait_for_player_ready() -> void: var game_state := BehaviourSubject.new("loading") # Spawn a task that updates state _async_load_game(game_state) # Wait for next state change (not current value) var next_state := await game_state.wait() print("Game state changed to: ", next_state) func _async_load_game(state: BehaviourSubject) -> void: await get_tree().create_timer(2.0).timeout state.on_next("ready") func _wait_for_first_input() -> void: var input_subject := Subject.new() # Wait for any input var first_input := await input_subject.wait() print("First input received: ", first_input) # Simulate input after delay get_tree().create_timer(1.0).timeout.connect( func(): input_subject.on_next("jump") ) ``` -------------------------------- ### Ignore Initial Emissions with skip - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `skip` operator ignores the first N emissions from the source observable. Subscriptions only begin receiving values after the specified count has been emitted and skipped. The output is an array of values emitted after the skip count. ```gdscript subject.skip(2).subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) subject.on_next(3) subject.on_next(1) ``` -------------------------------- ### Awaiting Observable Emissions with `wait()` in Godot Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Observable classes support awaiting the next value emission using the `wait()` method, similar to Godot's signal await. `wait()` returns `null` if the observable is disposed. Operator chains do not support direct await; use subscribe instead. For immediate values, use the `.value` property on BehaviourSubject/ReactiveProperty. ```gdscript # Basic await usage var next_value = await subject.wait() var health_change = await health.wait() ``` ```gdscript var subject := Subject.new() subject.on_next("first") # This waits for the next emission var result = await subject.wait() # Will wait subject.on_next("second") # result becomes "second" ``` ```gdscript var status := BehaviourSubject.new("idle") # To get current value: status.value # To wait for next change: await status.wait() ``` ```gdscript var health := ReactiveProperty.new(100) health.value = 90 # This change occurs immediately # Wait for the next change var new_health = await health.wait() ``` ```gdscript var s1 := Subject.new() var s2 := Subject.new() var merged := Observable.merge(s1, s2) # Will resolve with whichever emits first var first_result = await merged.wait() s2.on_next("second wins") # first_result becomes "second wins" s1.on_next("first") # This won't affect the await result ``` -------------------------------- ### Automatic Disposal with add_to() - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Enables automatic disposal of observables and subscriptions when their associated Godot Node exits the scene tree. This is the recommended pattern for managing resource cleanup, as it simplifies code and prevents memory leaks. ```gdscript extends Node func _ready() -> void: var health := ReactiveProperty.new(100) var updates := Subject.new() # Auto-dispose when this node exits tree health.add_to(self) updates.add_to(self) # Subscriptions also auto-dispose health.subscribe(func(hp): print("HP: ", hp)).add_to(self) updates.subscribe(func(msg): print(msg)).add_to(self) # No manual cleanup needed - all disposed on tree_exiting ``` -------------------------------- ### Take While - Emit While Condition True in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt The 'take_while' operator emits values as long as a given predicate returns true. It completes the subscription once the predicate returns false. This is useful for collecting items until a certain condition is met. ```gdscript extends Node func _ready() -> void: var countdown := Subject.new() # Process while countdown is positive countdown \ .take_while(func(n): return n > 0) \ .subscribe(func(n): print("Countdown: ", n)) countdown.on_next(5) # Countdown: 5 countdown.on_next(4) # Countdown: 4 countdown.on_next(3) # Countdown: 3 countdown.on_next(0) # Not emitted (predicate false) countdown.on_next(2) # Not emitted (already completed) # Practical: collect items until inventory full var items_collected := Subject.new() var inventory_count := 0 items_collected \ .take_while(func(_item): return inventory_count < 10) \ .subscribe(func(item): inventory_count += 1 print("Added to inventory: ", item) ) ``` -------------------------------- ### Conditionally Emit and Complete with take_while - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `take_while` operator emits values as long as a predicate function returns true. As soon as the predicate returns false, the observable automatically completes. The output is an array of values emitted while the condition was met. ```gdscript subject .take_while(func(x): return x <= 1) .subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) subject.on_next(1) ``` -------------------------------- ### Combine Multiple Observable Streams with Observable.merge Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Merges multiple observable streams into a single stream that emits values from any of the source streams. This is useful for consolidating various input sources, such as keyboard, gamepad, and network events, into a unified event stream for processing. Requires Subject and Observable classes. ```gdscript extends Node func _ready() -> void: var keyboard_input := Subject.new() var gamepad_input := Subject.new() var network_input := Subject.new() # Merge all input sources var all_inputs := Observable.merge( keyboard_input, gamepad_input, network_input ) # Single subscriber handles all inputs all_inputs.subscribe(func(input_event): _process_input(input_event) ).add_to(self) # Simulate various inputs keyboard_input.on_next({"type": "keyboard", "key": "W"}) gamepad_input.on_next({"type": "gamepad", "button": "A"}) network_input.on_next({"type": "network", "action": "jump"}) # All inputs are handled by the same subscriber # Output: # Processing: {"type": "keyboard", "key": "W"} # Processing: {"type": "gamepad", "button": "A"} # Processing: {"type": "network", "action": "jump"} func _process_input(input: Dictionary) -> void: print("Processing: ", input) ``` -------------------------------- ### Observable.merge: Combine Multiple Observable Streams Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Merges multiple observables into a single observable stream using variadic arguments for intuitive syntax. It combines streams so that values are emitted as they arrive from any source. Subscriptions to other sources are automatically disposed once a value is emitted when using `merge().wait()`. ```gdscript var s1 := Subject.new() var s2 := Subject.new() var s3 := Subject.new() Observable \ .merge(s1, s2, s3) \ .subscribe(func(x): arr.push_back(x)) s1.on_next("foo") s2.on_next("bar") s3.on_next("baz") ``` -------------------------------- ### Emit Last Value in Interval with throttle_last (sample) - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md Both `throttle_last()` and `sample()` are aliases for the same functionality: emitting the most recent item within periodic time intervals. This operator is useful for scenarios where you only care about the latest data points within a given timeframe. The output is an array of the last emitted values within each interval. ```gdscript subject.throttle_last(0.1).subscribe(func(x): arr.push_back(x)) # sample() is an alias for throttle_last() # subject.sample(0.1).subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) await get_tree().create_timer(0.05).timeout subject.on_next(3) await get_tree().create_timer(0.05).timeout subject.on_next(4) await get_tree().create_timer(0.1).timeout ``` -------------------------------- ### DisposableBag: Managing Disposable Objects in Godot Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md DisposableBag is a container for managing multiple Disposable objects. It automatically disposes all contained items when the bag itself is disposed. Items can be added using `add()` and the bag can be automatically disposed when its owning node exits using `add_to(self)`. It offers `clear()` for manual cleanup and `dispose()` to make the bag unusable. ```gdscript extends Node @onready var _subject1 := Subject.new() @onready var _subject2 := Subject.new() @onready var _bag := DisposableBag.new() func _ready() -> void: # Add disposables to the bag _bag.add(_subject1) _bag.add(_subject2) _bag.add(_subject1.subscribe(func(x): print("Subject1: ", x))) _bag.add(_subject2.subscribe(func(x): print("Subject2: ", x))) # The bag itself can be auto-disposed when node exits _bag.add_to(self) # Test emissions _subject1.on_next("Hello") _subject2.on_next("World") # Manual cleanup options: # _bag.clear() # Disposes all items but bag can still be used # _bag.dispose() # Disposes all items and makes bag unusable ``` -------------------------------- ### Conditionally Skip Emissions with skip_while - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `skip_while` operator skips emissions as long as a provided predicate function returns true. Once the predicate returns false, all subsequent emissions are processed, regardless of the condition. The output is an array of values emitted after the condition fails. ```gdscript subject .skip_while(func(x): return x <= 1) .subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) subject.on_next(1) ``` -------------------------------- ### Transform Values with select (map) - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `select` operator transforms each emitted value by applying a provided function. It's analogous to the `map` operator in other reactive programming libraries. This function takes an input value and returns a transformed output value. The output is an array of transformed values. ```gdscript subject .select(func(x): return x * 2) .subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) ``` -------------------------------- ### Throttle Last Signal Emissions - Gdscript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Emits the most recent signal value at periodic time intervals. This is useful for limiting the rate of updates, such as network updates, to prevent overwhelming the system. It takes a time interval in seconds as input. ```gdscript extends CharacterBody2D var position_updates := Subject.new() func _ready() -> void: # Send position updates max once per 0.1 seconds position_updates \ .throttle_last(0.1) \ .subscribe(func(pos): _send_network_update(pos)) # Even if position updates 60 times per second, # network only receives ~10 updates per second func _physics_process(_delta: float) -> void: move_and_slide() position_updates.on_next(global_position) func _send_network_update(pos: Vector2) -> void: print("Network update: ", pos) # Send to multiplayer peers... ``` -------------------------------- ### Filter Values by Predicate with where Operator Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Filters emitted values from an observable stream, allowing only those that satisfy a given predicate function to pass through. This operator is essential for selectively processing data based on specific conditions. It can be chained with other operators for complex data flow control. Requires Subject or ReactiveProperty and Observable classes. ```gdscript extends Node func _ready() -> void: var scores := Subject.new() # Only process high scores scores \ .where(func(score): return score >= 100) \ .subscribe(func(score): print("High score achieved: ", score) _award_bonus(score) ) scores.on_next(50) # Filtered out scores.on_next(150) # Prints: "High score achieved: 150" scores.on_next(75) # Filtered out scores.on_next(200) # Prints: "High score achieved: 200" # Combine where with other operators var enemy_health := ReactiveProperty.new(100) enemy_health \ .where(func(hp): return hp > 0) \ .where(func(hp): return hp <= 30) \ .take(1) \ .subscribe(func(_hp): print("Enemy weakened - enable finisher!") ) func _award_bonus(score: int) -> void: print("Bonus awarded for score: ", score) ``` -------------------------------- ### Skip While - Skip While Condition True in GDScript Source: https://context7.com/minami110/godot-signal-extensions/llms.txt The 'skip_while' operator skips values from a signal as long as a given predicate returns true. Once the predicate returns false, it emits that value and all subsequent values, regardless of the condition. This is useful for waiting for a specific game state. ```gdscript extends Node func _ready() -> void: var game_events := Subject.new() var game_started := false # Skip events until game starts game_events \ .skip_while(func(_event): return not game_started) \ .subscribe(func(event): _handle_game_event(event)) game_events.on_next("loading") # Skipped game_events.on_next("connecting") # Skipped game_started = true game_events.on_next("player_move") # Handled game_events.on_next("enemy_spawn") # Handled func _handle_game_event(event: String) -> void: print("Game event: ", event) ``` -------------------------------- ### Transform Emitted Values with select Operator Source: https://context7.com/minami110/godot-signal-extensions/llms.txt Applies a transformation function to each value emitted by an observable stream. This operator is analogous to the 'map' function in other reactive programming libraries. It's useful for modifying or restructuring data before it's consumed by subscribers. Requires Subject or ReactiveProperty and Observable classes. ```gdscript extends Node func _ready() -> void: var numbers := Subject.new() # Transform numbers to their squares numbers \ .select(func(x): return x * x) \ .subscribe(func(result): print("Square: ", result)) numbers.on_next(2) # Prints: Square: 4 numbers.on_next(5) # Prints: Square: 25 # Chain multiple transformations var player_xp := ReactiveProperty.new(0) player_xp \ .select(func(xp): return xp / 100) \ .select(func(level): return level + 1) \ .subscribe(func(level): $LevelLabel.text = "Level: %d" % level ) player_xp.value = 250 # Level: 3 player_xp.value = 500 # Level: 6 ``` -------------------------------- ### Filter Values with where (filter) - GDScript Source: https://github.com/minami110/godot-signal-extensions/blob/main/README.md The `where` operator filters emitted values, allowing only those that satisfy a predicate condition to pass through. It's analogous to the `filter` operator in other reactive programming libraries. The output is an array of values that met the predicate condition. ```gdscript subject .where(func(x): return x >= 2) .subscribe(func(x): arr.push_back(x)) subject.on_next(1) subject.on_next(2) subject.on_next(3) ```