class_name StudioHelpDialog extends PanelContainer ## Interactive Welcome, Quick Start Guide & Controls Cheatsheet for ModBison Studio. signal closed const SETTINGS_PATH := "user://studio_settings.json" var _check_show_on_start: CheckBox func _init() -> void: name = "StudioHelpDialog" custom_minimum_size = Vector2(720, 520) _build_ui() static func should_show_on_startup() -> bool: if not FileAccess.file_exists(SETTINGS_PATH): return true var fa := FileAccess.open(SETTINGS_PATH, FileAccess.READ) if fa == null: return true var parsed = JSON.parse_string(fa.get_as_text()) fa.close() if parsed is Dictionary and parsed.has("show_help_on_startup"): return bool(parsed["show_help_on_startup"]) return true static func save_show_on_startup(show_help: bool) -> void: var data := {"show_help_on_startup": show_help} var fa := FileAccess.open(SETTINGS_PATH, FileAccess.WRITE) if fa != null: fa.store_string(JSON.stringify(data)) fa.close() func _build_ui() -> void: # Style panel var style := StyleBoxFlat.new() style.bg_color = Color(0.14, 0.16, 0.20, 0.98) style.border_color = Color(0.9, 0.75, 0.25) style.set_border_width_all(2) style.set_corner_radius_all(8) style.content_margin_left = 24 style.content_margin_top = 20 style.content_margin_right = 24 style.content_margin_bottom = 20 add_theme_stylebox_override("panel", style) var root_vbox := VBoxContainer.new() root_vbox.add_theme_constant_override("separation", 14) add_child(root_vbox) # Header Row var header_hbox := HBoxContainer.new() var title_lbl := Label.new() title_lbl.text = "🦬 Welcome to ModBison Studio!" title_lbl.add_theme_font_size_override("font_size", 20) title_lbl.add_theme_color_override("font_color", Color(1.0, 0.88, 0.35)) header_hbox.add_child(title_lbl) var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL header_hbox.add_child(spacer) var close_x := Button.new() close_x.text = "✕" close_x.custom_minimum_size = Vector2(32, 28) close_x.pressed.connect(_on_close_pressed) header_hbox.add_child(close_x) root_vbox.add_child(header_hbox) var subtitle := Label.new() subtitle.text = "Your creator workbench for designing 3D worlds, placing interactive props, and playtesting in real-time." subtitle.add_theme_font_size_override("font_size", 12) subtitle.add_theme_color_override("font_color", Color(0.75, 0.78, 0.82)) root_vbox.add_child(subtitle) # Tab Container for Guide & Shortcuts var tabs := TabContainer.new() tabs.size_flags_vertical = Control.SIZE_EXPAND_FILL # Tab 1: Quick Start Guide var guide_scroll := ScrollContainer.new() guide_scroll.name = "Quick Start Guide" guide_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL var guide_content := VBoxContainer.new() guide_content.size_flags_horizontal = Control.SIZE_EXPAND_FILL guide_content.add_theme_constant_override("separation", 14) _add_guide_step(guide_content, "🎥 1. Navigate the 3D World", "• Fly Camera: Hold Right Mouse Button (RMB) + W, A, S, D, Q, E\n" + "• Orbit Camera: Hold Middle Mouse Button (MMB) and move mouse\n" + "• Zoom: Scroll mouse wheel up/down\n" + "• Focus View: Press [F] to instantly center and zoom onto the selected object") _add_guide_step(guide_content, "🖱️ 2. Select & Transform Props", "• Click any 3D model directly in the viewport (or in the Scene Hierarchy on the left)\n" + "• Switch transformation tools via top Toolbar or hotkeys:\n" + " [1] ↖ Select Mode [2] ✛ Move (drag arrows) [3] ↻ Rotate [4] ⤢ Scale\n" + "• Snapping: Set grid snap step (0.5m, 1.0m, etc.) from the Toolbar snap box\n" + "• Duplicate: Press [Ctrl + D] to duplicate, [Delete] to remove") _add_guide_step(guide_content, "📦 3. Add Models & Stacking", "• Quick Insert: Click [+ Bison], [+ Crate], [+ Tree], or [+ Lamp] on the top toolbar\n" + "• Collections Dock: Open the Collections tab on the right to browse theme packs and drag & drop models directly onto the ground!\n" + "• Object Stacking: Place items directly on top of other items — Studio automatically snaps surfaces so they stick together!") _add_guide_step(guide_content, "▶️ 4. Playtest Your World (F5)", "• Hit [F5] or click the green '▶ Play' button on the toolbar\n" + "• Your player avatar spawns immediately with full walking, jumping, and riding physics!\n" + "• Hit [F5] again or click '⏹ Stop' to cleanly return to Edit mode (all objects roll back to their original positions)") _add_guide_step(guide_content, "💾 5. Save & Return to Game", "• Save: Press [Ctrl + S] or File -> Save Project\n" + "• Return: Click '🏠 Main Menu' in the top-right corner to return to the game menu anytime") guide_scroll.add_child(guide_content) tabs.add_child(guide_scroll) # Tab 2: Keyboard & Mouse Reference var keys_scroll := ScrollContainer.new() keys_scroll.name = "Shortcuts & Controls" keys_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL var keys_vbox := VBoxContainer.new() keys_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL keys_vbox.add_theme_constant_override("separation", 10) _add_shortcut_row(keys_vbox, "RMB + WASDQE", "Fly camera through scene (Hold Shift to fly faster, Ctrl for precision)") _add_shortcut_row(keys_vbox, "MMB + Drag", "Pan camera across scene") _add_shortcut_row(keys_vbox, "Mouse Wheel", "Zoom camera in / out") _add_shortcut_row(keys_vbox, "LMB (Left Click)", "Select object in 3D viewport or click gizmo handles") _add_shortcut_row(keys_vbox, "F Key", "Focus camera on selected object") _add_shortcut_row(keys_vbox, "1, 2, 3, 4 Keys", "Switch tool: [1] Select, [2] Move, [3] Rotate, [4] Scale") _add_shortcut_row(keys_vbox, "7, 1, 3 Keys", "View Presets: [7] Top Ortho, [1] Front Ortho, [3] Side Ortho") _add_shortcut_row(keys_vbox, "5 Key", "Toggle Perspective / Orthographic projection") _add_shortcut_row(keys_vbox, "F5 Key", "Toggle Playtest (Play Solo / Stop Playtest)") _add_shortcut_row(keys_vbox, "Ctrl + Z / Ctrl + Y", "Undo / Redo world modifications") _add_shortcut_row(keys_vbox, "Ctrl + D", "Duplicate selected object") _add_shortcut_row(keys_vbox, "Delete / Backspace", "Delete selected object") _add_shortcut_row(keys_vbox, "Ctrl + S", "Save project to disk (.mbworld)") _add_shortcut_row(keys_vbox, "F1 Key", "Toggle this Help & Controls guide") keys_scroll.add_child(keys_vbox) tabs.add_child(keys_scroll) root_vbox.add_child(tabs) # Footer Row var footer_hbox := HBoxContainer.new() _check_show_on_start = CheckBox.new() _check_show_on_start.text = "Show this guide when opening Studio" _check_show_on_start.button_pressed = should_show_on_startup() _check_show_on_start.toggled.connect(func(val): save_show_on_startup(val)) footer_hbox.add_child(_check_show_on_start) var f_spacer := Control.new() f_spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL footer_hbox.add_child(f_spacer) var btn_close := Button.new() btn_close.text = "✨ Let's Build! (Close)" btn_close.add_theme_color_override("font_color", Color(1.0, 0.9, 0.35)) btn_close.custom_minimum_size = Vector2(160, 34) btn_close.pressed.connect(_on_close_pressed) footer_hbox.add_child(btn_close) root_vbox.add_child(footer_hbox) func _add_guide_step(parent: VBoxContainer, title: String, text: String) -> void: var box := VBoxContainer.new() var t := Label.new() t.text = title t.add_theme_font_size_override("font_size", 13) t.add_theme_color_override("font_color", Color(0.35, 0.85, 1.0)) box.add_child(t) var b := Label.new() b.text = text b.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART b.add_theme_font_size_override("font_size", 11) b.add_theme_color_override("font_color", Color(0.85, 0.87, 0.9)) box.add_child(b) parent.add_child(box) func _add_shortcut_row(parent: VBoxContainer, key: String, desc: String) -> void: var row := HBoxContainer.new() var k_lbl := Label.new() k_lbl.text = key k_lbl.custom_minimum_size = Vector2(180, 0) k_lbl.add_theme_font_size_override("font_size", 11) k_lbl.add_theme_color_override("font_color", Color(1.0, 0.85, 0.4)) row.add_child(k_lbl) var d_lbl := Label.new() d_lbl.text = desc d_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL d_lbl.add_theme_font_size_override("font_size", 11) d_lbl.add_theme_color_override("font_color", Color(0.82, 0.85, 0.88)) row.add_child(d_lbl) parent.add_child(row) func _on_close_pressed() -> void: visible = false closed.emit() func _unhandled_input(event: InputEvent) -> void: if not visible: return if event is InputEventKey: var k := event as InputEventKey if k.pressed and not k.echo: if k.keycode == KEY_ESCAPE or k.keycode == KEY_F1: _on_close_pressed() get_viewport().set_input_as_handled()