commit
012ad62491
@ -1,10 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="GdSdk Master" type="GdScript">
|
||||
<properties path="$USER_HOME$/Library/Caches/JetBrains/Rider2024.3/projects/stem-city.46f8129/sdk/GdSdk Master" version="Master" date="2024-06-01T15:14:16.000+02:00" />
|
||||
<properties path="$USER_HOME$/.cache/JetBrains/Rider2024.3/projects/stem-city.408706df/sdk/GdSdk Master" version="Master" date="2024-06-01T15:14:16.000+02:00" />
|
||||
<CLASSES />
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="file://$USER_HOME$/Library/Caches/JetBrains/Rider2024.3/projects/stem-city.46f8129/sdk/GdSdk Master" />
|
||||
<root url="file://$USER_HOME$/.cache/JetBrains/Rider2024.3/projects/stem-city.408706df/sdk/GdSdk Master" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Binary file not shown.
@ -0,0 +1,81 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://ib2t48fgsw62"]
|
||||
|
||||
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="1_ncwjb"]
|
||||
[ext_resource type="Script" path="res://scripts/attribution_screen.gd" id="1_qwu4y"]
|
||||
|
||||
[node name="AttributionScreen" type="CanvasLayer"]
|
||||
script = ExtResource("1_qwu4y")
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.145098, 0.172549, 0.231373, 1)
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 100
|
||||
theme_override_constants/margin_top = 100
|
||||
theme_override_constants/margin_right = 100
|
||||
theme_override_constants/margin_bottom = 100
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 30
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("1_ncwjb")
|
||||
theme_override_font_sizes/font_size = 48
|
||||
text = "Attributions"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="AttributionsContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="PowerPlantLabel" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "3D Models - \"Power Plant\" (https://skfb.ly/6vZoR) by Romain PERRONE is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/)"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="Kenny" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "3D Models - Provided by Kenny.nl (Thanks Kenny)"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="SoundLabel1" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "SoundFX - building placing.wav by strange_dragoon -- https://freesound.org/s/271141/ -- License: Attribution 3.0"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="SoundLabel2" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "SoundFX - building construct p2.wav by strange_dragoon -- https://freesound.org/s/271135/ -- License: Attribution 3.0"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="SongLabel" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "Music - Provided by PaoloArgento - The Best Jazz Club In New Orleans"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="SoundLabel3" type="Label" parent="MarginContainer/VBoxContainer/AttributionsContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "building construct p2.wav by strange_dragoon -- https://freesound.org/s/271135/ -- License: Attribution 3.0"
|
||||
autowrap_mode = 3
|
||||
@ -0,0 +1,162 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://b4s46k58ddpyc"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/sound_panel.gd" id="1_c6ykp"]
|
||||
[ext_resource type="FontFile" uid="uid://d0cxd77jybrcn" path="res://fonts/lilita_one_regular.ttf" id="2_kpgjp"]
|
||||
[ext_resource type="Texture2D" uid="uid://bwp2j1v3vnqbf" path="res://sprites/unmuted.png" id="3_c3hj5"]
|
||||
[ext_resource type="Texture2D" uid="uid://dkajgv48qw6hv" path="res://sprites/muted.png" id="4_jdlh2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u5w8t"]
|
||||
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0.356863, 0.670588, 0.768627, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
shadow_size = 5
|
||||
shadow_offset = Vector2(2, 2)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_wg3ha"]
|
||||
bg_color = Color(0.356863, 0.670588, 0.768627, 1)
|
||||
corner_radius_top_left = 4
|
||||
corner_radius_top_right = 4
|
||||
corner_radius_bottom_right = 4
|
||||
corner_radius_bottom_left = 4
|
||||
|
||||
[node name="SoundPanel" type="PanelContainer"]
|
||||
process_mode = 3 # Process even when the game is paused
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -200.0
|
||||
offset_top = -175.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 175.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_u5w8t")
|
||||
script = ExtResource("1_c6ykp")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 25
|
||||
theme_override_constants/margin_top = 25
|
||||
theme_override_constants/margin_right = 25
|
||||
theme_override_constants/margin_bottom = 25
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("2_kpgjp")
|
||||
theme_override_font_sizes/font_size = 32
|
||||
text = "Sound Settings"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_styles/separator = SubResource("StyleBoxFlat_wg3ha")
|
||||
|
||||
[node name="MusicSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="MusicTitle" type="Label" parent="MarginContainer/VBoxContainer/MusicSection"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "Music Volume"
|
||||
|
||||
[node name="MusicControls" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MusicSection"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="MusicMuteButton" type="TextureButton" parent="MarginContainer/VBoxContainer/MusicSection/MusicControls"]
|
||||
custom_minimum_size = Vector2(36, 36)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
toggle_mode = true
|
||||
texture_normal = ExtResource("3_c3hj5")
|
||||
texture_pressed = ExtResource("4_jdlh2")
|
||||
ignore_texture_size = true
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="MusicSlider" type="HSlider" parent="MarginContainer/VBoxContainer/MusicSection/MusicControls"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
max_value = 1.0
|
||||
step = 0.01
|
||||
value = 0.8
|
||||
|
||||
[node name="MusicValueLabel" type="Label" parent="MarginContainer/VBoxContainer/MusicSection/MusicControls"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "80%"
|
||||
|
||||
[node name="HSeparator2" type="HSeparator" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="SFXSection" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="SFXTitle" type="Label" parent="MarginContainer/VBoxContainer/SFXSection"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "Sound Effects Volume"
|
||||
|
||||
[node name="SFXControls" type="HBoxContainer" parent="MarginContainer/VBoxContainer/SFXSection"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="SFXMuteButton" type="TextureButton" parent="MarginContainer/VBoxContainer/SFXSection/SFXControls"]
|
||||
custom_minimum_size = Vector2(36, 36)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
toggle_mode = true
|
||||
texture_normal = ExtResource("3_c3hj5")
|
||||
texture_pressed = ExtResource("4_jdlh2")
|
||||
ignore_texture_size = true
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="SFXSlider" type="HSlider" parent="MarginContainer/VBoxContainer/SFXSection/SFXControls"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
max_value = 1.0
|
||||
step = 0.01
|
||||
value = 0.8
|
||||
|
||||
[node name="SFXValueLabel" type="Label" parent="MarginContainer/VBoxContainer/SFXSection/SFXControls"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "80%"
|
||||
|
||||
[node name="HSeparator3" type="HSeparator" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="CloseButtonContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="CloseButton" type="Button" parent="MarginContainer/VBoxContainer/CloseButtonContainer"]
|
||||
custom_minimum_size = Vector2(150, 50)
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("2_kpgjp")
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "Close"
|
||||
|
||||
[connection signal="toggled" from="MarginContainer/VBoxContainer/MusicSection/MusicControls/MusicMuteButton" to="." method="_on_music_mute_button_toggled"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/MusicSection/MusicControls/MusicSlider" to="." method="_on_music_slider_value_changed"]
|
||||
[connection signal="toggled" from="MarginContainer/VBoxContainer/SFXSection/SFXControls/SFXMuteButton" to="." method="_on_sfx_mute_button_toggled"]
|
||||
[connection signal="value_changed" from="MarginContainer/VBoxContainer/SFXSection/SFXControls/SFXSlider" to="." method="_on_sfx_slider_value_changed"]
|
||||
[connection signal="pressed" from="MarginContainer/VBoxContainer/CloseButtonContainer/CloseButton" to="." method="_on_close_button_pressed"]
|
||||
@ -0,0 +1,23 @@
|
||||
extends CanvasLayer
|
||||
|
||||
# Time in seconds to display the attribution screen
|
||||
const DISPLAY_TIME: float = 3.0
|
||||
const MAIN_SCENE_PATH: String = "res://scenes/main.tscn"
|
||||
|
||||
func _ready():
|
||||
# Set up the timer to automatically transition to the main scene
|
||||
var timer = Timer.new()
|
||||
add_child(timer)
|
||||
timer.wait_time = DISPLAY_TIME
|
||||
timer.one_shot = true
|
||||
timer.timeout.connect(_on_timer_timeout)
|
||||
timer.start()
|
||||
|
||||
func _on_timer_timeout():
|
||||
# Fade out the attribution screen
|
||||
var tween = create_tween()
|
||||
tween.tween_property(self, "modulate", Color(1, 1, 1, 0), 0.5)
|
||||
tween.tween_callback(change_scene)
|
||||
|
||||
func change_scene():
|
||||
get_tree().change_scene_to_file(MAIN_SCENE_PATH)
|
||||
@ -0,0 +1,118 @@
|
||||
extends Node
|
||||
|
||||
# This script provides an accurate counter for mission 3's residential buildings
|
||||
# Instead of polling, it listens for signals when buildings are constructed or demolished
|
||||
|
||||
func _ready():
|
||||
# Wait a moment for the game to initialize
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
|
||||
# Get builder reference
|
||||
var builder = get_node_or_null("/root/Main/Builder")
|
||||
if builder:
|
||||
# Connect to structure placed and removed signals
|
||||
builder.structure_placed.connect(_on_structure_placed)
|
||||
builder.structure_removed.connect(_on_structure_removed)
|
||||
builder.construction_manager.construction_completed.connect(_on_construction_completed)
|
||||
|
||||
# Do initial count on mission start
|
||||
update_mission_3_count()
|
||||
|
||||
# Called when a structure is placed
|
||||
func _on_structure_placed(structure_index, position):
|
||||
# We don't need immediate action - residential counts are updated after construction
|
||||
pass
|
||||
|
||||
# Called when a structure is removed
|
||||
func _on_structure_removed(structure_index, position):
|
||||
var builder = get_node_or_null("/root/Main/Builder")
|
||||
if builder and structure_index >= 0 and structure_index < builder.structures.size():
|
||||
if builder.structures[structure_index].type == 1: # Residential building
|
||||
# Wait one frame to make sure the GridMap is updated
|
||||
await get_tree().process_frame
|
||||
# Update the count
|
||||
update_mission_3_count()
|
||||
|
||||
# Called when construction is completed
|
||||
func _on_construction_completed(position):
|
||||
update_mission_3_count()
|
||||
|
||||
# Updates the mission 3 objective count based on actual residential buildings
|
||||
func update_mission_3_count():
|
||||
# Find the mission manager
|
||||
var mission_manager = get_node_or_null("/root/Main/MissionManager")
|
||||
if not mission_manager:
|
||||
return
|
||||
|
||||
# Check if we're in mission 3
|
||||
if mission_manager.current_mission and mission_manager.current_mission.id == "3":
|
||||
# Count the actual number of residential buildings
|
||||
var count = count_residential_buildings()
|
||||
|
||||
# Get the current objective count
|
||||
var current_count = 0
|
||||
for objective in mission_manager.current_mission.objectives:
|
||||
if objective.type == 3: # BUILD_RESIDENTIAL type
|
||||
current_count = objective.current_count
|
||||
break
|
||||
|
||||
# Only update if the counts don't match
|
||||
if current_count != count:
|
||||
# Reset the objective count to match the actual number
|
||||
mission_manager.reset_objective_count(3, count) # 3 is the BUILD_RESIDENTIAL type
|
||||
|
||||
func count_residential_buildings():
|
||||
# Find the builder
|
||||
var builder = get_node_or_null("/root/Main/Builder")
|
||||
if not builder:
|
||||
return 0
|
||||
|
||||
# Find the gridmap
|
||||
var gridmap = builder.gridmap
|
||||
if not gridmap:
|
||||
return 0
|
||||
|
||||
# Count residential buildings in the gridmap
|
||||
var residential_count = 0
|
||||
var found_positions = []
|
||||
|
||||
# First count buildings in the gridmap
|
||||
for cell in gridmap.get_used_cells():
|
||||
var structure_index = gridmap.get_cell_item(cell)
|
||||
if structure_index >= 0 and structure_index < builder.structures.size():
|
||||
if builder.structures[structure_index].type == 1: # 1 is RESIDENTIAL_BUILDING type
|
||||
residential_count += 1
|
||||
found_positions.append(Vector2(cell.x, cell.z))
|
||||
|
||||
# Also count completed buildings that might not be in the gridmap
|
||||
if builder.has_node("NavRegion3D"):
|
||||
var nav_region = builder.get_node("NavRegion3D")
|
||||
|
||||
for child in nav_region.get_children():
|
||||
if child.name.begins_with("Building_"):
|
||||
var parts = child.name.split("_")
|
||||
if parts.size() >= 3:
|
||||
var x = int(parts[1])
|
||||
var z = int(parts[2])
|
||||
var pos = Vector2(x, z)
|
||||
|
||||
# Only count if we haven't already counted this position
|
||||
if not pos in found_positions:
|
||||
residential_count += 1
|
||||
found_positions.append(pos)
|
||||
|
||||
# Also count any buildings under construction
|
||||
if builder.construction_manager:
|
||||
for position in builder.construction_manager.construction_sites:
|
||||
var site = builder.construction_manager.construction_sites[position]
|
||||
if site.structure_index >= 0 and site.structure_index < builder.structures.size():
|
||||
if builder.structures[site.structure_index].type == 1 and site.completed: # Only count completed residential buildings
|
||||
# Check if there's actually a building at this position in the GridMap
|
||||
var cell_item = builder.gridmap.get_cell_item(position)
|
||||
if cell_item >= 0: # Only count if there's still a building in the GridMap
|
||||
var pos = Vector2(position.x, position.z)
|
||||
if not pos in found_positions:
|
||||
residential_count += 1
|
||||
found_positions.append(pos)
|
||||
|
||||
return residential_count
|
||||
@ -0,0 +1,506 @@
|
||||
extends Node
|
||||
class_name JSBridge
|
||||
|
||||
# This script provides a bridge to JavaScript functionality
|
||||
# while gracefully handling platforms that don't support it
|
||||
|
||||
# Check if JavaScript is available
|
||||
static func has_interface() -> bool:
|
||||
# Check if running in a web environment
|
||||
# Use OS.has_feature("web") for consistency with sound_manager.gd
|
||||
if OS.has_feature("web"):
|
||||
print("Running in web environment, JavaScript should be available")
|
||||
|
||||
# Double-check by evaluating a simple script
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
var test_result = js.eval("!!window && typeof window !== 'undefined'")
|
||||
print("JavaScript test result: " + str(test_result))
|
||||
return test_result != null
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available, running in editor or non-web platform")
|
||||
else:
|
||||
print("Not running in web environment")
|
||||
|
||||
return false
|
||||
|
||||
# Get the JavaScript interface
|
||||
static func get_interface():
|
||||
if has_interface():
|
||||
return JavaScriptGlobal
|
||||
return null
|
||||
|
||||
# JavaScriptGlobal is a mock class that provides fallback implementations
|
||||
# for platforms that don't support JavaScript
|
||||
class JavaScriptGlobal:
|
||||
# Check if a JavaScript function exists
|
||||
static func has_function(function_name: String) -> bool:
|
||||
if not OS.has_feature("web"):
|
||||
return false
|
||||
|
||||
print("Checking if function exists: " + function_name)
|
||||
var script = "typeof %s === 'function'" % function_name
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
var result = js.eval(script)
|
||||
|
||||
# If result is null, the JavaScript eval failed
|
||||
if result == null:
|
||||
print("JavaScript eval failed when checking for function: " + function_name)
|
||||
return false
|
||||
|
||||
print("Function check result for " + function_name + ": " + str(result))
|
||||
return result
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
return false
|
||||
|
||||
# Evaluate JavaScript code
|
||||
static func eval(script: String):
|
||||
if not OS.has_feature("web"):
|
||||
return null
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
return js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
return null
|
||||
|
||||
# Call a JavaScript function with arguments
|
||||
static func call_js_function(function_name: String, args = []):
|
||||
if not OS.has_feature("web"):
|
||||
return null
|
||||
|
||||
var formatted_args = []
|
||||
for arg in args:
|
||||
if arg is String:
|
||||
formatted_args.append("\"%s\"" % arg.replace("\"", "\\\""))
|
||||
elif arg is Dictionary or arg is Array:
|
||||
formatted_args.append(JSON.stringify(arg))
|
||||
else:
|
||||
formatted_args.append(str(arg))
|
||||
|
||||
var script = "%s(%s)" % [function_name, ",".join(formatted_args)]
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
return js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
return null
|
||||
|
||||
# Connect to the learning companion - legacy method with postMessage fallback
|
||||
static func connectLearningCompanion(success_callback = null, error_callback = null):
|
||||
print("Attempting to connect to learning companion")
|
||||
|
||||
if not OS.has_feature("web"):
|
||||
print("Skipping learning companion connection on non-web platform")
|
||||
if error_callback != null and error_callback.is_valid():
|
||||
error_callback.call()
|
||||
return
|
||||
|
||||
# Always use postMessage approach regardless of function availability
|
||||
connectLearningCompanionViaPostMessage(success_callback, error_callback)
|
||||
|
||||
# Connect to the learning companion using only postMessage
|
||||
static func connectLearningCompanionViaPostMessage(success_callback = null, error_callback = null):
|
||||
print("Connecting to learning companion via postMessage")
|
||||
|
||||
if not OS.has_feature("web"):
|
||||
print("Skipping learning companion connection on non-web platform")
|
||||
if error_callback != null and error_callback.is_valid():
|
||||
error_callback.call()
|
||||
return
|
||||
|
||||
# Use postMessage approach exclusively - note: no return statements allowed in the script
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
// Send a message directly to the parent window
|
||||
if (window.parent) {
|
||||
console.log('Sending connection message to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_connect',
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
|
||||
// Set up a global event listener for responses if not already set up
|
||||
if (!window._stemCityListenerInitialized) {
|
||||
window._stemCityListenerInitialized = true;
|
||||
window.addEventListener('message', function(event) {
|
||||
console.log('Game received message:', event.data);
|
||||
if (event.data && event.data.type === 'stemCity_connect_ack') {
|
||||
console.log('Received connection acknowledgment from parent');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Don't use return statements here - they're not allowed in top-level eval
|
||||
var result = true;
|
||||
} else {
|
||||
console.log('No parent window found');
|
||||
var result = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error connecting via postMessage:', e);
|
||||
var result = false;
|
||||
}
|
||||
})();
|
||||
"""
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
js.eval(script)
|
||||
|
||||
# Always consider this a success - we'll use the force connection timer as backup
|
||||
print("Sent connection message via postMessage")
|
||||
|
||||
# Try to ensure audio is initialized as well since we now have user interaction
|
||||
JavaScriptGlobal.ensure_audio_initialized()
|
||||
|
||||
if success_callback != null and success_callback.is_valid():
|
||||
success_callback.call()
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
if error_callback != null and error_callback.is_valid():
|
||||
error_callback.call()
|
||||
|
||||
# The following methods call the JavaScript functions for game events using postMessage
|
||||
|
||||
static func onGameStarted():
|
||||
if not OS.has_feature("web"):
|
||||
return
|
||||
|
||||
print("Sending game started event via postMessage")
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending gameStarted message to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_gameStarted',
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
} else {
|
||||
console.log('No parent window found for gameStarted event');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending gameStarted via postMessage:', e);
|
||||
}
|
||||
})();
|
||||
"""
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
|
||||
static func onMissionStarted(mission_data: Dictionary):
|
||||
if not OS.has_feature("web"):
|
||||
return
|
||||
|
||||
print("Sending mission started event for mission: " + str(mission_data.get("id", "unknown")))
|
||||
var mission_json = JSON.stringify(mission_data)
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending missionStarted message to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_missionStarted',
|
||||
data: %s,
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
} else {
|
||||
console.log('No parent window found for missionStarted event');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending missionStarted via postMessage:', e);
|
||||
}
|
||||
})();
|
||||
""" % mission_json
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
|
||||
static func onMissionCompleted(mission_data: Dictionary):
|
||||
if not OS.has_feature("web"):
|
||||
return
|
||||
|
||||
print("Sending mission completed event for mission: " + str(mission_data.get("id", "unknown")))
|
||||
var mission_json = JSON.stringify(mission_data)
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending missionCompleted message to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_missionCompleted',
|
||||
data: %s,
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
} else {
|
||||
console.log('No parent window found for missionCompleted event');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending missionCompleted via postMessage:', e);
|
||||
}
|
||||
})();
|
||||
""" % mission_json
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
|
||||
static func onAllMissionsCompleted():
|
||||
if not OS.has_feature("web"):
|
||||
return
|
||||
|
||||
print("Sending all missions completed event")
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending allMissionsCompleted message to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_allMissionsCompleted',
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
} else {
|
||||
console.log('No parent window found for allMissionsCompleted event');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending allMissionsCompleted via postMessage:', e);
|
||||
}
|
||||
})();
|
||||
"""
|
||||
|
||||
# Use Engine.get_singleton for consistency with sound_manager.gd
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
|
||||
# Handle audio actions via JavaScript
|
||||
static func handle_audio_action(action: String, sound_name: String = "", volume: float = -1.0):
|
||||
if not OS.has_feature("web"):
|
||||
return false
|
||||
|
||||
print("Handling audio action via JavaScript bridge: " + action)
|
||||
|
||||
var action_data = {
|
||||
"action": action,
|
||||
"sound": sound_name,
|
||||
}
|
||||
|
||||
if volume >= 0.0:
|
||||
action_data["volume"] = volume
|
||||
|
||||
var action_json = JSON.stringify(action_data)
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending audio action to parent window:', %s);
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_audio',
|
||||
data: %s,
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
return true;
|
||||
} else {
|
||||
console.log('No parent window found for audio action');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending audio action via postMessage:', e);
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
""" % [action_json, action_json]
|
||||
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
return js.eval(script)
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available")
|
||||
return false
|
||||
|
||||
# Helper method to ensure the sound manager's audio is initialized
|
||||
# Call this method after user interaction to ensure audio works
|
||||
static func ensure_audio_initialized():
|
||||
if not OS.has_feature("web"):
|
||||
return true # Audio always works on non-web platforms
|
||||
|
||||
print("Ensuring audio is initialized via JavaScript bridge")
|
||||
|
||||
# Setup audio message listener if it's not already set up
|
||||
setup_audio_message_listener()
|
||||
|
||||
# Try to initialize audio through the sound manager if it exists
|
||||
var sound_manager = _get_sound_manager()
|
||||
|
||||
if sound_manager and sound_manager.has_method("init_web_audio_from_js"):
|
||||
print("Found SoundManager, calling init_web_audio_from_js")
|
||||
sound_manager.init_web_audio_from_js()
|
||||
|
||||
# Follow up with direct JavaScript audio context unlocking for extra reliability
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
_run_audio_unlock_script(js)
|
||||
|
||||
return true
|
||||
else:
|
||||
# Fallback: directly try to unlock web audio using JavaScript
|
||||
if Engine.has_singleton("JavaScriptBridge"):
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
var result = _run_audio_unlock_script(js)
|
||||
return result
|
||||
else:
|
||||
print("JavaScriptBridge singleton not available for audio initialization")
|
||||
return false
|
||||
|
||||
# Helper method to run the audio unlocking script with maximum compatibility
|
||||
static func _run_audio_unlock_script(js_interface):
|
||||
var script = """
|
||||
(function() {
|
||||
var result = false;
|
||||
try {
|
||||
// Simple approach to unlock audio
|
||||
console.log('Running simplified audio unlock');
|
||||
|
||||
// Create audio context if needed
|
||||
if (!window._godotAudioContext) {
|
||||
window._godotAudioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
}
|
||||
|
||||
var audioCtx = window._godotAudioContext;
|
||||
console.log('Audio context state:', audioCtx.state);
|
||||
|
||||
// Resume it (for Chrome/Safari)
|
||||
if (audioCtx.state === 'suspended') {
|
||||
audioCtx.resume();
|
||||
}
|
||||
|
||||
// Play a short, quiet beep
|
||||
var oscillator = audioCtx.createOscillator();
|
||||
var gainNode = audioCtx.createGain();
|
||||
gainNode.gain.value = 0.01; // Very quiet
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioCtx.destination);
|
||||
oscillator.start(0);
|
||||
oscillator.stop(0.1);
|
||||
|
||||
// Add event listeners for future interactions
|
||||
['click', 'touchstart', 'touchend'].forEach(function(event) {
|
||||
document.addEventListener(event, function() {
|
||||
if (audioCtx.state === 'suspended') {
|
||||
audioCtx.resume();
|
||||
}
|
||||
}, {once: false});
|
||||
});
|
||||
|
||||
result = audioCtx.state === 'running';
|
||||
} catch (e) {
|
||||
console.error("JavaScript bridge: Audio unlock error:", e);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
})()
|
||||
"""
|
||||
|
||||
var result = js_interface.eval(script)
|
||||
print("JavaScript audio initialization result:", result)
|
||||
return result
|
||||
|
||||
# Setup audio message listener from JavaScript
|
||||
static func setup_audio_message_listener():
|
||||
if not OS.has_feature("web"):
|
||||
return false
|
||||
|
||||
print("Setting up audio message listener via JavaScript bridge")
|
||||
|
||||
if not Engine.has_singleton("JavaScriptBridge"):
|
||||
print("JavaScriptBridge singleton not available")
|
||||
return false
|
||||
|
||||
var js = Engine.get_singleton("JavaScriptBridge")
|
||||
|
||||
# Register the callback function
|
||||
js.set_callback("godot_audio_callback", Callable(_get_sound_manager(), "process_js_audio_state"))
|
||||
|
||||
# Set up a listener for audio state messages
|
||||
var script = """
|
||||
(function() {
|
||||
// Set up message listener if not already done
|
||||
if (!window.godot_audio_listener_initialized) {
|
||||
window.addEventListener('message', function(event) {
|
||||
if (event.data && event.data.type === 'stemCity_audio_state') {
|
||||
console.log('Godot received audio state:', event.data);
|
||||
// Call our Godot callback with the state data
|
||||
if (typeof godot_audio_callback === 'function') {
|
||||
console.log('Sending audio state to Godot');
|
||||
godot_audio_callback(event.data.data);
|
||||
} else {
|
||||
console.warn('godot_audio_callback is not available');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Audio message listener initialized');
|
||||
window.godot_audio_listener_initialized = true;
|
||||
|
||||
// Request initial audio state from parent
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_audio',
|
||||
data: { action: 'GET_STATE' },
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
"""
|
||||
|
||||
var result = js.eval(script)
|
||||
print("Audio message listener setup result: ", result)
|
||||
return result
|
||||
|
||||
# Helper to get the sound manager instance
|
||||
static func _get_sound_manager():
|
||||
var sound_manager = null
|
||||
|
||||
# Try to find using meta
|
||||
if Engine.get_main_loop().has_meta("sound_manager"):
|
||||
sound_manager = Engine.get_main_loop().get_meta("sound_manager")
|
||||
else:
|
||||
# Try to find in scene tree
|
||||
var scene_tree = Engine.get_main_loop() as SceneTree
|
||||
if scene_tree:
|
||||
sound_manager = scene_tree.root.get_node_or_null("/root/SoundManager")
|
||||
|
||||
return sound_manager
|
||||
@ -0,0 +1,504 @@
|
||||
extends Node
|
||||
|
||||
signal music_volume_changed(new_volume)
|
||||
signal sfx_volume_changed(new_volume)
|
||||
signal music_muted_changed(is_muted)
|
||||
signal sfx_muted_changed(is_muted)
|
||||
signal audio_ready # Signal emitted when audio is initialized (important for web)
|
||||
|
||||
# Sound bridges for web builds
|
||||
var react_sound_bridge = null # Will be instantiated from a script
|
||||
var audio_bridge = null # Will be instantiated from a script
|
||||
|
||||
# Volume ranges from 0.0 to 1.0
|
||||
var music_volume: float = 0.8
|
||||
var sfx_volume: float = 0.8
|
||||
|
||||
# Mute states
|
||||
var music_muted: bool = false
|
||||
var sfx_muted: bool = false
|
||||
|
||||
# Bus indices for easier reference
|
||||
var music_bus_index: int
|
||||
var sfx_bus_index: int
|
||||
|
||||
# Default bus names
|
||||
const MUSIC_BUS_NAME = "Music"
|
||||
const SFX_BUS_NAME = "SFX"
|
||||
|
||||
var audio_initialized: bool = false
|
||||
|
||||
# Sound files dictionary - mapping simplified names to file paths
|
||||
const SOUND_FILES = {
|
||||
"jazzNewOrleans": "res://sounds/jazz_new_orleans.mp3",
|
||||
"lofiChillJazz": "res://sounds/lofi-chill-jazz-272869.mp3",
|
||||
"buildingPlacing": "res://sounds/building_placing.wav",
|
||||
"construction": "res://sounds/construction.wav",
|
||||
"powerDrill": "res://sounds/power_drill.mp3"
|
||||
}
|
||||
|
||||
# Current music track
|
||||
var current_music: String = ""
|
||||
|
||||
# Currently playing audio streams (for direct Godot playback)
|
||||
var music_player: AudioStreamPlayer = null
|
||||
var sfx_players: Dictionary = {}
|
||||
|
||||
func _ready():
|
||||
# Create music player for all platforms
|
||||
music_player = AudioStreamPlayer.new()
|
||||
add_child(music_player)
|
||||
|
||||
# For web builds, we'll use Audio Bridge
|
||||
if OS.has_feature("web"):
|
||||
# Set a flag to track initialization
|
||||
audio_initialized = false
|
||||
|
||||
print("Web build detected, Audio bridges will be used")
|
||||
|
||||
# Connect to the input events to detect user interaction as fallback
|
||||
get_viewport().connect("gui_focus_changed", _on_user_interaction)
|
||||
|
||||
# Try to use a custom Node for audio bridge functionality
|
||||
# Instead of relying on class_name registration or preload
|
||||
audio_bridge = Node.new()
|
||||
audio_bridge.name = "AudioBridge"
|
||||
add_child(audio_bridge)
|
||||
|
||||
# Set up the necessary properties
|
||||
audio_bridge.set_script(load("res://scripts/audio_bridge.gd"))
|
||||
|
||||
# Connect to the signal after the script is loaded
|
||||
if audio_bridge.has_signal("bridge_connected"):
|
||||
audio_bridge.bridge_connected.connect(_on_audio_bridge_connected)
|
||||
|
||||
# We also create the ReactSoundBridge for backward compatibility
|
||||
# But using the same approach as AudioBridge to avoid class_name dependency
|
||||
react_sound_bridge = Node.new()
|
||||
react_sound_bridge.name = "ReactSoundBridge"
|
||||
add_child(react_sound_bridge)
|
||||
|
||||
# Set up the necessary properties
|
||||
react_sound_bridge.set_script(load("res://scripts/react_sound_bridge.gd"))
|
||||
|
||||
# Connect to the signal after the script is loaded
|
||||
if react_sound_bridge.has_signal("audio_ready"):
|
||||
react_sound_bridge.audio_ready.connect(_on_react_audio_ready)
|
||||
|
||||
# Signal that we're using web audio (don't create audio buses)
|
||||
await get_tree().process_frame
|
||||
audio_initialized = true
|
||||
audio_ready.emit()
|
||||
|
||||
# Store a reference to this object in the main loop for JavaScript callbacks
|
||||
Engine.get_main_loop().set_meta("sound_manager", self)
|
||||
else:
|
||||
# For non-web platforms, set up standard Godot audio
|
||||
# Set up the audio buses
|
||||
_setup_audio_buses()
|
||||
|
||||
# Set the music player bus
|
||||
music_player.bus = MUSIC_BUS_NAME
|
||||
|
||||
# For non-web platforms, we can initialize immediately
|
||||
audio_initialized = true
|
||||
|
||||
# Emit the audio_ready signal
|
||||
audio_ready.emit()
|
||||
|
||||
# Setup audio buses (doesn't start audio playback)
|
||||
func _setup_audio_buses():
|
||||
# Initialize audio bus indices
|
||||
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
|
||||
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
|
||||
|
||||
# If the buses don't exist yet, create them
|
||||
if music_bus_index == -1:
|
||||
# Create music bus
|
||||
music_bus_index = AudioServer.bus_count
|
||||
AudioServer.add_bus()
|
||||
AudioServer.set_bus_name(music_bus_index, MUSIC_BUS_NAME)
|
||||
AudioServer.set_bus_send(music_bus_index, "Master")
|
||||
|
||||
if sfx_bus_index == -1:
|
||||
# Create SFX bus
|
||||
sfx_bus_index = AudioServer.bus_count
|
||||
AudioServer.add_bus()
|
||||
AudioServer.set_bus_name(sfx_bus_index, SFX_BUS_NAME)
|
||||
AudioServer.set_bus_send(sfx_bus_index, "Master")
|
||||
|
||||
# Verify buses were created correctly
|
||||
music_bus_index = AudioServer.get_bus_index(MUSIC_BUS_NAME)
|
||||
sfx_bus_index = AudioServer.get_bus_index(SFX_BUS_NAME)
|
||||
|
||||
# Apply initial settings
|
||||
_apply_music_volume()
|
||||
_apply_sfx_volume()
|
||||
|
||||
# Make sure buses aren't muted by default
|
||||
if music_bus_index != -1:
|
||||
AudioServer.set_bus_mute(music_bus_index, false)
|
||||
|
||||
if sfx_bus_index != -1:
|
||||
AudioServer.set_bus_mute(sfx_bus_index, false)
|
||||
|
||||
# Process sound state received from JavaScript
|
||||
func process_js_audio_state(state: Dictionary):
|
||||
# Update local state based on received data
|
||||
if state.has("musicVolume"):
|
||||
music_volume = state.musicVolume
|
||||
if state.has("sfxVolume"):
|
||||
sfx_volume = state.sfxVolume
|
||||
if state.has("musicMuted"):
|
||||
music_muted = state.musicMuted
|
||||
if state.has("sfxMuted"):
|
||||
sfx_muted = state.sfxMuted
|
||||
if state.has("currentMusic"):
|
||||
current_music = state.currentMusic
|
||||
|
||||
# Emit signals about changes
|
||||
music_volume_changed.emit(music_volume)
|
||||
sfx_volume_changed.emit(sfx_volume)
|
||||
music_muted_changed.emit(music_muted)
|
||||
sfx_muted_changed.emit(sfx_muted)
|
||||
|
||||
# Called when ReactSoundBridge reports it's ready
|
||||
func _on_react_audio_ready():
|
||||
print("ReactSoundBridge reports ready")
|
||||
audio_initialized = true
|
||||
|
||||
# Update local state from React
|
||||
if react_sound_bridge != null:
|
||||
if react_sound_bridge.get("music_volume") != null:
|
||||
music_volume = react_sound_bridge.music_volume
|
||||
if react_sound_bridge.get("sfx_volume") != null:
|
||||
sfx_volume = react_sound_bridge.sfx_volume
|
||||
if react_sound_bridge.get("music_muted") != null:
|
||||
music_muted = react_sound_bridge.music_muted
|
||||
if react_sound_bridge.get("sfx_muted") != null:
|
||||
sfx_muted = react_sound_bridge.sfx_muted
|
||||
if react_sound_bridge.get("current_music") != null:
|
||||
current_music = react_sound_bridge.current_music
|
||||
|
||||
# Emit the audio ready signal
|
||||
audio_ready.emit()
|
||||
|
||||
# Called when AudioBridge connects to the platform-one sound manager
|
||||
func _on_audio_bridge_connected(is_connected: bool):
|
||||
print("AudioBridge connected: ", is_connected)
|
||||
|
||||
if is_connected:
|
||||
audio_initialized = true
|
||||
|
||||
# Request the sound state from the platform-one sound manager
|
||||
if audio_bridge.has_method("get_sound_state"):
|
||||
audio_bridge.get_sound_state()
|
||||
|
||||
# Emit the audio ready signal
|
||||
audio_ready.emit()
|
||||
|
||||
# Called when any user interaction happens in web builds
|
||||
func _on_user_interaction(_arg=null):
|
||||
if OS.has_feature("web") and not audio_initialized:
|
||||
_initialize_web_audio()
|
||||
|
||||
# Process input events directly
|
||||
func _input(event):
|
||||
if OS.has_feature("web") and not audio_initialized:
|
||||
if event is InputEventMouseButton or event is InputEventKey:
|
||||
if event.pressed:
|
||||
_initialize_web_audio()
|
||||
|
||||
# If this method is called from JavaScript, it will help the game to
|
||||
# initialize audio properly in web builds
|
||||
func init_web_audio_from_js():
|
||||
if OS.has_feature("web") and not audio_initialized:
|
||||
_initialize_web_audio()
|
||||
|
||||
# Initialize audio for web builds
|
||||
func _initialize_web_audio():
|
||||
if audio_initialized:
|
||||
return
|
||||
|
||||
# For web builds, we notify JavaScript to initialize audio
|
||||
if OS.has_feature("web"):
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("INITIALIZE_AUDIO")
|
||||
|
||||
# We don't need to create any dummy players, as JavaScript will handle the audio
|
||||
audio_initialized = true
|
||||
audio_ready.emit()
|
||||
return
|
||||
|
||||
# For non-web platforms, initialize Godot audio (this shouldn't get called)
|
||||
if not OS.has_feature("web"):
|
||||
# Set the flag to prevent multiple initializations
|
||||
audio_initialized = true
|
||||
audio_ready.emit()
|
||||
|
||||
# Play background music
|
||||
func play_music(sound_name: String, loop: bool = true):
|
||||
if not audio_initialized:
|
||||
return
|
||||
|
||||
# Store the current music name
|
||||
current_music = sound_name
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to play music: ", sound_name)
|
||||
if audio_bridge.has_method("play_music") and audio_bridge.play_music(sound_name):
|
||||
return
|
||||
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to play music: ", sound_name)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_MUSIC", sound_name)
|
||||
return
|
||||
|
||||
# For native builds, use Godot audio
|
||||
if not SOUND_FILES.has(sound_name):
|
||||
return
|
||||
|
||||
# Get the file path
|
||||
var file_path = SOUND_FILES[sound_name]
|
||||
|
||||
# Load the audio stream
|
||||
var stream = load(file_path)
|
||||
if stream == null:
|
||||
return
|
||||
|
||||
# Stop current music if playing
|
||||
if music_player.playing:
|
||||
music_player.stop()
|
||||
|
||||
# Set up and play the music
|
||||
music_player.stream = stream
|
||||
if music_muted:
|
||||
music_player.volume_db = linear_to_db(0)
|
||||
else:
|
||||
music_player.volume_db = linear_to_db(music_volume)
|
||||
music_player.bus = MUSIC_BUS_NAME
|
||||
|
||||
# Set looping if supported by the stream
|
||||
if stream is AudioStreamMP3 or stream is AudioStreamOggVorbis:
|
||||
stream.loop = loop
|
||||
|
||||
music_player.play()
|
||||
|
||||
# Play a sound effect
|
||||
func play_sfx(sound_name: String):
|
||||
if not audio_initialized:
|
||||
return
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to play sfx: ", sound_name)
|
||||
if audio_bridge.has_method("play_sfx") and audio_bridge.play_sfx(sound_name):
|
||||
return
|
||||
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to play sfx: ", sound_name)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("PLAY_SFX", sound_name)
|
||||
return
|
||||
|
||||
# For native builds, use Godot audio
|
||||
if not SOUND_FILES.has(sound_name):
|
||||
return
|
||||
|
||||
# Get the file path
|
||||
var file_path = SOUND_FILES[sound_name]
|
||||
|
||||
# Load the audio stream
|
||||
var stream = load(file_path)
|
||||
if stream == null:
|
||||
return
|
||||
|
||||
# Create or reuse a player for this sound
|
||||
var player: AudioStreamPlayer
|
||||
if not sfx_players.has(sound_name):
|
||||
player = AudioStreamPlayer.new()
|
||||
add_child(player)
|
||||
sfx_players[sound_name] = player
|
||||
else:
|
||||
player = sfx_players[sound_name]
|
||||
if player.playing:
|
||||
player.stop()
|
||||
|
||||
# Set up and play the sound
|
||||
player.stream = stream
|
||||
if sfx_muted:
|
||||
player.volume_db = linear_to_db(0)
|
||||
else:
|
||||
player.volume_db = linear_to_db(sfx_volume)
|
||||
player.bus = SFX_BUS_NAME
|
||||
player.play()
|
||||
|
||||
# Stop background music
|
||||
func stop_music():
|
||||
if not audio_initialized:
|
||||
return
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to stop music")
|
||||
if audio_bridge.has_method("stop_music") and audio_bridge.stop_music():
|
||||
current_music = ""
|
||||
return
|
||||
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to stop music")
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("STOP_MUSIC")
|
||||
current_music = ""
|
||||
return
|
||||
|
||||
# For native builds, use Godot audio
|
||||
if music_player and music_player.playing:
|
||||
music_player.stop()
|
||||
|
||||
current_music = ""
|
||||
|
||||
# Set music volume (0.0 to 1.0)
|
||||
func set_music_volume(volume: float):
|
||||
music_volume = clampf(volume, 0.0, 1.0)
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to set music volume: ", music_volume)
|
||||
if audio_bridge.has_method("set_music_volume"):
|
||||
audio_bridge.set_music_volume(music_volume)
|
||||
else:
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to set music volume: ", music_volume)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("SET_MUSIC_VOLUME", "", music_volume)
|
||||
else:
|
||||
# Apply to local Godot audio system
|
||||
_apply_music_volume()
|
||||
|
||||
# Emit signal
|
||||
music_volume_changed.emit(music_volume)
|
||||
|
||||
# Set SFX volume (0.0 to 1.0)
|
||||
func set_sfx_volume(volume: float):
|
||||
sfx_volume = clampf(volume, 0.0, 1.0)
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to set sfx volume: ", sfx_volume)
|
||||
if audio_bridge.has_method("set_sfx_volume"):
|
||||
audio_bridge.set_sfx_volume(sfx_volume)
|
||||
else:
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to set sfx volume: ", sfx_volume)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("SET_SFX_VOLUME", "", sfx_volume)
|
||||
else:
|
||||
# Apply to local Godot audio system
|
||||
_apply_sfx_volume()
|
||||
|
||||
# Emit signal
|
||||
sfx_volume_changed.emit(sfx_volume)
|
||||
|
||||
# Toggle music mute state
|
||||
func toggle_music_mute():
|
||||
music_muted = !music_muted
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to toggle music mute: ", music_muted)
|
||||
if audio_bridge.has_method("toggle_music_mute"):
|
||||
audio_bridge.toggle_music_mute()
|
||||
else:
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to toggle music mute: ", music_muted)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_MUSIC_MUTE")
|
||||
else:
|
||||
# Apply to local Godot audio system
|
||||
_apply_music_volume()
|
||||
|
||||
# Emit signal
|
||||
music_muted_changed.emit(music_muted)
|
||||
|
||||
# Toggle SFX mute state
|
||||
func toggle_sfx_mute():
|
||||
sfx_muted = !sfx_muted
|
||||
|
||||
# For web builds, try multiple bridge options
|
||||
if OS.has_feature("web"):
|
||||
# Try AudioBridge first (platform-one integration)
|
||||
if audio_bridge != null and audio_bridge.get("is_connected") == true:
|
||||
print("Using AudioBridge to toggle sfx mute: ", sfx_muted)
|
||||
if audio_bridge.has_method("toggle_sfx_mute"):
|
||||
audio_bridge.toggle_sfx_mute()
|
||||
else:
|
||||
# Fall back to JavaScript Bridge
|
||||
print("Using JavaScriptBridge to toggle sfx mute: ", sfx_muted)
|
||||
JSBridge.JavaScriptGlobal.handle_audio_action("TOGGLE_SFX_MUTE")
|
||||
else:
|
||||
# Apply to local Godot audio system
|
||||
_apply_sfx_volume()
|
||||
|
||||
# Emit signal
|
||||
sfx_muted_changed.emit(sfx_muted)
|
||||
|
||||
# Apply music volume settings
|
||||
func _apply_music_volume():
|
||||
# Skip for web builds - JavaScript Bridge handles volume
|
||||
if OS.has_feature("web"):
|
||||
return
|
||||
|
||||
# For non-web builds, use the audio buses
|
||||
if music_bus_index != -1:
|
||||
if music_muted:
|
||||
AudioServer.set_bus_mute(music_bus_index, true)
|
||||
else:
|
||||
AudioServer.set_bus_mute(music_bus_index, false)
|
||||
# Convert from linear to decibels (approximately -80dB to 0dB)
|
||||
var db_value = linear_to_db(music_volume)
|
||||
AudioServer.set_bus_volume_db(music_bus_index, db_value)
|
||||
|
||||
# Update music player volume if it exists
|
||||
if music_player != null:
|
||||
if music_muted:
|
||||
music_player.volume_db = linear_to_db(0)
|
||||
else:
|
||||
music_player.volume_db = linear_to_db(music_volume)
|
||||
|
||||
# Apply SFX volume settings
|
||||
func _apply_sfx_volume():
|
||||
# Skip for web builds - JavaScript Bridge handles volume
|
||||
if OS.has_feature("web"):
|
||||
return
|
||||
|
||||
# For non-web builds, use the audio buses
|
||||
if sfx_bus_index != -1:
|
||||
if sfx_muted:
|
||||
AudioServer.set_bus_mute(sfx_bus_index, true)
|
||||
else:
|
||||
AudioServer.set_bus_mute(sfx_bus_index, false)
|
||||
# Convert from linear to decibels
|
||||
var db_value = linear_to_db(sfx_volume)
|
||||
AudioServer.set_bus_volume_db(sfx_bus_index, db_value)
|
||||
|
||||
# Update all sfx player volumes
|
||||
for player in sfx_players.values():
|
||||
if player != null:
|
||||
if sfx_muted:
|
||||
player.volume_db = linear_to_db(0)
|
||||
else:
|
||||
player.volume_db = linear_to_db(sfx_volume)
|
||||
|
||||
# Helper function to convert linear volume to decibels with a more usable range
|
||||
func linear_to_db(linear_value: float) -> float:
|
||||
if linear_value <= 0:
|
||||
return -80.0 # Very low but not -INF
|
||||
return 20.0 * log(linear_value) / log(10.0)
|
||||
@ -0,0 +1,109 @@
|
||||
extends PanelContainer
|
||||
|
||||
signal closed
|
||||
|
||||
# References to UI controls
|
||||
@onready var music_slider = $MarginContainer/VBoxContainer/MusicSection/MusicControls/MusicSlider
|
||||
@onready var sfx_slider = $MarginContainer/VBoxContainer/SFXSection/SFXControls/SFXSlider
|
||||
@onready var music_mute_button = $MarginContainer/VBoxContainer/MusicSection/MusicControls/MusicMuteButton
|
||||
@onready var sfx_mute_button = $MarginContainer/VBoxContainer/SFXSection/SFXControls/SFXMuteButton
|
||||
@onready var music_value_label = $MarginContainer/VBoxContainer/MusicSection/MusicControls/MusicValueLabel
|
||||
@onready var sfx_value_label = $MarginContainer/VBoxContainer/SFXSection/SFXControls/SFXValueLabel
|
||||
|
||||
func _ready():
|
||||
# Hide the panel initially
|
||||
visible = false
|
||||
|
||||
# Make sure this control blocks mouse input from passing through
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
|
||||
# Connect to SoundManager signals
|
||||
var sound_manager = get_node_or_null("/root/SoundManager")
|
||||
if sound_manager:
|
||||
sound_manager.music_volume_changed.connect(_on_music_volume_changed)
|
||||
sound_manager.sfx_volume_changed.connect(_on_sfx_volume_changed)
|
||||
sound_manager.music_muted_changed.connect(_on_music_muted_changed)
|
||||
sound_manager.sfx_muted_changed.connect(_on_sfx_muted_changed)
|
||||
|
||||
# Initialize UI with current values
|
||||
music_slider.value = sound_manager.music_volume
|
||||
sfx_slider.value = sound_manager.sfx_volume
|
||||
music_mute_button.button_pressed = sound_manager.music_muted
|
||||
sfx_mute_button.button_pressed = sound_manager.sfx_muted
|
||||
|
||||
# Update labels
|
||||
_update_music_label(sound_manager.music_volume)
|
||||
_update_sfx_label(sound_manager.sfx_volume)
|
||||
else:
|
||||
print("ERROR: SoundManager not found!")
|
||||
|
||||
func show_panel():
|
||||
visible = true
|
||||
# Pause the game when the sound panel is open to prevent
|
||||
# accidental building placement while adjusting sound
|
||||
get_tree().paused = true
|
||||
|
||||
func hide_panel():
|
||||
visible = false
|
||||
# Resume the game when the panel is closed
|
||||
get_tree().paused = false
|
||||
|
||||
func _on_close_button_pressed():
|
||||
hide_panel()
|
||||
# Emit signal that panel was closed
|
||||
closed.emit()
|
||||
|
||||
# Consume the event to prevent click-through
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
# Handle slider changes
|
||||
func _on_music_slider_value_changed(value):
|
||||
var sound_manager = get_node_or_null("/root/SoundManager")
|
||||
if sound_manager:
|
||||
sound_manager.set_music_volume(value)
|
||||
_update_music_label(value)
|
||||
|
||||
func _on_sfx_slider_value_changed(value):
|
||||
var sound_manager = get_node_or_null("/root/SoundManager")
|
||||
if sound_manager:
|
||||
sound_manager.set_sfx_volume(value)
|
||||
_update_sfx_label(value)
|
||||
|
||||
# Handle mute button toggling
|
||||
func _on_music_mute_button_toggled(toggled_on):
|
||||
var sound_manager = get_node_or_null("/root/SoundManager")
|
||||
if sound_manager:
|
||||
sound_manager.music_muted = toggled_on
|
||||
sound_manager._apply_music_volume()
|
||||
sound_manager.music_muted_changed.emit(toggled_on)
|
||||
|
||||
func _on_sfx_mute_button_toggled(toggled_on):
|
||||
var sound_manager = get_node_or_null("/root/SoundManager")
|
||||
if sound_manager:
|
||||
sound_manager.sfx_muted = toggled_on
|
||||
sound_manager._apply_sfx_volume()
|
||||
sound_manager.sfx_muted_changed.emit(toggled_on)
|
||||
|
||||
# Update UI from SoundManager events
|
||||
func _on_music_volume_changed(new_volume):
|
||||
music_slider.value = new_volume
|
||||
_update_music_label(new_volume)
|
||||
|
||||
func _on_sfx_volume_changed(new_volume):
|
||||
sfx_slider.value = new_volume
|
||||
_update_sfx_label(new_volume)
|
||||
|
||||
func _on_music_muted_changed(is_muted):
|
||||
music_mute_button.button_pressed = is_muted
|
||||
|
||||
func _on_sfx_muted_changed(is_muted):
|
||||
sfx_mute_button.button_pressed = is_muted
|
||||
|
||||
# Helper functions to update percentage labels
|
||||
func _update_music_label(value):
|
||||
var percentage = int(value * 100)
|
||||
music_value_label.text = str(percentage) + "%"
|
||||
|
||||
func _update_sfx_label(value):
|
||||
var percentage = int(value * 100)
|
||||
sfx_value_label.text = str(percentage) + "%"
|
||||
Binary file not shown.
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://dsyidkx5rlck3"
|
||||
path="res://.godot/imported/building_placing.wav-6750ed644c8875413c24123dee0b5984.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sounds/building_placing.wav"
|
||||
dest_files=["res://.godot/imported/building_placing.wav-6750ed644c8875413c24123dee0b5984.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=0
|
||||
Binary file not shown.
@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://b1nj2ohshhvfa"
|
||||
path="res://.godot/imported/construction.wav-3edc485a6e0d52ecced83e58f1834814.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sounds/construction.wav"
|
||||
dest_files=["res://.godot/imported/construction.wav-3edc485a6e0d52ecced83e58f1834814.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=0
|
||||
Binary file not shown.
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="mp3"
|
||||
type="AudioStreamMP3"
|
||||
uid="uid://cco2pa7jouvxe"
|
||||
path="res://.godot/imported/jazz_new_orleans.mp3-9c30cd48932c10000290cd0aa89376ac.mp3str"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sounds/jazz_new_orleans.mp3"
|
||||
dest_files=["res://.godot/imported/jazz_new_orleans.mp3-9c30cd48932c10000290cd0aa89376ac.mp3str"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=true
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="mp3"
|
||||
type="AudioStreamMP3"
|
||||
uid="uid://dc6t4dmow110x"
|
||||
path="res://.godot/imported/lofi-chill-jazz-272869.mp3-b6fa96cb13fe77ea771a95fd6c8b3e74.mp3str"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sounds/lofi-chill-jazz-272869.mp3"
|
||||
dest_files=["res://.godot/imported/lofi-chill-jazz-272869.mp3-b6fa96cb13fe77ea771a95fd6c8b3e74.mp3str"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0.0
|
||||
bpm=0.0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="mp3"
|
||||
type="AudioStreamMP3"
|
||||
uid="uid://c7ji2wofye2fo"
|
||||
path="res://.godot/imported/power_drill.mp3-b237a687215d757fe9f33b1fb09636fd.mp3str"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sounds/power_drill.mp3"
|
||||
dest_files=["res://.godot/imported/power_drill.mp3-b237a687215d757fe9f33b1fb09636fd.mp3str"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://djxd33vvtr58p"
|
||||
path="res://.godot/imported/muted.png-c594e352e1c503ffa3799ecca5bea12f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/muted.png"
|
||||
dest_files=["res://.godot/imported/muted.png-c594e352e1c503ffa3799ecca5bea12f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://hx3maksi7ma"
|
||||
path="res://.godot/imported/unmuted.png-e722f5f143c0d36eb69ba2a737c9a364.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/unmuted.png"
|
||||
dest_files=["res://.godot/imported/unmuted.png-e722f5f143c0d36eb69ba2a737c9a364.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Loading…
Reference in New Issue