add changes to javascript bridge to get charts working

pull/18/head
Wade 2025-05-04 22:21:54 +07:00
parent d96b4224d8
commit e403e87dfd
36 changed files with 415 additions and 84 deletions

@ -63,3 +63,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/building-small-a.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -61,3 +61,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/store.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -37,3 +37,10 @@ input_labels = Array[String]([])
companion_dialog = {}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -46,3 +46,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/road-straight-lightposts.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/road-intersection.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -54,3 +54,10 @@ companion_dialog = {
}
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"])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/power_plant.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/grass-trees-tall.glb", "res://models/grass.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -59,3 +59,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -46,3 +46,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://structures/store.tres"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/pavement-fountain.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -58,3 +58,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -46,3 +46,10 @@ companion_dialog = {
}
unlocked_items = Array[String](["res://models/building-small-c.glb"])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -58,3 +58,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -48,3 +48,10 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}

@ -17,7 +17,7 @@
[ext_resource type="PackedScene" uid="uid://dmsy06s02tcw4" path="res://scenes/generic_text_panel.tscn" id="13_7i6dj"]
[ext_resource type="Resource" uid="uid://bh65eqgid4kxy" path="res://structures/building-small-c.tres" id="13_kf5aa"]
[ext_resource type="Script" uid="uid://ctqqmg4ydlbse" path="res://scripts/mission/mission_ui.gd" id="13_xvw5w"]
[ext_resource type="Script" uid="uid://b6kf2nelnggk6" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Script" uid="uid://bt3emc1vt40gq" path="res://resources/generic_text_panel.resource.gd" id="14_76jlq"]
[ext_resource type="Resource" uid="uid://dqqe3iofnleup" path="res://structures/pavement-fountain.tres" id="14_t5ykj"]
[ext_resource type="Script" uid="uid://cjaik5ku37xqx" path="res://scripts/mission/mission_data.gd" id="14_vcrh8"]
[ext_resource type="Resource" uid="uid://dveu4dnue0d54" path="res://structures/road-intersection.tres" id="15_e755i"]
@ -139,6 +139,13 @@ companion_dialog = {
}
unlocked_items = Array[String]([])
starting_structures = Array[String]([])
open_react_graph = false
open_react_table = false
react_data = {}
react_table_data = {
"headers": [],
"rows": []
}
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mission"]
bg_color = Color(0.145098, 0.172549, 0.231373, 0.941176)

@ -12,10 +12,10 @@ signal open_react_graph(data)
signal open_react_table(data)
# Variables
var _interface = null
static var _interface = null
var _init_data = null
var _init_check_received = false
var _pending_signals = []
static var _pending_signals = []
var _my_js_callback = JavaScriptBridge.create_callback(receive_init_data)
var window = null
@ -34,49 +34,7 @@ func _ready():
receive_init_data("test")
# Set up message listener and interface
JavaScriptBridge.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);
}
}
};
// 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;
}
}
});
""")
# Set up the interface
_interface = JavaScript.get_interface()
@ -175,43 +133,142 @@ static func has_interface() -> bool:
static func get_interface():
return JavaScript.get_interface()
# Helper method to convert Godot Dictionary to JSON string
static func JSON_stringify(data) -> String:
return JSON.stringify(data)
#static 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:
# _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
# })
static func send_open_graph(graph_data: Dictionary):
# Format the message to match what React expects
var message = {
"source": "godot-game",
"type": "open_react_graph",
"data": graph_data
}
# First, emit the Godot signal for internal listeners
# Then, send the message directly to React via postMessage for external listeners
if OS.has_feature("web"):
# send_signal("open_react_graph", graph_data)
var message_json = JSON_stringify(message)
var script = """
(function() {
try {
if (window.parent) {
console.log('Sending missionStarted message to parent window');
window.parent.postMessage({
type: 'open_react_graph',
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);
}
})();
""" % message_json
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
print("JavaScriptBridge singleton not available")
else:
# For non-web platforms, add to pending signals
_pending_signals.append({"signal_name": "open_react_graph", "data": graph_data})
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)
static func send_open_table(table_data: Dictionary):
# Format the message to match what React expects
var message = {
"source": "godot-game",
"type": "open_react_table",
"data": table_data
}
# First, emit the Godot signal for internal listeners
# emit_signal("open_react_table", table_data)
# Then, send the message directly to React via postMessage for external listeners
if OS.has_feature("web"):
var message_json = JSON_stringify(message)
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);
}
})();
""" % message_json
if Engine.has_singleton("JavaScriptBridge"):
var js = Engine.get_singleton("JavaScriptBridge")
js.eval(script)
else:
_pending_signals.append({"signal_name": signal_name, "data": data})
print("JavaScriptBridge singleton not available")
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
})
# For non-web platforms, add to pending signals
_pending_signals.append({"signal_name": "open_react_table", "data": 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
# })
# Static wrappers so send_open_ functions can be called on the class directly
#static func send_open_graph(graph_data: Dictionary) -> void:
# if instance:
# instance.send_open_graph(graph_data)
# else:
# push_error("JavaScriptBridge not initialized; cannot send_open_graph")
#static func send_open_table(table_data: Dictionary) -> void:
# if instance:
# instance.send_open_table(table_data)
# else:
# push_error("JavaScriptBridge not initialized; cannot send_open_table")

@ -21,3 +21,10 @@ class_name MissionData
@export var companion_dialog: Dictionary = {} # Map of event keys to dialog entries for the learning companion
@export var unlocked_items: Array[String] = [] # Array of structure resource paths that get unlocked after mission completion
@export var starting_structures: Array[String] = [] # Array of structure resource paths that are unlocked when this mission starts
@export var open_react_graph: bool = false # Whether to display the React graph component
@export var open_react_table: bool = false # Whether to display the React table component
@export var react_data: Dictionary
@export var react_table_data: Dictionary = {
"headers": [], # Array of header text strings
"rows": [] # 2D array of cell values: [[row1cell1, row1cell2...], [row2cell1, row2cell2...], ...]
} # Data structure for React table component

@ -137,7 +137,13 @@ func _convert_missions(mission_dicts: Array) -> Array[MissionData]:
mission_data.id = mission_dict.get("id", "")
mission_data.title = mission_dict.get("title", "")
mission_data.description = mission_dict.get("description", "")
mission_data.open_react_graph = mission_dict.get("open_react_graph", false)
mission_data.open_react_table = mission_dict.get("open_react_table", false)
mission_data.react_data = mission_dict.get("react_data", {})
mission_data.react_table_data = mission_dict.get("react_table_data", {
"headers": [], # Array of header text strings
"rows": [] # 2D array of cell values: [[row1cell1, row1cell2...], [row2cell1, row2cell2...], ...]
})
# Convert objectives
var objectives: Array[MissionObjective] = []
for obj in mission_dict.get("objectives", []):

@ -60,6 +60,14 @@ func _ready() -> void:
await _setup_learning_companion_communication()
print("JavaScript bridge setup completed")
# Connect to the generic_text_panel closed signal if it exists
var generic_text_panel = get_node_or_null("/root/Main/CanvasLayer/GenericTextPanel")
if generic_text_panel and generic_text_panel.has_signal("closed"):
print("Found generic_text_panel, connecting to closed signal")
if generic_text_panel.is_connected("closed", _on_learning_panel_closed_for_react):
generic_text_panel.disconnect("closed", _on_learning_panel_closed_for_react)
generic_text_panel.closed.connect(_on_learning_panel_closed_for_react)
# Only proceed with other initialization after bridge is established
print("\n=== Starting Mission Loader Initialization ===")
# Initialize mission loader
@ -312,6 +320,35 @@ func _show_learning_panel(mission: MissionData) -> void:
# Show the panel
learning_panel.show_learning_panel(mission)
# We set current_mission when starting a mission, so there's no need to store it again
# We now use the generic_text_panel.closed signal configured in _ready()
# We use current_mission directly now, no need for _current_learning_mission
# This function handles showing graph or table data after panel closes
func _on_learning_panel_closed_for_react() -> void:
# For generic text panel, we don't need to disconnect since the signal connection is handled in _ready
# We now use the current_mission property directly instead of _current_learning_mission
print("Panel closed, checking for graph/table data to display")
# Check if we have a current mission
if not current_mission:
print("No current mission")
return
print("Current mission: ", current_mission.id, ", open_react_graph: ", current_mission.open_react_graph, ", open_react_table: ", current_mission.open_react_table)
# Send React data via the autoloaded JavaScriptBridge node
if current_mission.open_react_graph:
print("Opening React graph with data:", current_mission.react_data)
JSBridge.send_open_graph(current_mission.react_data)
elif current_mission.open_react_table:
print("Opening React table with data:", current_mission.react_table_data)
JSBridge.send_open_table(current_mission.react_table_data)
# We're now using current_mission directly, so no need to clear anything
# Function to start mission with error handling
func start_mission(mission: MissionData):