Work for loading missions.
parent
20d631de6f
commit
6055d88d5e
@ -0,0 +1,3 @@
|
||||
{
|
||||
"godotTools.editorPath.godot4": "c:\\Users\\jason\\Downloads\\Godot_v4.4.1-stable_mono_win64\\Godot_v4.4.1-stable_mono_win64\\Godot_v4.4.1-stable_mono_win64.exe"
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
[gd_resource type="Resource" script_class="MissionConfig" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/mission/mission_config.gd" id="1_config"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_config")
|
||||
starting_structures = ["res://structures/road-straight.tres", "res://structures/building-small-a.tres"]
|
||||
initial_structures = [
|
||||
{
|
||||
"structure_path": "res://structures/road-straight.tres",
|
||||
"position": Vector3(0, 0, 0),
|
||||
"rotation": 0
|
||||
},
|
||||
{
|
||||
"structure_path": "res://structures/road-straight.tres",
|
||||
"position": Vector3(1, 0, 0),
|
||||
"rotation": 90
|
||||
}
|
||||
]
|
||||
missions = [
|
||||
{
|
||||
"id": "custom_mission_1",
|
||||
"title": "Road Network",
|
||||
"description": "Build a basic road network for your city",
|
||||
"objectives": [
|
||||
{
|
||||
"type": 1,
|
||||
"target_count": 4,
|
||||
"structure_path": "res://structures/road-straight.tres",
|
||||
"description": "Build 4 straight roads"
|
||||
}
|
||||
],
|
||||
"rewards": {
|
||||
"cash": 200,
|
||||
"experience": 25
|
||||
},
|
||||
"unlocked_items": [
|
||||
"res://structures/road-corner.tres"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "custom_mission_2",
|
||||
"title": "First Houses",
|
||||
"description": "Start building houses along your roads",
|
||||
"objectives": [
|
||||
{
|
||||
"type": 1,
|
||||
"target_count": 2,
|
||||
"structure_path": "res://structures/building-small-a.tres",
|
||||
"description": "Build 2 Type A houses"
|
||||
}
|
||||
],
|
||||
"rewards": {
|
||||
"cash": 300,
|
||||
"experience": 50
|
||||
},
|
||||
"unlocked_items": [
|
||||
"res://structures/building-small-b.tres",
|
||||
"res://structures/building-small-c.tres"
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,85 @@
|
||||
[gd_resource type="Resource" script_class="MissionData" load_steps=9 format=3 uid="uid://bh148683scgge"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_objective"]
|
||||
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="2_mission"]
|
||||
[ext_resource type="Resource" uid="uid://dykbopx8n3c3v" path="res://resources/patterns/road_square.tres" id="3_pattern"]
|
||||
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="4_building_a"]
|
||||
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="5_building_c"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_objective1"]
|
||||
script = ExtResource("1_objective")
|
||||
type = 3
|
||||
target_count = 1
|
||||
current_count = 0
|
||||
description = "Build a 2x2 road square using corner and straight roads"
|
||||
completed = false
|
||||
pattern_rules = ExtResource("3_pattern")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_objective2"]
|
||||
script = ExtResource("1_objective")
|
||||
type = 0
|
||||
target_count = 2
|
||||
current_count = 0
|
||||
description = "Build 2 Type A houses inside the road square"
|
||||
completed = false
|
||||
structure = ExtResource("4_building_a")
|
||||
|
||||
[sub_resource type="Resource" id="Resource_objective3"]
|
||||
script = ExtResource("1_objective")
|
||||
type = 0
|
||||
target_count = 2
|
||||
current_count = 0
|
||||
description = "Build 2 Type C houses outside the road square"
|
||||
completed = false
|
||||
structure = ExtResource("5_building_c")
|
||||
|
||||
[resource]
|
||||
script = ExtResource("2_mission")
|
||||
id = "road_square_neighborhood"
|
||||
title = "Square Neighborhood"
|
||||
description = "Create a well-organized neighborhood by building a square road pattern and placing different types of houses strategically. Complete this mission to unlock all building types!"
|
||||
objectives = Array[ExtResource("1_objective")]([SubResource("Resource_objective1"), SubResource("Resource_objective2"), SubResource("Resource_objective3")])
|
||||
rewards = {
|
||||
"cash": 500,
|
||||
"experience": 100
|
||||
}
|
||||
next_mission_id = ""
|
||||
graph_path = ""
|
||||
full_screen_path = ""
|
||||
intro_text = "Let's create an organized neighborhood! First, we'll build a square road pattern using corner and straight roads. Then, we'll place Type A houses inside the square and Type C houses outside to create a diverse residential area. Complete this mission to unlock all building types!"
|
||||
question_text = ""
|
||||
correct_answer = ""
|
||||
feedback_text = ""
|
||||
incorrect_feedback = ""
|
||||
company_data = ""
|
||||
power_math_content = ""
|
||||
num_of_user_inputs = 1
|
||||
input_labels = Array[String]([])
|
||||
companion_dialog = {
|
||||
"mission_completed": {
|
||||
"animation": "excited",
|
||||
"duration": 6000,
|
||||
"text": "Excellent work! You've created a well-organized neighborhood with a good mix of house types. You've unlocked all building types!"
|
||||
},
|
||||
"mission_started": {
|
||||
"animation": "excited",
|
||||
"duration": 6000,
|
||||
"text": ["Time to build a well-organized neighborhood!", "Let's start by creating a square road pattern."]
|
||||
},
|
||||
"objective_completed_1": {
|
||||
"animation": "happy",
|
||||
"duration": 5000,
|
||||
"text": ["Perfect square! The roads are perfectly aligned.", "Now we can start placing houses inside."]
|
||||
},
|
||||
"objective_completed_2": {
|
||||
"animation": "happy",
|
||||
"duration": 5000,
|
||||
"text": ["The Type A houses fit perfectly inside the road square!", "Now let's add some Type C houses outside."]
|
||||
},
|
||||
"objective_completed_3": {
|
||||
"animation": "happy",
|
||||
"duration": 5000,
|
||||
"text": ["Great placement of the Type C houses outside the square!", "This creates a nice variety in our neighborhood."]
|
||||
}
|
||||
}
|
||||
unlocked_items = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres", "res://structures/building-small-c.tres", "res://structures/building-garage.tres"])
|
||||
@ -0,0 +1,56 @@
|
||||
[gd_resource type="Resource" script_class="MissionData" load_steps=5 format=3 uid="uid://cua0khnbyusip"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/mission/mission_objective.gd" id="1_objective"]
|
||||
[ext_resource type="Script" path="res://scripts/mission/mission_data.gd" id="2_mission"]
|
||||
[ext_resource type="Resource" uid="uid://cntgl86ianngh" path="res://structures/building-small-a.tres" id="3_building_a"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_objective1"]
|
||||
script = ExtResource("1_objective")
|
||||
type = 1
|
||||
target_count = 3
|
||||
current_count = 0
|
||||
description = "Build 3 Type A houses to unlock all building types"
|
||||
completed = false
|
||||
structure = ExtResource("3_building_a")
|
||||
|
||||
[resource]
|
||||
script = ExtResource("2_mission")
|
||||
id = "unlock_buildings"
|
||||
title = "Building Variety"
|
||||
description = "Demonstrate your basic building skills to unlock access to all residential building types!"
|
||||
objectives = Array[ExtResource("1_objective")]([SubResource("Resource_objective1")])
|
||||
rewards = {
|
||||
"cash": 300,
|
||||
"experience": 50
|
||||
}
|
||||
next_mission_id = "construction_efficiency"
|
||||
graph_path = ""
|
||||
full_screen_path = ""
|
||||
intro_text = "The city council is impressed with your development plans! Show them you can handle basic construction by building a few houses, and they'll grant you access to build all residential building types."
|
||||
question_text = ""
|
||||
correct_answer = ""
|
||||
feedback_text = ""
|
||||
incorrect_feedback = ""
|
||||
company_data = ""
|
||||
power_math_content = ""
|
||||
num_of_user_inputs = 0
|
||||
input_labels = Array[String]([])
|
||||
companion_dialog = {
|
||||
"mission_completed": {
|
||||
"animation": "excited",
|
||||
"duration": 6000,
|
||||
"text": "Great job! You now have access to all residential building types!"
|
||||
},
|
||||
"mission_started": {
|
||||
"animation": "excited",
|
||||
"duration": 6000,
|
||||
"text": ["Let's start building some houses!", "Show the city council what you can do!"]
|
||||
},
|
||||
"objective_completed_1": {
|
||||
"animation": "happy",
|
||||
"duration": 5000,
|
||||
"text": ["Nice work with those houses!", "The city council is impressed with your building skills!"]
|
||||
}
|
||||
}
|
||||
unlocked_items = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres", "res://structures/building-small-c.tres", "res://structures/building-garage.tres", "res://structures/road-straight.tres"])
|
||||
starting_structures = Array[String](["res://structures/building-small-a.tres", "res://structures/building-small-b.tres"])
|
||||
@ -0,0 +1,10 @@
|
||||
[gd_resource type="Resource" script_class="GenericText" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://resources/generic_text_panel.resource.gd" id="1_resource"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_resource")
|
||||
panel_type = 0
|
||||
title = "Building Selector"
|
||||
body_text = "Select structures to build in your city."
|
||||
button_text = "Close"
|
||||
@ -0,0 +1,39 @@
|
||||
[gd_resource type="Resource" script_class="PatternRules" load_steps=9 format=3 uid="uid://dykbopx8n3c3v"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/mission/pattern_rules.gd" id="1_rules"]
|
||||
[ext_resource type="Script" path="res://scripts/mission/pattern_rule.gd" id="2_rule"]
|
||||
[ext_resource type="Resource" uid="uid://d2jplegnkl6u2" path="res://structures/road-corner.tres" id="3_corner"]
|
||||
[ext_resource type="Resource" uid="uid://dv14kkhb6umkv" path="res://structures/road-straight.tres" id="4_straight"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_corner1"]
|
||||
script = ExtResource("2_rule")
|
||||
type = 0
|
||||
offset = Vector2i(0, 0)
|
||||
structure = ExtResource("3_corner")
|
||||
rotation = 0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_straight1"]
|
||||
script = ExtResource("2_rule")
|
||||
type = 0
|
||||
offset = Vector2i(1, 0)
|
||||
structure = ExtResource("4_straight")
|
||||
rotation = 0
|
||||
|
||||
[sub_resource type="Resource" id="Resource_straight2"]
|
||||
script = ExtResource("2_rule")
|
||||
type = 0
|
||||
offset = Vector2i(0, 1)
|
||||
structure = ExtResource("4_straight")
|
||||
rotation = 90
|
||||
|
||||
[sub_resource type="Resource" id="Resource_corner2"]
|
||||
script = ExtResource("2_rule")
|
||||
type = 0
|
||||
offset = Vector2i(1, 1)
|
||||
structure = ExtResource("3_corner")
|
||||
rotation = 180
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_rules")
|
||||
pattern_size = Vector2i(2, 2)
|
||||
rules = Array[ExtResource("2_rule")]([SubResource("Resource_corner1"), SubResource("Resource_straight1"), SubResource("Resource_straight2"), SubResource("Resource_corner2")])
|
||||
@ -0,0 +1,56 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b6x8v0j6y5n3q"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/mission/learning_panel.gd" id="1_2k4m3"]
|
||||
|
||||
[node name="LearningPanel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_2k4m3")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 20
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 20
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "Learning Panel"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ContentLabel" type="Label" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Welcome to the learning panel!"
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="SubmitButtonContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="SubmitButton" type="Button" parent="PanelContainer/MarginContainer/ScrollContainer/VBoxContainer/SubmitButtonContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
text = "Submit"
|
||||
@ -1,540 +1,164 @@
|
||||
extends Node
|
||||
class_name JSBridge
|
||||
|
||||
# Make this a singleton
|
||||
static var instance = null
|
||||
|
||||
# Signals
|
||||
signal init_data_received(data)
|
||||
signal init_check_received
|
||||
signal mission_progress_updated(data)
|
||||
signal mission_completed(data)
|
||||
signal open_react_graph(data)
|
||||
signal open_react_table(data)
|
||||
|
||||
# Variables
|
||||
var _interface = null
|
||||
var _init_data = null
|
||||
var _init_check_received = false
|
||||
var _pending_signals = []
|
||||
|
||||
func _init():
|
||||
instance = self
|
||||
|
||||
# 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
|
||||
func _ready():
|
||||
# Connect signals when the node is initialized
|
||||
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');
|
||||
}
|
||||
});
|
||||
# Wait for the interface to be available
|
||||
await get_tree().process_frame
|
||||
|
||||
# Set up message listener and interface
|
||||
JavaScript.JavaScriptGlobal.eval("""
|
||||
// Create the Godot interface
|
||||
window.godot_interface = {
|
||||
_callbacks: {},
|
||||
emit_signal: function(signal_name, data) {
|
||||
console.log('Emitting signal:', signal_name, 'with data:', data);
|
||||
if (window.godot_interface._callbacks[signal_name]) {
|
||||
window.godot_interface._callbacks[signal_name](data);
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
static func sendCompanionDialog(key: String, dialog_data: Dictionary):
|
||||
if not OS.has_feature("web"):
|
||||
return
|
||||
|
||||
print("Sending companion dialog for key: " + key)
|
||||
var dialog_json = JSON.stringify(dialog_data)
|
||||
var script = """
|
||||
(function() {
|
||||
try {
|
||||
if (window.parent) {
|
||||
console.log('Sending companion dialog to parent window');
|
||||
window.parent.postMessage({
|
||||
type: 'stemCity_companionDialog',
|
||||
key: '%s',
|
||||
data: %s,
|
||||
source: 'godot-game',
|
||||
timestamp: Date.now()
|
||||
}, '*');
|
||||
} else {
|
||||
console.log('No parent window found for companion dialog');
|
||||
};
|
||||
|
||||
// Set up message listener
|
||||
window.addEventListener('message', function(event) {
|
||||
console.log('Received message:', event.data);
|
||||
|
||||
// Handle the message
|
||||
if (event.data && event.data.type) {
|
||||
switch(event.data.type) {
|
||||
case 'cityBuilder_init':
|
||||
console.log('Received init data:', event.data.data);
|
||||
window.godot_interface.emit_signal('init_data_received', event.data.data);
|
||||
break;
|
||||
case 'cityBuilder_init_check':
|
||||
console.log('Received init check');
|
||||
window.godot_interface.emit_signal('init_check_received');
|
||||
break;
|
||||
case 'mission_progress_updated':
|
||||
window.godot_interface.emit_signal('mission_progress_updated', event.data.data);
|
||||
break;
|
||||
case 'mission_completed':
|
||||
window.godot_interface.emit_signal('mission_completed', event.data.data);
|
||||
break;
|
||||
case 'open_react_graph':
|
||||
window.godot_interface.emit_signal('open_react_graph', event.data.data);
|
||||
break;
|
||||
case 'open_react_table':
|
||||
window.godot_interface.emit_signal('open_react_table', event.data.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error sending companion dialog via postMessage:', e);
|
||||
}
|
||||
})();
|
||||
""" % [key, dialog_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")
|
||||
});
|
||||
""")
|
||||
|
||||
# Set up the interface
|
||||
_interface = JavaScript.get_interface()
|
||||
print("JavaScript interface initialized")
|
||||
|
||||
# Set up callbacks in JavaScript
|
||||
JavaScript.JavaScriptGlobal.eval("""
|
||||
window.godot_interface._callbacks.init_data_received = function(data) {
|
||||
console.log('Emitting init_data_received signal with data:', data);
|
||||
// Don't re-emit the signal to avoid recursion
|
||||
window.godot_interface._callbacks.init_data_received = null;
|
||||
};
|
||||
window.godot_interface._callbacks.init_check_received = function() {
|
||||
console.log('Emitting init_check_received signal');
|
||||
// Don't re-emit the signal to avoid recursion
|
||||
window.godot_interface._callbacks.init_check_received = null;
|
||||
};
|
||||
window.godot_interface._callbacks.mission_progress_updated = function(data) {
|
||||
window.godot_interface.emit_signal('mission_progress_updated', data);
|
||||
};
|
||||
window.godot_interface._callbacks.mission_completed = function(data) {
|
||||
window.godot_interface.emit_signal('mission_completed', data);
|
||||
};
|
||||
window.godot_interface._callbacks.open_react_graph = function(data) {
|
||||
window.godot_interface.emit_signal('open_react_graph', data);
|
||||
};
|
||||
window.godot_interface._callbacks.open_react_table = function(data) {
|
||||
window.godot_interface.emit_signal('open_react_table', data);
|
||||
};
|
||||
""")
|
||||
print("JavaScript callbacks set up")
|
||||
|
||||
# Process any pending signals
|
||||
_process_pending_signals()
|
||||
|
||||
# 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")
|
||||
func _process_pending_signals():
|
||||
if _pending_signals.size() > 0:
|
||||
print("Processing pending signals...")
|
||||
for signal_data in _pending_signals:
|
||||
emit_signal(signal_data.signal_name, signal_data.data)
|
||||
_pending_signals.clear()
|
||||
print("All pending signals processed")
|
||||
|
||||
# Static methods for interface checks
|
||||
static func has_interface() -> bool:
|
||||
return JavaScript.has_interface()
|
||||
|
||||
static func get_interface():
|
||||
return JavaScript.get_interface()
|
||||
|
||||
func send_signal(signal_name: String, data = null):
|
||||
if JavaScript.has_interface():
|
||||
if _interface and _interface.has_method("emit_signal"):
|
||||
_interface.emit_signal(signal_name, data)
|
||||
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
|
||||
_pending_signals.append({"signal_name": signal_name, "data": data})
|
||||
else:
|
||||
_pending_signals.append({"signal_name": signal_name, "data": data})
|
||||
|
||||
func send_mission_progress(mission_id: String, objective_index: int, current_count: int, target_count: int):
|
||||
send_signal("mission_progress_updated", {
|
||||
"mission_id": mission_id,
|
||||
"objective_index": objective_index,
|
||||
"current_count": current_count,
|
||||
"target_count": target_count
|
||||
})
|
||||
|
||||
func send_mission_completed(mission_id: String):
|
||||
send_signal("mission_completed", {
|
||||
"mission_id": mission_id
|
||||
})
|
||||
|
||||
func send_open_graph(graph_data: Dictionary):
|
||||
send_signal("open_react_graph", graph_data)
|
||||
|
||||
func send_open_table(table_data: Dictionary):
|
||||
send_signal("open_react_table", table_data)
|
||||
|
||||
func send_companion_dialog(dialog_type: String, dialog_data: Dictionary):
|
||||
send_signal("companion_dialog", {
|
||||
"type": dialog_type,
|
||||
"data": dialog_data
|
||||
})
|
||||
|
||||
func send_audio_action(action: String, data: Dictionary = {}):
|
||||
send_signal("audio_action", {
|
||||
"action": action,
|
||||
"data": data
|
||||
})
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
extends Node
|
||||
|
||||
# JavaScript global class for handling JavaScript functionality
|
||||
class_name JavaScript
|
||||
|
||||
# Check if JavaScript is available
|
||||
static func has_interface() -> bool:
|
||||
# Check if running in a web environment
|
||||
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
|
||||
@ -0,0 +1 @@
|
||||
uid://djghkkucobwfl
|
||||
@ -0,0 +1 @@
|
||||
uid://s05virx2cxsi
|
||||
@ -0,0 +1 @@
|
||||
uid://d1ndmeoogiuf4
|
||||
@ -0,0 +1 @@
|
||||
uid://nujnbtcf5kkf
|
||||
@ -0,0 +1 @@
|
||||
uid://cuuydsps7ubth
|
||||
@ -0,0 +1,197 @@
|
||||
extends Node
|
||||
class_name MissionLoader
|
||||
|
||||
const MissionData = preload("res://scripts/mission/mission_data.gd")
|
||||
const MissionObjective = preload("res://scripts/mission/mission_objective.gd")
|
||||
|
||||
var mission_manager: MissionManager
|
||||
var builder: Node3D
|
||||
|
||||
func _init(manager: MissionManager, builder_ref: Node3D):
|
||||
mission_manager = manager
|
||||
builder = builder_ref
|
||||
|
||||
# Load mission data from JavaScript
|
||||
func load_from_js(mission_data: Dictionary) -> void:
|
||||
print("\n=== Loading Mission Data from JavaScript ===")
|
||||
print("Received mission data:", mission_data)
|
||||
|
||||
if not mission_data:
|
||||
print("WARNING: No mission data received from JavaScript")
|
||||
return
|
||||
|
||||
print("Converting mission data to Godot objects...")
|
||||
if "missions" in mission_data:
|
||||
var missions = _convert_missions(mission_data.missions)
|
||||
print("Converted missions:", missions)
|
||||
print("Number of missions converted: ", missions.size())
|
||||
|
||||
if missions.is_empty():
|
||||
print("WARNING: No missions were converted from the data")
|
||||
return
|
||||
|
||||
print("Setting up missions...")
|
||||
mission_manager.missions = missions
|
||||
print("Missions set in manager. Current missions array size: ", mission_manager.missions.size())
|
||||
if mission_manager.missions.size() > 0:
|
||||
print("First mission details:")
|
||||
print(" Title: ", mission_manager.missions[0].title)
|
||||
print(" Description: ", mission_manager.missions[0].description)
|
||||
print(" Number of objectives: ", mission_manager.missions[0].objectives.size())
|
||||
print("=== Mission Data Loading Complete ===\n")
|
||||
else:
|
||||
print("WARNING: No 'missions' key found in mission data")
|
||||
|
||||
# Unlock the starting structures
|
||||
func _unlock_starting_structures(structure_paths: Array) -> void:
|
||||
print("\n=== Unlocking Starting Structures ===")
|
||||
print("Paths to unlock: ", structure_paths)
|
||||
|
||||
if not builder:
|
||||
push_error("Builder not available")
|
||||
return
|
||||
|
||||
# Get current structures
|
||||
var structures = builder.get_structures()
|
||||
if not structures:
|
||||
push_error("No structures available")
|
||||
return
|
||||
|
||||
# Print current unlock status
|
||||
print("\nCurrent structure status:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked)
|
||||
|
||||
# Process each path
|
||||
for path in structure_paths:
|
||||
print("\nProcessing path: ", path)
|
||||
|
||||
# Handle both structures/ and models/ paths
|
||||
var possible_paths = []
|
||||
|
||||
# Add the original path
|
||||
possible_paths.append(path)
|
||||
|
||||
# Handle .tres to .glb conversion
|
||||
if path.ends_with(".tres"):
|
||||
# Convert structures/ path to models/ path
|
||||
if "structures/" in path:
|
||||
possible_paths.append(path.replace("structures/", "models/").replace(".tres", ".glb"))
|
||||
|
||||
# Handle .glb to .tres conversion
|
||||
if path.ends_with(".glb"):
|
||||
# Convert models/ path to structures/ path
|
||||
if "models/" in path:
|
||||
possible_paths.append(path.replace("models/", "structures/").replace(".glb", ".tres"))
|
||||
|
||||
print("Trying possible paths: ", possible_paths)
|
||||
|
||||
var found_match = false
|
||||
for structure in structures:
|
||||
if not structure.model:
|
||||
continue
|
||||
|
||||
var structure_path = structure.model.resource_path
|
||||
|
||||
print("Checking structure: ", structure_path)
|
||||
|
||||
# Only try exact path matches
|
||||
for possible_path in possible_paths:
|
||||
if structure_path == possible_path:
|
||||
print("Found exact path match: ", possible_path)
|
||||
structure.unlocked = true
|
||||
found_match = true
|
||||
break
|
||||
|
||||
if found_match:
|
||||
break
|
||||
|
||||
if not found_match:
|
||||
print("WARNING: No match found for path: ", path)
|
||||
print("Available structures:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print(" ", i, ": ", structure.model.resource_path)
|
||||
|
||||
# Print final unlock status
|
||||
print("\nFinal structure status:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked)
|
||||
|
||||
print("=== Structure Unlocking Complete ===\n")
|
||||
|
||||
# Convert mission dictionaries to MissionData objects
|
||||
func _convert_missions(mission_dicts: Array) -> Array[MissionData]:
|
||||
print("Converting mission configurations...")
|
||||
var converted: Array[MissionData] = []
|
||||
|
||||
for mission_dict in mission_dicts:
|
||||
var mission_data = MissionData.new()
|
||||
|
||||
# Set basic properties
|
||||
mission_data.id = mission_dict.get("id", "")
|
||||
mission_data.title = mission_dict.get("title", "")
|
||||
mission_data.description = mission_dict.get("description", "")
|
||||
|
||||
# Convert objectives
|
||||
var objectives: Array[MissionObjective] = []
|
||||
for obj in mission_dict.get("objectives", []):
|
||||
var objective = MissionObjective.new()
|
||||
objective.type = obj.get("type", 0)
|
||||
objective.target_count = obj.get("target_count", 0)
|
||||
objective.description = obj.get("description", "")
|
||||
|
||||
# Load structure resource if specified
|
||||
var structure_path = obj.get("structure_path", "")
|
||||
if structure_path:
|
||||
objective.structure = load(structure_path)
|
||||
|
||||
objectives.append(objective)
|
||||
|
||||
mission_data.objectives = objectives
|
||||
|
||||
# Set rewards
|
||||
mission_data.rewards = mission_dict.get("rewards", {})
|
||||
|
||||
# Set unlocked items
|
||||
var unlocked_items_array: Array[String] = []
|
||||
for item in mission_dict.get("unlocked_items", []):
|
||||
unlocked_items_array.append(str(item))
|
||||
mission_data.unlocked_items = unlocked_items_array
|
||||
|
||||
# Set starting structures
|
||||
var starting_structures_array: Array[String] = []
|
||||
for item in mission_dict.get("starting_structures", []):
|
||||
starting_structures_array.append(str(item))
|
||||
mission_data.starting_structures = starting_structures_array
|
||||
|
||||
# Set additional properties
|
||||
mission_data.next_mission_id = mission_dict.get("next_mission_id", "")
|
||||
mission_data.graph_path = mission_dict.get("graph_path", "")
|
||||
mission_data.full_screen_path = mission_dict.get("full_screen_path", "")
|
||||
mission_data.intro_text = mission_dict.get("intro_text", "")
|
||||
mission_data.question_text = mission_dict.get("question_text", "")
|
||||
mission_data.correct_answer = mission_dict.get("correct_answer", "")
|
||||
mission_data.feedback_text = mission_dict.get("feedback_text", "")
|
||||
mission_data.incorrect_feedback = mission_dict.get("incorrect_feedback", "")
|
||||
mission_data.company_data = mission_dict.get("company_data", "")
|
||||
mission_data.power_math_content = mission_dict.get("power_math_content", "")
|
||||
mission_data.num_of_user_inputs = mission_dict.get("num_of_user_inputs", 1)
|
||||
|
||||
# Convert input labels to Array[String]
|
||||
var input_labels_array: Array[String] = []
|
||||
for label in mission_dict.get("input_labels", []):
|
||||
input_labels_array.append(str(label))
|
||||
mission_data.input_labels = input_labels_array
|
||||
|
||||
# Set companion dialog
|
||||
mission_data.companion_dialog = mission_dict.get("companion_dialog", {})
|
||||
|
||||
converted.append(mission_data)
|
||||
|
||||
return converted
|
||||
@ -0,0 +1 @@
|
||||
uid://c7x6l804lvsdk
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
||||
extends Resource
|
||||
class_name PatternRule
|
||||
|
||||
enum RuleType {
|
||||
STRUCTURE, # Check for specific structure
|
||||
EMPTY, # Check for empty space
|
||||
ROTATION # Check structure rotation
|
||||
}
|
||||
|
||||
@export var type: RuleType = RuleType.STRUCTURE
|
||||
@export var offset: Vector2i
|
||||
@export var structure: Structure # Structure to check for if type is STRUCTURE
|
||||
@export var rotation: int # Rotation to check for if type is ROTATION
|
||||
@ -0,0 +1 @@
|
||||
uid://clv4vhawbuihu
|
||||
@ -0,0 +1,5 @@
|
||||
extends Resource
|
||||
class_name PatternRules
|
||||
|
||||
@export var pattern_size: Vector2i = Vector2i(2, 2) # Size of the pattern grid
|
||||
@export var rules: Array[PatternRule] # Array of pattern rules to check
|
||||
@ -0,0 +1 @@
|
||||
uid://dq0hn1a4b0c0
|
||||
@ -0,0 +1,83 @@
|
||||
extends Node
|
||||
|
||||
# Process structure unlocking based on mission data
|
||||
static func process_mission_structures(structures: Array[Structure], mission_data):
|
||||
print("\n=== Processing Mission Structures ===")
|
||||
print("Mission ID: ", mission_data.id)
|
||||
|
||||
# Print current unlock status
|
||||
print("\nCurrent structure status:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
|
||||
|
||||
# Don't lock all structures at start - only unlock new ones
|
||||
var paths_to_unlock = []
|
||||
|
||||
# Only add starting structures - unlocked_items are handled when mission completes
|
||||
if mission_data.starting_structures.size() > 0:
|
||||
print("\nProcessing starting structures: ", mission_data.starting_structures)
|
||||
paths_to_unlock.append_array(mission_data.starting_structures)
|
||||
|
||||
# Process all paths to unlock
|
||||
print("\nAttempting to unlock ", paths_to_unlock.size(), " structures")
|
||||
for path in paths_to_unlock:
|
||||
unlock_structure_by_path(structures, path)
|
||||
|
||||
# Print final unlock status
|
||||
print("\nFinal structure status:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
|
||||
|
||||
print("=== Structure Processing Complete ===\n")
|
||||
|
||||
# Helper function to unlock a structure by its resource path
|
||||
static func unlock_structure_by_path(structures: Array[Structure], path: String):
|
||||
print("\nTrying to unlock structure with path: ", path)
|
||||
|
||||
# Print all structures and their current unlock status
|
||||
print("\nCurrent structure status:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
|
||||
|
||||
print("\nStarting structure matching...")
|
||||
for structure in structures:
|
||||
if not structure.model:
|
||||
print("WARNING: Structure has no model!")
|
||||
continue
|
||||
|
||||
var structure_path = structure.model.resource_path
|
||||
print("\nChecking structure: ", structure_path)
|
||||
|
||||
# Try exact path match first
|
||||
if structure_path == path:
|
||||
print("Found exact path match, unlocking")
|
||||
structure.unlocked = true
|
||||
break
|
||||
|
||||
# Try converting between structures/ and models/ paths
|
||||
if path.ends_with(".tres") and "structures/" in path:
|
||||
var glb_path = path.replace("structures/", "models/").replace(".tres", ".glb")
|
||||
if structure_path == glb_path:
|
||||
print("Found converted path match (tres->glb), unlocking")
|
||||
structure.unlocked = true
|
||||
break
|
||||
|
||||
if path.ends_with(".glb") and "models/" in path:
|
||||
var tres_path = path.replace("models/", "structures/").replace(".glb", ".tres")
|
||||
if structure_path == tres_path:
|
||||
print("Found converted path match (glb->tres), unlocking")
|
||||
structure.unlocked = true
|
||||
break
|
||||
|
||||
# Print final unlock status for all structures
|
||||
print("\nFinal structure status after matching:")
|
||||
for i in range(structures.size()):
|
||||
var structure = structures[i]
|
||||
if structure.model:
|
||||
print("Structure ", i, ": ", structure.model.resource_path, " - Unlocked: ", structure.unlocked if "unlocked" in structure else "no unlock property")
|
||||
@ -0,0 +1 @@
|
||||
uid://d4j6txm8aicsd
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue