T2 tutorial: all five chapters with Old Tam, scripted dusk and demo wolf, rabbits for the hunt, title-screen Tutorial entry, pause-menu skip/leave, goodbye card; full bot playthrough test
This commit is contained in:
@@ -61,9 +61,14 @@ func setup(p: Node3D) -> void:
|
||||
_redraw()
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if Guide.active and event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_H:
|
||||
if not Guide.active or not (event is InputEventKey) or not event.pressed or event.echo:
|
||||
return
|
||||
if event.keycode == KEY_H:
|
||||
_hidden = not _hidden
|
||||
_redraw()
|
||||
elif event.keycode in [KEY_ENTER, KEY_KP_ENTER] and Guide.current().get("goal", {}).has("continue"):
|
||||
Guide.continue_step()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
func _redraw() -> void:
|
||||
var s: Dictionary = Guide.current()
|
||||
@@ -84,13 +89,62 @@ func _on_done(_i: int, s: Dictionary) -> void:
|
||||
tw.tween_interval(1.4)
|
||||
tw.tween_property(_tick, "modulate:a", 0.0, 0.6)
|
||||
|
||||
## The end: Old Tam's summary, and Start your own world / Keep exploring.
|
||||
var card: PanelContainer
|
||||
|
||||
func _on_finished() -> void:
|
||||
_box.visible = false
|
||||
_tick.text = "✓ Chapter done. More of the tutorial is on its way."
|
||||
_tick.modulate.a = 1.0
|
||||
var tw := create_tween()
|
||||
tw.tween_interval(3.0)
|
||||
tw.tween_property(_tick, "modulate:a", 0.0, 1.0)
|
||||
if card != null and is_instance_valid(card):
|
||||
return
|
||||
card = UITheme.panel()
|
||||
card.set_anchors_and_offsets_preset(Control.PRESET_CENTER)
|
||||
card.grow_horizontal = Control.GROW_DIRECTION_BOTH
|
||||
card.grow_vertical = Control.GROW_DIRECTION_BOTH
|
||||
card.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
add_child(card)
|
||||
var v := VBoxContainer.new()
|
||||
v.add_theme_constant_override("separation", 10)
|
||||
v.custom_minimum_size = Vector2(520, 0)
|
||||
card.add_child(v)
|
||||
var f: Dictionary = Guide.finish
|
||||
v.add_child(UITheme.label(String(f.get("title", "Tutorial done")), 26))
|
||||
var t := UITheme.label("%s: \"%s\"" % [Guide.guide_name, String(f.get("text", ""))] if not Guide.guide_name.is_empty() else String(f.get("text", "")), 16, UITheme.TEXT_DIM)
|
||||
t.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
t.custom_minimum_size = Vector2(520, 0)
|
||||
v.add_child(t)
|
||||
var h := HBoxContainer.new()
|
||||
h.add_theme_constant_override("separation", 10)
|
||||
v.add_child(h)
|
||||
for b in [["Start your own world", _on_new_world], ["Keep exploring", _on_keep_exploring]]:
|
||||
var btn := Button.new()
|
||||
btn.text = b[0]
|
||||
btn.focus_mode = Control.FOCUS_NONE
|
||||
btn.custom_minimum_size = Vector2(240, 44)
|
||||
btn.add_theme_font_size_override("font_size", 18)
|
||||
btn.pressed.connect(b[1])
|
||||
h.add_child(btn)
|
||||
if player != null and player.get("ui_open") != null:
|
||||
player.ui_open = true
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
func _on_keep_exploring() -> void:
|
||||
if card != null:
|
||||
card.queue_free()
|
||||
card = null
|
||||
if player != null and player.get("ui_open") != null:
|
||||
player.ui_open = false
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
## Saves the tutorial world and goes to the title screen's New World page.
|
||||
func _on_new_world() -> void:
|
||||
if Guide.world != null and is_instance_valid(Guide.world):
|
||||
Guide.world.save_now()
|
||||
get_tree().paused = false
|
||||
Saves.current_id = ""
|
||||
get_tree().auto_accept_quit = true
|
||||
var TS: Script = load("res://scripts/ui/title_screen.gd")
|
||||
TS.start_page = "new"
|
||||
get_tree().change_scene_to_file("res://scenes/title.tscn")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
_update_marker()
|
||||
|
||||
@@ -80,6 +80,11 @@ func bind(p: CharacterBody3D) -> void:
|
||||
sens_row.add_child(sens)
|
||||
_main.add_child(sens_row)
|
||||
|
||||
# The tutorial: skip a step, or leave it (saved, to carry on later).
|
||||
if Guide.active:
|
||||
_main.add_child(_button("Skip this tutorial step", func(): Guide.skip(); close()))
|
||||
_main.add_child(_button("Leave the tutorial (carry on later)", _on_quit_to_title))
|
||||
|
||||
var saved: bool = Saves.has_world()
|
||||
_online = Net.state == "connected"
|
||||
if _online:
|
||||
|
||||
@@ -9,6 +9,8 @@ var _pages: Dictionary = {} ## name -> Control
|
||||
var _name_edit: LineEdit
|
||||
var _seed_edit: LineEdit
|
||||
var _rot_pick: OptionButton
|
||||
## The page to open on (the tutorial's "Start your own world" sends you to "new").
|
||||
static var start_page := ""
|
||||
## [setting, what the New World page calls it]
|
||||
const ROT_CHOICES := [["normal", "Normally (raw meat lasts 2 days)"], ["slow", "Slowly (twice as long)"], ["off", "Never"]]
|
||||
var _list: VBoxContainer
|
||||
@@ -100,7 +102,8 @@ func _ready() -> void:
|
||||
Net.players_changed.connect(func():
|
||||
if _pages["lobby"].visible:
|
||||
_fill_lobby())
|
||||
_show("main")
|
||||
_show(start_page if start_page in _pages else "main")
|
||||
start_page = ""
|
||||
# Back from a server (left, kicked, or it went away): say why.
|
||||
if Net.state == "offline" and not Net.last_reason.is_empty():
|
||||
_show("join")
|
||||
@@ -202,9 +205,13 @@ func _main_page() -> Control:
|
||||
var v := VBoxContainer.new()
|
||||
v.add_theme_constant_override("separation", 10)
|
||||
var worlds := Saves.list_worlds()
|
||||
if not worlds.is_empty():
|
||||
if worlds.is_empty():
|
||||
v.add_child(UITheme.label("New here? Try the tutorial (about 20 minutes).", 15, UITheme.TEXT_DIM))
|
||||
else:
|
||||
var w: Dictionary = worlds[0]
|
||||
v.add_child(_button("Continue: %s" % w.name, _play.bind(w.id)))
|
||||
var tut := tutorial_world()
|
||||
v.add_child(_button("Tutorial" if tut.is_empty() else "Tutorial (carry on)", _play_tutorial))
|
||||
v.add_child(_button("New World", _show.bind("new")))
|
||||
var lb := _button("Load World", _show.bind("load"))
|
||||
lb.disabled = worlds.is_empty()
|
||||
@@ -673,6 +680,20 @@ func _create() -> void:
|
||||
var rot: String = ROT_CHOICES[maxi(_rot_pick.selected, 0)][0]
|
||||
_play(Saves.create_world(world_name, seed, {"rot": rot}))
|
||||
|
||||
## The tutorial (docs/TUTORIAL_DESIGN.md): its own world on the original
|
||||
## island (seed 1337), made the first time and carried on after that.
|
||||
const TUTORIAL_SEED := 1337
|
||||
|
||||
static func tutorial_world() -> Dictionary:
|
||||
for w in Saves.list_worlds():
|
||||
if bool(w.get("tutorial", false)):
|
||||
return w
|
||||
return {}
|
||||
|
||||
func _play_tutorial() -> void:
|
||||
var w := tutorial_world()
|
||||
_play(String(w.id) if not w.is_empty() else Saves.create_world("Tutorial", TUTORIAL_SEED, {"tutorial": true}))
|
||||
|
||||
func _play(id: String) -> void:
|
||||
Saves.current_id = id
|
||||
get_tree().change_scene_to_file(WORLD_SCENE)
|
||||
|
||||
Reference in New Issue
Block a user