diff --git a/data/owniverse_audio_loader.gd b/data/owniverse_audio_loader.gd index e8d6a87..e1e6c05 100644 --- a/data/owniverse_audio_loader.gd +++ b/data/owniverse_audio_loader.gd @@ -23,7 +23,7 @@ const EFFECTS: Dictionary = { "select": preload("res://sound/effects/select.ogg"), "star_birth": preload("res://sound/effects/star_birth.ogg"), "structure_decay": preload("res://sound/effects/structure_decay.ogg"), - "swipe": preload("res://sound/effects/swipe.ogg"), + #"swipe": preload("res://sound/effects/swipe.ogg"), "time_minus": preload("res://sound/effects/time_minus.ogg"), "time_plus": preload("res://sound/effects/time_plus.ogg"), "trend_action": preload("res://sound/effects/trend_action.ogg"), diff --git a/stage/stage.gd b/stage/stage.gd index aa0a2f0..7f969d0 100644 --- a/stage/stage.gd +++ b/stage/stage.gd @@ -94,6 +94,7 @@ var g_active_items: Dictionary = {} var pending_page: int = -1 var active_actions_dirty: bool = true var active_actions_elapsed: float = 0.0 +var interface_sounds_enabled: bool = false var owniverse:Owniverse = Owniverse.new() var owniverse_items: Dictionary[String, OwniverseItem] = {} @@ -323,6 +324,11 @@ func _ready() -> void: for pa in page_actions: page_actions[pa].action_pressed.connect(_page_action_pressed) page_actions[pa].page_id = pa + _enable_interface_sounds.call_deferred() + + +func _enable_interface_sounds() -> void: + interface_sounds_enabled = true ## Starts a fresh universe. Can be connected directly to a button's pressed signal. @@ -398,10 +404,15 @@ func _process(delta: float) -> void: func _change_speed(delta: int) -> void: - + var previous_speed: int = owniverse.speed owniverse.speed = owniverse.speed + delta var speed: int = owniverse.speed + if speed > previous_speed: + effects_player.play_effect("time_plus") + elif speed < previous_speed: + effects_player.play_effect("time_minus") + var ind: int = 1 for icon in g_speed_icons: if ind <= speed: @@ -421,6 +432,17 @@ func _change_speed(delta: int) -> void: func _on_topmenu_button_pressed(screen_name: String) -> void: + var current_screen: String = "" + for screen_id in screens: + if screens[screen_id].visible: + current_screen = screen_id + break + + if current_screen == screen_name: + return + + if interface_sounds_enabled: + effects_player.play_effect("window_close" if screen_name == "World" else "window_open") for screen_id in screens: #print(screen_name, " ←→ ", screen_id) diff --git a/tests/test_effects_player.gd b/tests/test_effects_player.gd index 82be2e4..94b1955 100644 --- a/tests/test_effects_player.gd +++ b/tests/test_effects_player.gd @@ -25,17 +25,14 @@ func run() -> void: check(not player.play_effect("select"), "Playing effect is not restarted") check(player.stream == selected_stream and player.playing, "Rejected duplicate keeps current playback") player.stop() - check(not player.play_effect("select"), "Named effect is rejected during its two-second cooldown") - check(player.play_effect("blast"), "Different effect starts without waiting for another name") - player.stop() - player._last_played_msec.select -= player.EFFECT_COOLDOWN_MSEC - check(player.play_effect("select"), "Named effect starts after its cooldown") + check(player.play_effect("select"), "Stopped effect can start again immediately") + check(player.play_effect("blast"), "Different effect starts immediately") check(not player.play_effect("missing_effect"), "Unknown effect is rejected") - check(player.stream == player._effects.select, "Unknown effect does not interrupt playback") + check(player.stream == player._effects.blast, "Unknown effect does not interrupt playback") player.set_enabled(false) check(player.stream_paused, "Disabling effects pauses current playback") - check(not player.play_effect("blast") and player.stream == player._effects.select, "Disabled player rejects new effects") + check(not player.play_effect("select") and player.stream == player._effects.blast, "Disabled player rejects new effects") player.set_enabled(true) check(not player.stream_paused, "Enabling effects resumes current playback") diff --git a/tests/test_stellar_rules.gd b/tests/test_stellar_rules.gd index 973227b..03b2bfb 100644 --- a/tests/test_stellar_rules.gd +++ b/tests/test_stellar_rules.gd @@ -176,8 +176,19 @@ func run() -> void: world.state.amount.Met = 100 world.state.auto_resources.H = 100 world.evolute(E.of("0.1")) - check((E.eq(world.state.amount.RD, 101) and E.eq(world.state.amount.H, 0)), "Automatic formation uses its resource budget") - check(E.ge(world.state.binds.RD.HP, 1), "Automatic formation also creates planets") + check((E.eq(world.state.amount.RD, 1) and E.eq(world.state.amount.H, 100)), "Automatic formation cannot spend hydrogen retained from an earlier cycle") + check(E.eq(world.state.auto_resources.get("H", 0), 0), "Unused automatic hydrogen becomes an ordinary free resource") + dispose(world) + + world = fixture() + world.catalog.entities.N.auto = 1 + world.state.amount.RD = 1 + world.state.amount.H = 100 + world.state.amount.Met = 100 + world.evolute(100) + check(E.gt(world.state.amount.RD, 1), "Automatic formation can spend newly produced hydrogen") + check(E.ge(world.state.amount.H, 100), "Automatic formation preserves hydrogen available before the cycle") + check(E.ge(world.state.binds.get("RD", {}).get("HP", 0), 1), "Automatic formation also creates planets") dispose(world) world = fixture() diff --git a/tests/test_ui_updates.gd b/tests/test_ui_updates.gd index a0191cd..17279c1 100644 --- a/tests/test_ui_updates.gd +++ b/tests/test_ui_updates.gd @@ -21,6 +21,7 @@ func run() -> void: root.add_child(scene) await process_frame await process_frame + check(stage.effects_player.stream == null, "Startup does not play a UI sound") stage.stage_page_scroller.scroll_horizontal = stage.PAGE_WIDTH await process_frame stage.owniverse.state.amount.H = 1000 @@ -54,6 +55,16 @@ func run() -> void: stage.owniverse.state.time.year = E.of("1234.5") stage._refresh_universe_ui() check(E.eq(stage.date_time.text, "1 234.5"), "Time displays tenths with thousands separator") + stage.effects_player.set_enabled(true) + stage.owniverse.speed = 1 + stage._change_speed(1) + check(stage.effects_player.stream == stage.effects_player._effects.time_plus, "Increasing speed plays its effect") + stage._change_speed(-1) + check(stage.effects_player.stream == stage.effects_player._effects.time_minus, "Decreasing speed plays its effect") + stage._on_topmenu_button_pressed("MainMenu") + check(stage.effects_player.stream == stage.effects_player._effects.window_open, "Opening a window plays its effect") + stage._on_topmenu_button_pressed("World") + check(stage.effects_player.stream == stage.effects_player._effects.window_close, "Closing a window plays its effect") for entity in stage.owniverse.catalog.entities.values(): entity.free() stage.owniverse.free() scene.free() diff --git a/ui/audio/owniverse_effects_player.gd b/ui/audio/owniverse_effects_player.gd index d42c2bd..1c4df36 100644 --- a/ui/audio/owniverse_effects_player.gd +++ b/ui/audio/owniverse_effects_player.gd @@ -1,11 +1,8 @@ class_name OwniverseEffectsPlayer extends AudioStreamPlayer -const EFFECT_COOLDOWN_MSEC := 2000 - var enabled: bool = true var _effects: Dictionary = OwniverseAudioLoader.get_effects() -var _last_played_msec: Dictionary = {} func set_enabled(value: bool) -> void: @@ -21,11 +18,7 @@ func play_effect(effect_id: String) -> bool: return false if playing and stream == _effects[effect_id]: return false - var now: int = Time.get_ticks_msec() - if now - int(_last_played_msec.get(effect_id, -EFFECT_COOLDOWN_MSEC)) < EFFECT_COOLDOWN_MSEC: - return false stream = _effects[effect_id] play() - _last_played_msec[effect_id] = now return true diff --git a/ui/audio/owniverse_music_player.gd b/ui/audio/owniverse_music_player.gd index 0896b03..fdd4103 100644 --- a/ui/audio/owniverse_music_player.gd +++ b/ui/audio/owniverse_music_player.gd @@ -2,8 +2,8 @@ class_name OwniverseMusicPlayer extends AudioStreamPlayer const EXCLUDED_TRACK := "apocalypse_the_end" -const MIN_PAUSE_SECONDS := 120.0 -const MAX_PAUSE_SECONDS := 300.0 +const MIN_PAUSE_SECONDS := 2 * 60.0 +const MAX_PAUSE_SECONDS := 5 * 60.0 var enabled: bool = true var last_track: String = "" diff --git a/world/owniverse/automatic_production.gd b/world/owniverse/automatic_production.gd index a19f488..7264a6c 100644 --- a/world/owniverse/automatic_production.gd +++ b/world/owniverse/automatic_production.gd @@ -58,8 +58,10 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di amount = world.ledger.credit(target, id, amount) world.batches.append_batch(target, id, amount, from_year, to_year) - # Keep unused automatic resources across cycles; they are part of free amounts. - var budget: Dictionary = previous.get("auto_resources", {}).duplicate(true) + # Automation may spend only the automatic output produced during this cycle. + # Any unused part becomes an ordinary free resource on the next cycle instead + # of remaining eligible for later automatic production. + var budget: Dictionary = {} for id in automatic: budget[id] = E.add(budget.get(id, 0), automatic[id]) for id in budget: