Ship the 0.8.0 first-hour and campaign milestone
Add the campaign shell, pause-safe checkpoint resume, post-shift campaign continue, confirmed campaign reset, difficulty presets, searchable Help, content-matrix validation, recovery events, contextual dialogue, policy/debrief context and eligible-event previews. Extend persistence with atomic separate files and add campaign/checkpoint/tutorial smokes. Headless 247/247, Python 94/94, tutorial/campaign/session/scenario smokes green.
This commit is contained in:
1 parent
86332cd401
commit
aa51ee331e
38 files changed
+1361
-1144
No files matched your search
@@ -5,6 +5,8 @@ extends CanvasLayer
|
||||
## (the `V` bind stays as a debug shortcut).
|
||||
|
||||
signal venue_selected(scenario_id: String)
|
||||
signal continue_requested()
|
||||
signal help_requested()
|
||||
signal settings_requested()
|
||||
signal quit_requested()
|
||||
|
||||
@@ -19,6 +21,9 @@ const VENUES := [
|
||||
var _index := 0
|
||||
var _buttons: Array[Button] = []
|
||||
var _root: Control = null
|
||||
var _status: Label = null
|
||||
var _continue_button: Button = null
|
||||
var _campaign: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
layer = 20
|
||||
@@ -88,6 +93,20 @@ func _build() -> void:
|
||||
subtitle.add_theme_color_override("font_color", Color(0.72, 0.75, 0.82))
|
||||
column.add_child(subtitle)
|
||||
|
||||
_status = Label.new()
|
||||
_status.name = "CampaignStatus"
|
||||
_status.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
_status.add_theme_color_override("font_color", Color(0.62, 0.78, 0.9))
|
||||
column.add_child(_status)
|
||||
|
||||
_continue_button = Button.new()
|
||||
_continue_button.name = "ContinueButton"
|
||||
_continue_button.custom_minimum_size = Vector2(460, 52)
|
||||
_continue_button.pressed.connect(_on_continue_pressed)
|
||||
_style_venue_button(_continue_button)
|
||||
column.add_child(_continue_button)
|
||||
|
||||
for i in VENUES.size():
|
||||
var venue: Dictionary = VENUES[i]
|
||||
var button := Button.new()
|
||||
@@ -109,6 +128,12 @@ func _build() -> void:
|
||||
row.add_theme_constant_override("separation", 12)
|
||||
column.add_child(row)
|
||||
|
||||
var help_button := Button.new()
|
||||
help_button.name = "HelpButton"
|
||||
help_button.text = "Help"
|
||||
help_button.pressed.connect(func(): help_requested.emit())
|
||||
row.add_child(help_button)
|
||||
|
||||
var settings_button := Button.new()
|
||||
settings_button.name = "SettingsButton"
|
||||
settings_button.text = "Settings"
|
||||
@@ -164,12 +189,35 @@ func _focus_index(i: int) -> void:
|
||||
_index = clampi(i, 0, _buttons.size() - 1)
|
||||
_buttons[_index].grab_focus()
|
||||
|
||||
func open() -> void:
|
||||
func open(campaign: Dictionary = {}) -> void:
|
||||
_campaign = campaign.duplicate(true)
|
||||
visible = true
|
||||
_root.visible = true
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
var next_id := str(_campaign.get("next_venue", ""))
|
||||
var complete := bool(_campaign.get("complete", false))
|
||||
if _status:
|
||||
if complete:
|
||||
_status.text = "Campaign complete — pick any venue for a free shift."
|
||||
elif next_id != "":
|
||||
_status.text = "Campaign shift %d · next: %s" % [
|
||||
int(_campaign.get("shift_number", 1)), _venue_label(next_id)]
|
||||
if _continue_button:
|
||||
var next_label := _venue_label(next_id)
|
||||
_continue_button.text = "Continue shift — %s" % next_label if next_id != "" else "Campaign complete"
|
||||
_continue_button.visible = not complete
|
||||
_focus_index(_index)
|
||||
|
||||
func _venue_label(id: String) -> String:
|
||||
for venue in VENUES:
|
||||
if str(venue["id"]) == id:
|
||||
return str(venue["label"])
|
||||
return id.replace("_", " ").capitalize()
|
||||
|
||||
func _on_continue_pressed() -> void:
|
||||
close()
|
||||
continue_requested.emit()
|
||||
|
||||
func close() -> void:
|
||||
visible = false
|
||||
_root.visible = false
|
||||
|
||||
Reference in new issue
Block a user