Add simulated CCS computer
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 981 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://crss7xdb4asxw"
|
||||
path="res://.godot/imported/computer-base.png-c22fe210cc788b2a30551b9078564447.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/ccs_cases_2/images/computer/computer-base.png"
|
||||
dest_files=["res://.godot/imported/computer-base.png-c22fe210cc788b2a30551b9078564447.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="20" height="14" x="2" y="3" rx="2"/>
|
||||
<line x1="8" x2="16" y1="21" y2="21"/>
|
||||
<line x1="12" x2="12" y1="17" y2="21"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 324 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://diqn3ntrortw"
|
||||
path="res://.godot/imported/monitor.svg-1101ac90716471f71a4ffd9814ccca4c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/icons/lucide/monitor.svg"
|
||||
dest_files=["res://.godot/imported/monitor.svg-1101ac90716471f71a4ffd9814ccca4c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=8 format=3]
|
||||
[gd_scene load_steps=11 format=3]
|
||||
|
||||
[ext_resource type="Texture2D" path="res://assets/ccs_cases_2/images/doctor/office-backdrop.png" id="1_backdrop"]
|
||||
[ext_resource type="Script" path="res://scripts/ui/ccs_cases_2/doctor_office.gd" id="2_script"]
|
||||
@@ -6,6 +6,9 @@
|
||||
[ext_resource type="Texture2D" path="res://assets/ccs_cases_2/images/doctor/patient-44-white-male.png" id="4_patient"]
|
||||
[ext_resource type="Shader" path="res://shaders/ccs_cases_2/patient_idle.gdshader" id="5_patient_fit"]
|
||||
[ext_resource type="Texture2D" path="res://assets/ccs_cases_2/images/doctor/chat-bubble.png" id="6_chat_bubble"]
|
||||
[ext_resource type="Texture2D" path="res://assets/ccs_cases_2/images/computer/computer-base.png" id="7_computer"]
|
||||
[ext_resource type="Texture2D" path="res://assets/icons/lucide/monitor.svg" id="8_monitor"]
|
||||
[ext_resource type="Texture2D" path="res://assets/icons/lucide/x.svg" id="9_close"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_patient_fit"]
|
||||
shader = ExtResource("5_patient_fit")
|
||||
@@ -221,3 +224,62 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="ComputerLauncher" type="Button" parent="."]
|
||||
z_index = 40
|
||||
layout_mode = 0
|
||||
offset_left = 24.0
|
||||
offset_top = 24.0
|
||||
offset_right = 188.0
|
||||
offset_bottom = 78.0
|
||||
mouse_default_cursor_shape = 2
|
||||
tooltip_text = "Open simulated computer"
|
||||
icon = ExtResource("8_monitor")
|
||||
icon_max_width = 25
|
||||
expand_icon = true
|
||||
text = "Computer"
|
||||
|
||||
[node name="ComputerOverlay" type="Control" parent="."]
|
||||
visible = false
|
||||
z_index = 100
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 0
|
||||
|
||||
[node name="Dimmer" type="ColorRect" parent="ComputerOverlay"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 0
|
||||
color = Color(0.015, 0.025, 0.04, 0.86)
|
||||
|
||||
[node name="Screen" type="TextureRect" parent="ComputerOverlay"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
texture = ExtResource("7_computer")
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Close" type="Button" parent="ComputerOverlay"]
|
||||
layout_mode = 0
|
||||
offset_left = 1208.0
|
||||
offset_top = 20.0
|
||||
offset_right = 1272.0
|
||||
offset_bottom = 84.0
|
||||
mouse_default_cursor_shape = 2
|
||||
tooltip_text = "Close computer"
|
||||
icon = ExtResource("9_close")
|
||||
icon_max_width = 30
|
||||
expand_icon = true
|
||||
|
||||
@@ -29,6 +29,9 @@ const DIALOGUE_BUBBLE_VERTICAL_CHROME := 95.0
|
||||
@onready var previous_page_button: Button = $DialogueChoiceArea/ChoicePager/Previous
|
||||
@onready var page_label: Label = $DialogueChoiceArea/ChoicePager/Page
|
||||
@onready var next_page_button: Button = $DialogueChoiceArea/ChoicePager/Next
|
||||
@onready var computer_launcher: Button = $ComputerLauncher
|
||||
@onready var computer_overlay: Control = $ComputerOverlay
|
||||
@onready var computer_close_button: Button = $ComputerOverlay/Close
|
||||
|
||||
var blink_tween: Tween
|
||||
var dialogue_tween: Tween
|
||||
@@ -44,11 +47,14 @@ var dialogue_characters_per_second := 35.0
|
||||
var dialogue_reveal_complete := false
|
||||
var dialogue_layout_generation := 0
|
||||
var dialogue_input_enabled := true
|
||||
var computer_available := true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
previous_page_button.pressed.connect(_change_choice_page.bind(-1))
|
||||
next_page_button.pressed.connect(_change_choice_page.bind(1))
|
||||
computer_launcher.pressed.connect(open_computer)
|
||||
computer_close_button.pressed.connect(close_computer)
|
||||
_update_clock()
|
||||
_schedule_patient_blink()
|
||||
hide_patient_dialogue()
|
||||
@@ -158,6 +164,8 @@ func is_patient_dialogue_visible() -> bool:
|
||||
|
||||
|
||||
func configure_interview(node: Dictionary) -> void:
|
||||
computer_available = true
|
||||
computer_launcher.visible = not computer_overlay.visible
|
||||
if debrief_panel != null and is_instance_valid(debrief_panel):
|
||||
debrief_panel.queue_free()
|
||||
debrief_panel = null
|
||||
@@ -220,6 +228,10 @@ func complete_response_transition() -> void:
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if computer_overlay.visible:
|
||||
return
|
||||
if _event_targets_computer_launcher(event):
|
||||
return
|
||||
if not dialogue_input_enabled or not dialogue_bubble.visible:
|
||||
return
|
||||
var should_advance := false
|
||||
@@ -241,6 +253,41 @@ func set_dialogue_input_enabled(enabled: bool) -> void:
|
||||
dialogue_input_enabled = enabled
|
||||
|
||||
|
||||
func open_computer() -> void:
|
||||
if not computer_available or computer_overlay.visible:
|
||||
return
|
||||
computer_overlay.visible = true
|
||||
computer_launcher.visible = false
|
||||
if dialogue_tween and dialogue_tween.is_valid():
|
||||
dialogue_tween.pause()
|
||||
computer_close_button.grab_focus()
|
||||
|
||||
|
||||
func close_computer() -> bool:
|
||||
if not computer_overlay.visible:
|
||||
return false
|
||||
computer_overlay.visible = false
|
||||
computer_launcher.visible = computer_available
|
||||
if dialogue_tween and dialogue_tween.is_valid():
|
||||
dialogue_tween.play()
|
||||
return true
|
||||
|
||||
|
||||
func is_computer_open() -> bool:
|
||||
return computer_overlay.visible
|
||||
|
||||
|
||||
func _event_targets_computer_launcher(event: InputEvent) -> bool:
|
||||
if not computer_launcher.visible:
|
||||
return false
|
||||
if event is InputEventMouseButton:
|
||||
return event.button_index == MOUSE_BUTTON_LEFT \
|
||||
and computer_launcher.get_global_rect().has_point(event.position)
|
||||
if event is InputEventScreenTouch:
|
||||
return computer_launcher.get_global_rect().has_point(event.position)
|
||||
return false
|
||||
|
||||
|
||||
func _split_dialogue_pages(text: String) -> Array[String]:
|
||||
var normalized := text.strip_edges()
|
||||
if normalized.is_empty():
|
||||
@@ -397,6 +444,9 @@ func _on_dialogue_choice_pressed(choice_id: String) -> void:
|
||||
|
||||
|
||||
func show_debrief(debrief: Dictionary, score: Dictionary) -> void:
|
||||
computer_available = false
|
||||
close_computer()
|
||||
computer_launcher.visible = false
|
||||
hide_patient_dialogue()
|
||||
hide_dialogue_choices()
|
||||
if debrief_panel != null and is_instance_valid(debrief_panel):
|
||||
|
||||
@@ -70,6 +70,12 @@ func set_pause_menu_open(open: bool) -> void:
|
||||
active_scene.set_dialogue_input_enabled(not open)
|
||||
|
||||
|
||||
func close_transient_overlay() -> bool:
|
||||
if active_scene != null and active_scene.has_method("close_computer"):
|
||||
return bool(active_scene.close_computer())
|
||||
return false
|
||||
|
||||
|
||||
func compatible_cases(cases: Array) -> Array:
|
||||
return cases.filter(func(item: Variant) -> bool:
|
||||
return typeof(item) == TYPE_DICTIONARY \
|
||||
|
||||
@@ -699,6 +699,9 @@ func _input(event: InputEvent) -> void:
|
||||
if not AuthState.is_logged_in or current_screen_name == "login":
|
||||
return
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
if _close_current_transient_overlay():
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if drawer_open:
|
||||
_close_drawer()
|
||||
get_viewport().set_input_as_handled()
|
||||
@@ -708,6 +711,13 @@ func _input(event: InputEvent) -> void:
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _close_current_transient_overlay() -> bool:
|
||||
var screen: Control = screens.get(current_screen_name)
|
||||
if screen != null and screen.has_method("close_transient_overlay"):
|
||||
return bool(screen.close_transient_overlay())
|
||||
return false
|
||||
|
||||
|
||||
func _screen_uses_pause_menu(screen_name: String) -> bool:
|
||||
return screen_name in ["player", "ccs_player", "ccs_cases_2", "flashcards_study"]
|
||||
|
||||
|
||||
@@ -85,6 +85,16 @@ func _run() -> void:
|
||||
_assert(screen.current_view == "doctor_office", "an active interactive attempt should open the doctor office")
|
||||
_assert(not screen.active_scene.is_patient_dialogue_visible(), "the opening should wait for a clinician dialogue selection")
|
||||
_assert(screen.active_scene.choice_container.get_child_count() == 1, "the scene should render server-provided dialogue choices")
|
||||
_assert(screen.active_scene.computer_launcher.visible, "the doctor office should expose the simulated computer launcher")
|
||||
_assert(screen.active_scene.computer_launcher.icon.resource_path.ends_with("monitor.svg"), "the computer launcher should use the monitor icon")
|
||||
screen.active_scene.computer_launcher.pressed.emit()
|
||||
_assert(screen.active_scene.is_computer_open(), "the computer launcher should open the simulated desktop")
|
||||
_assert(not screen.active_scene.computer_launcher.visible, "the launcher should hide while the computer is open")
|
||||
_assert(screen.active_scene.computer_overlay.get_node("Screen").texture.resource_path.ends_with("computer-base.png"), "the overlay should render the supplied computer base")
|
||||
_assert(screen.active_scene.computer_overlay.get_node("Screen").stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED, "the computer should be fully contained without cropping")
|
||||
_assert(screen.close_transient_overlay(), "Escape routing should close the computer before the pause menu")
|
||||
_assert(not screen.active_scene.is_computer_open(), "closing the transient overlay should restore the doctor office")
|
||||
_assert(not screen.close_transient_overlay(), "a second Escape should be available to open the pause menu")
|
||||
_assert(is_equal_approx(screen.stage_root.scale.x, screen.stage_root.scale.y), "the graphical stage should preserve its aspect ratio")
|
||||
var stale_graphical_attempt := _sample_attempt(false)
|
||||
stale_graphical_attempt.experienceMode = "graphical_interview"
|
||||
@@ -106,6 +116,13 @@ func _run() -> void:
|
||||
_assert(screen.active_scene.dialogue_continue_hint.text == "Click to continue", "every dialogue page should use supported continuation wording")
|
||||
_assert(screen.active_scene.dialogue_text.visible_characters_behavior == TextServer.VC_CHARS_AFTER_SHAPING, "typewriter text should wrap before glyphs are revealed")
|
||||
var wrapped_line_count: int = screen.active_scene.dialogue_text.get_line_count()
|
||||
screen.active_scene.open_computer()
|
||||
var computer_blocked_page: int = screen.active_scene.dialogue_page_index
|
||||
var computer_click := _left_click(screen.active_scene.get_global_rect().get_center())
|
||||
screen.active_scene._input(computer_click)
|
||||
_assert(screen.active_scene.dialogue_page_index == computer_blocked_page and not screen.active_scene.dialogue_reveal_complete, "the open computer should block scene-wide dialogue clicks")
|
||||
screen.active_scene.computer_close_button.pressed.emit()
|
||||
_assert(not screen.active_scene.is_computer_open(), "the computer close button should return to the interview")
|
||||
var outside_click := _left_click(screen.active_scene.get_global_rect().end + Vector2(50.0, 50.0))
|
||||
screen.active_scene._input(outside_click)
|
||||
_assert(not screen.active_scene.dialogue_reveal_complete, "clicks outside the doctor scene should not affect dialogue")
|
||||
@@ -169,6 +186,7 @@ func _run() -> void:
|
||||
await process_frame
|
||||
_assert(screen.current_view == "interview_debrief", "a completed graphical interview should remain in CCS Cases 2")
|
||||
_assert(screen.active_scene.find_child("InterviewDebrief", true, false) != null, "the graphical case should render its debrief")
|
||||
_assert(not screen.active_scene.computer_launcher.visible, "the computer launcher should stay hidden during the interview debrief")
|
||||
|
||||
var sizing_scene = load("res://scenes/ccs_cases_2/doctor_office.tscn").instantiate()
|
||||
host.add_child(sizing_scene)
|
||||
|
||||
Reference in New Issue
Block a user