Add categorized Logbook foundation
This commit is contained in:
parent
b3880c9e2b
commit
969ed8f10f
13 changed files with 1001 additions and 68 deletions
98
tests/logbook_runtime_validation.gd
Normal file
98
tests/logbook_runtime_validation.gd
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
extends SceneTree
|
||||
|
||||
const MainScene = preload("res://main/main.tscn")
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var main := MainScene.instantiate()
|
||||
root.add_child(main)
|
||||
for _frame: int in 6:
|
||||
await process_frame
|
||||
assert(bool(main.get("_application_initialized")))
|
||||
var save_manager := main.get("_save_manager") as PlayerSaveManager
|
||||
assert(save_manager.initialize_new_game())
|
||||
main.call("_enter_gameplay")
|
||||
for _frame: int in 8:
|
||||
await process_frame
|
||||
assert(bool(main.get("_gameplay_started")))
|
||||
|
||||
var game_ui := main.get_node("%GameUI") as GameUI
|
||||
var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
|
||||
var logbook := player_menu.get_node("%CatalogLogbook") as LogbookPage
|
||||
var backdrop := main.get_node("%PlayerMenuBackdrop") as ColorRect
|
||||
var hotbar := game_ui.get_node("%Hotbar") as Control
|
||||
|
||||
var shortcut := InputEventKey.new()
|
||||
shortcut.pressed = true
|
||||
shortcut.physical_keycode = KEY_L
|
||||
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
|
||||
await create_timer(2.2).timeout
|
||||
assert(player_menu.visible)
|
||||
assert(logbook.visible)
|
||||
assert(backdrop.visible)
|
||||
assert(not bool(game_ui.get("_player_menu_hotbar_visible")))
|
||||
assert(not hotbar.visible)
|
||||
|
||||
var entry_buttons: Dictionary = logbook.get("_entry_buttons")
|
||||
assert(entry_buttons.size() == 4)
|
||||
await _capture_if_requested("-unknown")
|
||||
logbook.call(
|
||||
"_select_category", LogbookCatalog.Category.FRESH_WATER
|
||||
)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).is_empty())
|
||||
await _capture_if_requested("-fresh")
|
||||
logbook.call("_select_category", LogbookCatalog.Category.SALT_WATER)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).is_empty())
|
||||
logbook.call("_select_category", LogbookCatalog.Category.OTHER)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 4)
|
||||
var player := main.get("_player") as Player
|
||||
player.collection_log.mark_discovered(&"bluegill")
|
||||
await process_frame
|
||||
logbook.call("_select_entry", &"bluegill", &"bluegill")
|
||||
await create_timer(0.2).timeout
|
||||
await _capture_if_requested("-known")
|
||||
|
||||
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
|
||||
await create_timer(2.2).timeout
|
||||
assert(not player_menu.visible)
|
||||
|
||||
assert(bool(player_menu.call("_handle_direct_page_shortcut", shortcut)))
|
||||
await create_timer(2.2).timeout
|
||||
player_menu.call(
|
||||
"_show_section_immediate", PlayerMenu.Section.PROFILE
|
||||
)
|
||||
await process_frame
|
||||
assert(not bool(
|
||||
player_menu.call("_handle_direct_page_shortcut", shortcut)
|
||||
))
|
||||
assert(player_menu.visible)
|
||||
player_menu.close_menu(PlayerMenu.CloseReason.TEARDOWN, false)
|
||||
|
||||
print(
|
||||
"Logbook runtime validation: PASS at %dx%d"
|
||||
% [root.size.x, root.size.y]
|
||||
)
|
||||
main.queue_free()
|
||||
await process_frame
|
||||
quit()
|
||||
|
||||
|
||||
func _capture_if_requested(suffix: String) -> void:
|
||||
if not OS.has_environment("NETFISHING_LOGBOOK_CAPTURE"):
|
||||
return
|
||||
await process_frame
|
||||
await process_frame
|
||||
var image: Image = root.get_texture().get_image()
|
||||
var path: String = (
|
||||
OS.get_environment("NETFISHING_LOGBOOK_CAPTURE")
|
||||
+ suffix
|
||||
+ ".png"
|
||||
)
|
||||
assert(image.save_png(path) == OK)
|
||||
1
tests/logbook_runtime_validation.gd.uid
Normal file
1
tests/logbook_runtime_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ciii3u308pk4n
|
||||
142
tests/logbook_validation.gd
Normal file
142
tests/logbook_validation.gd
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
extends SceneTree
|
||||
|
||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||
const LogbookPageScene = preload("res://ui/logbook_page.tscn")
|
||||
const CatalogResource: FishPoolType = preload(
|
||||
"res://fish/pools/test_water_pool.tres"
|
||||
)
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
if OS.has_environment("NETFISHING_TEST_CREATE_ROOT"):
|
||||
var data_root := PlayerDataRoot.new()
|
||||
root.add_child(data_root)
|
||||
var result: Dictionary = data_root.create_unbound_root(
|
||||
OS.get_environment("NETFISHING_TEST_CREATE_ROOT")
|
||||
)
|
||||
assert(bool(result.get("ok", false)))
|
||||
data_root.queue_free()
|
||||
print("Logbook portable test root: PASS")
|
||||
quit()
|
||||
return
|
||||
_validate_catalog()
|
||||
await _validate_page()
|
||||
print("Logbook validation: PASS")
|
||||
quit()
|
||||
|
||||
|
||||
func _validate_catalog() -> void:
|
||||
var expected_ids: Array[StringName] = [
|
||||
&"bluegill", &"bass", &"carp", &"sunfish",
|
||||
]
|
||||
assert(LogbookCatalog.CATALOG_ORDER == expected_ids)
|
||||
for index: int in expected_ids.size():
|
||||
var fish := CatalogResource.get_fish_by_id(expected_ids[index])
|
||||
assert(fish != null)
|
||||
assert(
|
||||
LogbookCatalog.category_for(fish)
|
||||
== LogbookCatalog.Category.OTHER
|
||||
)
|
||||
assert(LogbookCatalog.catalog_number(fish.id) == index + 1)
|
||||
assert(
|
||||
LogbookCatalog.empty_state(LogbookCatalog.Category.FRESH_WATER)
|
||||
== "No freshwater catches cataloged yet."
|
||||
)
|
||||
assert(
|
||||
LogbookCatalog.empty_state(LogbookCatalog.Category.SALT_WATER)
|
||||
== "No saltwater catches cataloged yet."
|
||||
)
|
||||
|
||||
|
||||
func _validate_page() -> void:
|
||||
var collection := CollectionLogType.new()
|
||||
var inventory := FishInventoryType.new()
|
||||
root.add_child(collection)
|
||||
root.add_child(inventory)
|
||||
var page := LogbookPageScene.instantiate() as LogbookPage
|
||||
root.add_child(page)
|
||||
await process_frame
|
||||
page.setup(collection, inventory, CatalogResource)
|
||||
page.activate()
|
||||
await process_frame
|
||||
|
||||
var entries: Dictionary = page.get("_entry_buttons")
|
||||
assert(entries.size() == 4)
|
||||
for entry_value: Variant in entries.values():
|
||||
var entry := entry_value as Button
|
||||
assert(entry != null)
|
||||
assert(entry.text.contains("???"))
|
||||
assert(entry.tooltip_text.is_empty())
|
||||
assert(entry.icon == null)
|
||||
assert(entry.accessibility_name == "Unknown catalog entry")
|
||||
for fish in CatalogResource.candidates:
|
||||
assert(not entry.text.contains(fish.display_name))
|
||||
|
||||
page.call(
|
||||
"_select_category", LogbookCatalog.Category.FRESH_WATER
|
||||
)
|
||||
await create_timer(0.25).timeout
|
||||
assert((page.get("_entry_buttons") as Dictionary).is_empty())
|
||||
assert(
|
||||
(page.get("_empty_state") as Label).text
|
||||
== "No freshwater catches cataloged yet."
|
||||
)
|
||||
page.call("_select_category", LogbookCatalog.Category.SALT_WATER)
|
||||
await create_timer(0.25).timeout
|
||||
assert((page.get("_entry_buttons") as Dictionary).is_empty())
|
||||
assert(
|
||||
(page.get("_empty_state") as Label).text
|
||||
== "No saltwater catches cataloged yet."
|
||||
)
|
||||
page.call("_select_category", LogbookCatalog.Category.OTHER)
|
||||
await create_timer(0.25).timeout
|
||||
|
||||
var bluegill = CatalogResource.get_fish_by_id(&"bluegill")
|
||||
collection.mark_discovered(&"bluegill")
|
||||
await process_frame
|
||||
entries = page.get("_entry_buttons")
|
||||
var known := entries.get(&"bluegill") as Button
|
||||
assert(known != null)
|
||||
assert(known.text == bluegill.display_name)
|
||||
assert(known.icon == bluegill.display_texture)
|
||||
|
||||
var fish_catch := FishCatchType.new()
|
||||
fish_catch.fish = bluegill
|
||||
fish_catch.fish_id = bluegill.id
|
||||
fish_catch.weight_lb = bluegill.get_minimum_weight()
|
||||
fish_catch.display_scale = bluegill.get_display_scale_for_weight(
|
||||
fish_catch.weight_lb
|
||||
)
|
||||
fish_catch.sale_value = bluegill.get_sale_value_for_weight(
|
||||
fish_catch.weight_lb
|
||||
)
|
||||
fish_catch.ensure_identity()
|
||||
inventory.add_catch(fish_catch)
|
||||
page.call("_select_entry", &"bluegill", &"bluegill")
|
||||
await process_frame
|
||||
assert(_detail_text(page).contains("number owned\n1"))
|
||||
assert(_detail_text(page).contains("number caught\nUnknown"))
|
||||
assert(_detail_text(page).contains("body of water\nUnknown"))
|
||||
assert(_detail_text(page).contains("catalog number\n#001"))
|
||||
|
||||
inventory.remove_catch_by_id(fish_catch.catch_id)
|
||||
await process_frame
|
||||
assert(_detail_text(page).contains("number owned\n0"))
|
||||
page.queue_free()
|
||||
collection.queue_free()
|
||||
inventory.queue_free()
|
||||
|
||||
|
||||
func _detail_text(page: LogbookPage) -> String:
|
||||
var values: PackedStringArray = []
|
||||
var detail_body := page.get("_detail_body") as VBoxContainer
|
||||
for node: Node in detail_body.find_children("*", "Label", true, false):
|
||||
values.append((node as Label).text)
|
||||
return "\n".join(values)
|
||||
1
tests/logbook_validation.gd.uid
Normal file
1
tests/logbook_validation.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c8pt0ibr37rmw
|
||||
72
ui/logbook_catalog.gd
Normal file
72
ui/logbook_catalog.gd
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
class_name LogbookCatalog
|
||||
extends RefCounted
|
||||
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
|
||||
enum Category {
|
||||
FRESH_WATER,
|
||||
SALT_WATER,
|
||||
OTHER,
|
||||
}
|
||||
|
||||
# Catalog numbers are a presentation contract. Add future species deliberately;
|
||||
# filtering and display order must not redefine an existing number.
|
||||
const CATALOG_ORDER: Array[StringName] = [
|
||||
&"bluegill",
|
||||
&"bass",
|
||||
&"carp",
|
||||
&"sunfish",
|
||||
]
|
||||
|
||||
# These maps remain explicit until fish resources gain authored habitat fields.
|
||||
const FRESH_WATER_IDS: Array[StringName] = []
|
||||
const SALT_WATER_IDS: Array[StringName] = []
|
||||
|
||||
|
||||
static func category_for(fish: FishDataType) -> Category:
|
||||
if fish == null:
|
||||
return Category.OTHER
|
||||
if fish.id in FRESH_WATER_IDS:
|
||||
return Category.FRESH_WATER
|
||||
if fish.id in SALT_WATER_IDS:
|
||||
return Category.SALT_WATER
|
||||
return Category.OTHER
|
||||
|
||||
|
||||
static func category_label(category: Category) -> String:
|
||||
match category:
|
||||
Category.FRESH_WATER:
|
||||
return "Fresh Water"
|
||||
Category.SALT_WATER:
|
||||
return "Salt Water"
|
||||
_:
|
||||
return "Other"
|
||||
|
||||
|
||||
static func empty_state(category: Category) -> String:
|
||||
match category:
|
||||
Category.FRESH_WATER:
|
||||
return "No freshwater catches cataloged yet."
|
||||
Category.SALT_WATER:
|
||||
return "No saltwater catches cataloged yet."
|
||||
_:
|
||||
return "No entries available."
|
||||
|
||||
|
||||
static func catalog_number(fish_id: StringName) -> int:
|
||||
var index: int = CATALOG_ORDER.find(fish_id)
|
||||
return index + 1 if index >= 0 else 0
|
||||
|
||||
|
||||
static func ordered_species(
|
||||
candidates: Array[FishDataType],
|
||||
) -> Array[FishDataType]:
|
||||
var by_id: Dictionary[StringName, FishDataType] = {}
|
||||
for fish: FishDataType in candidates:
|
||||
if fish != null and not fish.id.is_empty():
|
||||
by_id[fish.id] = fish
|
||||
var ordered: Array[FishDataType] = []
|
||||
for fish_id: StringName in CATALOG_ORDER:
|
||||
if by_id.has(fish_id):
|
||||
ordered.append(by_id[fish_id])
|
||||
return ordered
|
||||
1
ui/logbook_catalog.gd.uid
Normal file
1
ui/logbook_catalog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://5vvacdo53k0n
|
||||
617
ui/logbook_page.gd
Normal file
617
ui/logbook_page.gd
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
class_name LogbookPage
|
||||
extends Control
|
||||
|
||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||
|
||||
const DETAIL_FADE_DURATION: float = 0.12
|
||||
const CATEGORY_FADE_DURATION: float = UIMotion.UTILITY_EXIT_DURATION
|
||||
const INK := Color("251b10")
|
||||
const MUTED_INK := Color("6d5b45")
|
||||
const PAPER := Color("f2e6c9")
|
||||
const PAPER_DARK := Color("e8d7b4")
|
||||
|
||||
var _collection_log: CollectionLogType
|
||||
var _inventory: FishInventoryType
|
||||
var _catalog: FishPoolType
|
||||
var _category: LogbookCatalog.Category = LogbookCatalog.Category.OTHER
|
||||
var _selected_id: StringName
|
||||
var _selected_entry_key: StringName
|
||||
var _active: bool = false
|
||||
var _interactive: bool = false
|
||||
var _entry_buttons: Dictionary[StringName, Button] = {}
|
||||
var _detail_generation: int = 0
|
||||
var _detail_tween: Tween
|
||||
var _category_generation: int = 0
|
||||
var _category_tween: Tween
|
||||
|
||||
var _category_tabs: Array[Button] = []
|
||||
var _catalog_scroll: ScrollContainer
|
||||
var _catalog_grid: GridContainer
|
||||
var _empty_state: Label
|
||||
var _detail_body: VBoxContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_build_interface()
|
||||
resized.connect(_update_responsive_layout)
|
||||
_update_responsive_layout()
|
||||
set_interactive(false)
|
||||
|
||||
|
||||
func setup(
|
||||
collection_log: CollectionLogType,
|
||||
inventory: FishInventoryType,
|
||||
catalog: FishPoolType,
|
||||
) -> void:
|
||||
_collection_log = collection_log
|
||||
_inventory = inventory
|
||||
_catalog = catalog
|
||||
if not _collection_log.fish_discovered.is_connected(
|
||||
_on_fish_discovered
|
||||
):
|
||||
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
||||
if not _collection_log.collection_changed.is_connected(
|
||||
_on_collection_changed
|
||||
):
|
||||
_collection_log.collection_changed.connect(_on_collection_changed)
|
||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||
_refresh_catalog()
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
_active = true
|
||||
set_interactive(true)
|
||||
_refresh_catalog()
|
||||
|
||||
|
||||
func deactivate() -> void:
|
||||
_active = false
|
||||
set_interactive(false)
|
||||
_cancel_detail_tween()
|
||||
_cancel_category_tween()
|
||||
|
||||
|
||||
func set_interactive(value: bool) -> void:
|
||||
_interactive = value and _active
|
||||
mouse_filter = (
|
||||
Control.MOUSE_FILTER_PASS
|
||||
if _interactive
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
for tab: Button in _category_tabs:
|
||||
tab.focus_mode = (
|
||||
Control.FOCUS_ALL if _interactive else Control.FOCUS_NONE
|
||||
)
|
||||
tab.mouse_filter = (
|
||||
Control.MOUSE_FILTER_STOP
|
||||
if _interactive
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
for entry: Button in _entry_buttons.values():
|
||||
entry.focus_mode = (
|
||||
Control.FOCUS_ALL if _interactive else Control.FOCUS_NONE
|
||||
)
|
||||
entry.mouse_filter = (
|
||||
Control.MOUSE_FILTER_STOP
|
||||
if _interactive
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
|
||||
|
||||
func focus_initial() -> void:
|
||||
if not _active or not _interactive:
|
||||
return
|
||||
var selected := _entry_buttons.get(_selected_id) as Button
|
||||
if selected != null:
|
||||
selected.grab_focus()
|
||||
elif not _entry_buttons.is_empty():
|
||||
var first := _entry_buttons.values().front() as Button
|
||||
first.grab_focus()
|
||||
else:
|
||||
_category_tabs[int(_category)].grab_focus()
|
||||
|
||||
|
||||
func _build_interface() -> void:
|
||||
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
var outer := MarginContainer.new()
|
||||
outer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
outer.add_theme_constant_override("margin_left", 52)
|
||||
outer.add_theme_constant_override("margin_top", 116)
|
||||
outer.add_theme_constant_override("margin_right", 52)
|
||||
outer.add_theme_constant_override("margin_bottom", 18)
|
||||
add_child(outer)
|
||||
|
||||
var layout := VBoxContainer.new()
|
||||
layout.add_theme_constant_override("separation", 8)
|
||||
outer.add_child(layout)
|
||||
|
||||
var tabs := HBoxContainer.new()
|
||||
tabs.add_theme_constant_override("separation", 8)
|
||||
layout.add_child(tabs)
|
||||
for category: LogbookCatalog.Category in [
|
||||
LogbookCatalog.Category.FRESH_WATER,
|
||||
LogbookCatalog.Category.SALT_WATER,
|
||||
LogbookCatalog.Category.OTHER,
|
||||
]:
|
||||
var tab := Button.new()
|
||||
tab.custom_minimum_size = Vector2(168, 42)
|
||||
tab.toggle_mode = true
|
||||
tab.text = LogbookCatalog.category_label(category)
|
||||
tab.button_pressed = category == _category
|
||||
tab.pressed.connect(_select_category.bind(category))
|
||||
UtilityPageStyle.apply_button(tab)
|
||||
tabs.add_child(tab)
|
||||
_category_tabs.append(tab)
|
||||
|
||||
var book := HBoxContainer.new()
|
||||
book.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
book.add_theme_constant_override("separation", 12)
|
||||
layout.add_child(book)
|
||||
|
||||
var left_page := PanelContainer.new()
|
||||
left_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
left_page.size_flags_stretch_ratio = 1.12
|
||||
left_page.add_theme_stylebox_override(
|
||||
"panel", _paper_style(PAPER, true)
|
||||
)
|
||||
book.add_child(left_page)
|
||||
var left_margin := MarginContainer.new()
|
||||
_set_margins(left_margin, 18, 16, 18, 16)
|
||||
left_page.add_child(left_margin)
|
||||
var left_layout := VBoxContainer.new()
|
||||
left_layout.add_theme_constant_override("separation", 8)
|
||||
left_margin.add_child(left_layout)
|
||||
var heading := _label("catch catalog", 25)
|
||||
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
left_layout.add_child(heading)
|
||||
_empty_state = _label("", 18)
|
||||
_empty_state.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_empty_state.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_empty_state.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
left_layout.add_child(_empty_state)
|
||||
_catalog_scroll = ScrollContainer.new()
|
||||
_catalog_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_catalog_scroll.horizontal_scroll_mode = (
|
||||
ScrollContainer.SCROLL_MODE_DISABLED
|
||||
)
|
||||
left_layout.add_child(_catalog_scroll)
|
||||
_catalog_grid = GridContainer.new()
|
||||
_catalog_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_catalog_grid.add_theme_constant_override("h_separation", 8)
|
||||
_catalog_grid.add_theme_constant_override("v_separation", 8)
|
||||
_catalog_scroll.add_child(_catalog_grid)
|
||||
|
||||
var gutter := ColorRect.new()
|
||||
gutter.custom_minimum_size.x = 10
|
||||
gutter.color = Color("795f3f")
|
||||
gutter.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
book.add_child(gutter)
|
||||
|
||||
var right_page := PanelContainer.new()
|
||||
right_page.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
right_page.add_theme_stylebox_override(
|
||||
"panel", _paper_style(PAPER_DARK, false)
|
||||
)
|
||||
book.add_child(right_page)
|
||||
var right_margin := MarginContainer.new()
|
||||
_set_margins(right_margin, 22, 16, 22, 16)
|
||||
right_page.add_child(right_margin)
|
||||
var details_scroll := ScrollContainer.new()
|
||||
details_scroll.horizontal_scroll_mode = (
|
||||
ScrollContainer.SCROLL_MODE_DISABLED
|
||||
)
|
||||
right_margin.add_child(details_scroll)
|
||||
_detail_body = VBoxContainer.new()
|
||||
_detail_body.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_detail_body.add_theme_constant_override("separation", 8)
|
||||
details_scroll.add_child(_detail_body)
|
||||
_show_no_selection()
|
||||
|
||||
|
||||
func _refresh_catalog() -> void:
|
||||
if not is_node_ready():
|
||||
return
|
||||
var preserved_selection: StringName = _selected_id
|
||||
var preserved_entry_key: StringName = _selected_entry_key
|
||||
_clear_entries()
|
||||
var species: Array[FishDataType] = []
|
||||
if _catalog != null:
|
||||
species = LogbookCatalog.ordered_species(_catalog.candidates)
|
||||
for fish: FishDataType in species:
|
||||
if LogbookCatalog.category_for(fish) != _category:
|
||||
continue
|
||||
var discovered: bool = _collection_log.has_discovered(fish.id)
|
||||
var entry: Button = _make_entry(fish if discovered else null)
|
||||
var selection_key: StringName = (
|
||||
fish.id if discovered else _unknown_selection_key(fish)
|
||||
)
|
||||
_entry_buttons[selection_key] = entry
|
||||
entry.pressed.connect(
|
||||
_select_entry.bind(
|
||||
selection_key,
|
||||
fish.id if discovered else StringName(),
|
||||
)
|
||||
)
|
||||
entry.focus_entered.connect(
|
||||
_select_entry.bind(
|
||||
selection_key,
|
||||
fish.id if discovered else StringName(),
|
||||
)
|
||||
)
|
||||
_catalog_grid.add_child(entry)
|
||||
_empty_state.text = LogbookCatalog.empty_state(_category)
|
||||
_empty_state.visible = _entry_buttons.is_empty()
|
||||
_catalog_scroll.visible = not _entry_buttons.is_empty()
|
||||
if (
|
||||
not preserved_selection.is_empty()
|
||||
and _entry_buttons.has(preserved_selection)
|
||||
):
|
||||
_selected_id = preserved_selection
|
||||
_selected_entry_key = preserved_selection
|
||||
elif _entry_buttons.has(preserved_entry_key):
|
||||
_selected_id = StringName()
|
||||
_selected_entry_key = preserved_entry_key
|
||||
else:
|
||||
_selected_id = StringName()
|
||||
_selected_entry_key = StringName()
|
||||
_refresh_selection_styles()
|
||||
_refresh_details(false)
|
||||
set_interactive(_interactive)
|
||||
|
||||
|
||||
func _make_entry(fish: FishDataType) -> Button:
|
||||
var entry := Button.new()
|
||||
entry.custom_minimum_size = Vector2(220, 122)
|
||||
entry.toggle_mode = true
|
||||
entry.clip_text = true
|
||||
entry.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||
entry.add_theme_font_size_override("font_size", 17)
|
||||
entry.add_theme_color_override("font_color", INK)
|
||||
entry.add_theme_color_override("font_focus_color", INK)
|
||||
entry.add_theme_color_override("font_hover_color", INK)
|
||||
entry.add_theme_stylebox_override("normal", _entry_style(false))
|
||||
entry.add_theme_stylebox_override("hover", _entry_style(true))
|
||||
entry.add_theme_stylebox_override("focus", _entry_style(true))
|
||||
entry.add_theme_stylebox_override("pressed", _entry_style(true))
|
||||
if fish == null:
|
||||
entry.text = "?\n???\nCatch this creature\nto learn more."
|
||||
entry.tooltip_text = ""
|
||||
entry.accessibility_name = "Unknown catalog entry"
|
||||
else:
|
||||
entry.text = fish.display_name
|
||||
entry.icon = fish.display_texture
|
||||
entry.add_theme_constant_override("icon_max_width", 118)
|
||||
entry.expand_icon = true
|
||||
entry.vertical_icon_alignment = VERTICAL_ALIGNMENT_TOP
|
||||
entry.tooltip_text = fish.display_name
|
||||
entry.accessibility_name = fish.display_name
|
||||
return entry
|
||||
|
||||
|
||||
func _select_category(category: LogbookCatalog.Category) -> void:
|
||||
if category == _category:
|
||||
return
|
||||
_category = category
|
||||
_selected_id = StringName()
|
||||
_selected_entry_key = StringName()
|
||||
for index: int in _category_tabs.size():
|
||||
_category_tabs[index].button_pressed = index == int(_category)
|
||||
_begin_category_transition()
|
||||
|
||||
|
||||
func _begin_category_transition() -> void:
|
||||
_cancel_category_tween()
|
||||
_category_generation += 1
|
||||
var generation: int = _category_generation
|
||||
var restore_interactive: bool = _interactive
|
||||
set_interactive(false)
|
||||
_category_tween = create_tween()
|
||||
_category_tween.tween_method(
|
||||
_set_category_content_alpha,
|
||||
1.0,
|
||||
0.0,
|
||||
CATEGORY_FADE_DURATION * 0.5,
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_category_tween.tween_callback(
|
||||
func() -> void:
|
||||
if generation != _category_generation:
|
||||
return
|
||||
_refresh_catalog()
|
||||
_set_category_content_alpha(0.0)
|
||||
)
|
||||
_category_tween.tween_method(
|
||||
_set_category_content_alpha,
|
||||
0.0,
|
||||
1.0,
|
||||
CATEGORY_FADE_DURATION * 0.5,
|
||||
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
_category_tween.finished.connect(
|
||||
func() -> void:
|
||||
if generation != _category_generation:
|
||||
return
|
||||
_category_tween = null
|
||||
_set_category_content_alpha(1.0)
|
||||
set_interactive(restore_interactive)
|
||||
focus_initial(),
|
||||
CONNECT_ONE_SHOT,
|
||||
)
|
||||
|
||||
|
||||
func _cancel_category_tween() -> void:
|
||||
_category_generation += 1
|
||||
if _category_tween != null:
|
||||
_category_tween.kill()
|
||||
_category_tween = null
|
||||
_set_category_content_alpha(1.0)
|
||||
|
||||
|
||||
func _set_category_content_alpha(alpha: float) -> void:
|
||||
if _catalog_scroll != null:
|
||||
_catalog_scroll.modulate.a = alpha
|
||||
if _empty_state != null:
|
||||
_empty_state.modulate.a = alpha
|
||||
if _detail_body != null:
|
||||
_detail_body.modulate.a = alpha
|
||||
|
||||
|
||||
func _select_entry(entry_key: StringName, fish_id: StringName) -> void:
|
||||
if fish_id.is_empty():
|
||||
_selected_id = StringName()
|
||||
_selected_entry_key = entry_key
|
||||
_refresh_selection_styles()
|
||||
_show_unknown()
|
||||
return
|
||||
if (
|
||||
_collection_log == null
|
||||
or not _collection_log.has_discovered(fish_id)
|
||||
):
|
||||
return
|
||||
_selected_id = fish_id
|
||||
_selected_entry_key = entry_key
|
||||
_refresh_selection_styles()
|
||||
_refresh_details(true)
|
||||
|
||||
|
||||
func _refresh_details(animate: bool) -> void:
|
||||
if _selected_id.is_empty():
|
||||
_show_no_selection()
|
||||
return
|
||||
if (
|
||||
_collection_log == null
|
||||
or not _collection_log.has_discovered(_selected_id)
|
||||
):
|
||||
_show_unknown()
|
||||
return
|
||||
var fish: FishDataType = (
|
||||
_catalog.get_fish_by_id(_selected_id)
|
||||
if _catalog != null
|
||||
else null
|
||||
)
|
||||
if fish == null:
|
||||
_show_no_selection()
|
||||
return
|
||||
var rebuild: Callable = _build_known_details.bind(fish)
|
||||
if animate:
|
||||
_crossfade_details(rebuild)
|
||||
else:
|
||||
rebuild.call()
|
||||
|
||||
|
||||
func _build_known_details(fish: FishDataType) -> void:
|
||||
_clear_details()
|
||||
var catalog_number: int = LogbookCatalog.catalog_number(fish.id)
|
||||
var title := _label(fish.display_name, 30)
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_detail_body.add_child(title)
|
||||
if fish.display_texture != null:
|
||||
var artwork := TextureRect.new()
|
||||
artwork.custom_minimum_size = Vector2(0, 150)
|
||||
artwork.texture = fish.display_texture
|
||||
artwork.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
artwork.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
artwork.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
artwork.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_detail_body.add_child(artwork)
|
||||
_add_detail_row(
|
||||
"catalog number",
|
||||
"#%03d" % catalog_number if catalog_number > 0 else "Unknown",
|
||||
)
|
||||
_add_detail_row("rarity", fish.get_rarity_name())
|
||||
_add_detail_row("body of water", "Unknown")
|
||||
_add_detail_row("time of day", _availability_text(fish))
|
||||
_add_detail_row(
|
||||
"weight range",
|
||||
"%.2f–%.2f lb" % [
|
||||
fish.get_minimum_weight(),
|
||||
fish.get_maximum_weight(),
|
||||
],
|
||||
)
|
||||
_add_detail_row(
|
||||
"value range",
|
||||
"%d–%d fish coin" % [
|
||||
fish.sell_value_min,
|
||||
fish.sell_value_max,
|
||||
],
|
||||
)
|
||||
_add_detail_row("number caught", "Unknown")
|
||||
_add_detail_row(
|
||||
"number owned",
|
||||
str(_inventory.get_count(fish.id) if _inventory != null else 0),
|
||||
)
|
||||
_add_detail_row("facts", "Unknown")
|
||||
|
||||
|
||||
func _show_no_selection() -> void:
|
||||
_clear_details()
|
||||
var instruction := _label("Select an entry to read its catch record.", 21)
|
||||
instruction.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
instruction.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
instruction.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
instruction.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_detail_body.add_child(instruction)
|
||||
|
||||
|
||||
func _show_unknown() -> void:
|
||||
_clear_details()
|
||||
var unknown := _label("???", 34)
|
||||
unknown.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_detail_body.add_child(unknown)
|
||||
var instruction := _label("Catch this creature to learn more.", 20)
|
||||
instruction.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
instruction.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_detail_body.add_child(instruction)
|
||||
|
||||
|
||||
func _add_detail_row(row_name: String, value: String) -> void:
|
||||
var row := VBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 1)
|
||||
var heading := _label(row_name, 15)
|
||||
heading.add_theme_color_override("font_color", MUTED_INK)
|
||||
var content := _label(value, 20)
|
||||
content.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
row.add_child(heading)
|
||||
row.add_child(content)
|
||||
_detail_body.add_child(row)
|
||||
|
||||
|
||||
func _availability_text(fish: FishDataType) -> String:
|
||||
if fish.availability == null:
|
||||
return "Unknown"
|
||||
if fish.availability.allow_day and fish.availability.allow_night:
|
||||
return "day and night"
|
||||
if fish.availability.allow_day:
|
||||
return "day"
|
||||
if fish.availability.allow_night:
|
||||
return "night"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
func _crossfade_details(rebuild: Callable) -> void:
|
||||
_cancel_detail_tween()
|
||||
_detail_generation += 1
|
||||
var generation: int = _detail_generation
|
||||
_detail_tween = create_tween()
|
||||
_detail_tween.tween_property(
|
||||
_detail_body, "modulate:a", 0.0, DETAIL_FADE_DURATION * 0.5
|
||||
)
|
||||
_detail_tween.tween_callback(
|
||||
func() -> void:
|
||||
if generation == _detail_generation:
|
||||
rebuild.call()
|
||||
)
|
||||
_detail_tween.tween_property(
|
||||
_detail_body, "modulate:a", 1.0, DETAIL_FADE_DURATION * 0.5
|
||||
)
|
||||
_detail_tween.finished.connect(
|
||||
func() -> void:
|
||||
if generation == _detail_generation:
|
||||
_detail_tween = null,
|
||||
CONNECT_ONE_SHOT,
|
||||
)
|
||||
|
||||
|
||||
func _cancel_detail_tween() -> void:
|
||||
_detail_generation += 1
|
||||
if _detail_tween != null:
|
||||
_detail_tween.kill()
|
||||
_detail_tween = null
|
||||
if _detail_body != null:
|
||||
_detail_body.modulate.a = 1.0
|
||||
|
||||
|
||||
func _clear_entries() -> void:
|
||||
for child: Node in _catalog_grid.get_children():
|
||||
_catalog_grid.remove_child(child)
|
||||
child.queue_free()
|
||||
_entry_buttons.clear()
|
||||
|
||||
|
||||
func _clear_details() -> void:
|
||||
for child: Node in _detail_body.get_children():
|
||||
_detail_body.remove_child(child)
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func _refresh_selection_styles() -> void:
|
||||
for fish_id: StringName in _entry_buttons:
|
||||
_entry_buttons[fish_id].button_pressed = (
|
||||
fish_id == _selected_entry_key
|
||||
)
|
||||
|
||||
|
||||
func _on_fish_discovered(fish_id: StringName) -> void:
|
||||
if _catalog == null or _catalog.get_fish_by_id(fish_id) == null:
|
||||
return
|
||||
_selected_id = fish_id
|
||||
_selected_entry_key = fish_id
|
||||
_refresh_catalog()
|
||||
|
||||
|
||||
func _on_collection_changed() -> void:
|
||||
_refresh_catalog()
|
||||
|
||||
|
||||
func _on_inventory_changed() -> void:
|
||||
_refresh_details(false)
|
||||
|
||||
|
||||
func _update_responsive_layout() -> void:
|
||||
if _catalog_grid == null:
|
||||
return
|
||||
_catalog_grid.columns = 1 if size.x < 900.0 else 2
|
||||
|
||||
|
||||
func _unknown_selection_key(fish: FishDataType) -> StringName:
|
||||
var number: int = LogbookCatalog.catalog_number(fish.id)
|
||||
return StringName("unknown_%d" % number)
|
||||
|
||||
|
||||
func _label(text: String, font_size: int) -> Label:
|
||||
var label := Label.new()
|
||||
label.text = text
|
||||
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||
label.add_theme_font_size_override("font_size", font_size)
|
||||
label.add_theme_color_override("font_color", INK)
|
||||
return label
|
||||
|
||||
|
||||
func _paper_style(color: Color, left: bool) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = color
|
||||
style.border_color = Color("9b7a4f")
|
||||
style.set_border_width_all(3)
|
||||
style.corner_radius_top_left = 26 if left else 6
|
||||
style.corner_radius_bottom_left = 32 if left else 6
|
||||
style.corner_radius_top_right = 6 if left else 28
|
||||
style.corner_radius_bottom_right = 6 if left else 34
|
||||
return style
|
||||
|
||||
|
||||
func _entry_style(selected: bool) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = Color("fff5dc") if selected else Color("eadcbd")
|
||||
style.border_color = Color("247a8b") if selected else Color("9b7a4f")
|
||||
style.set_border_width_all(3 if selected else 2)
|
||||
style.set_corner_radius_all(8)
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_top = 8
|
||||
style.content_margin_right = 8
|
||||
style.content_margin_bottom = 8
|
||||
return style
|
||||
|
||||
|
||||
func _set_margins(
|
||||
container: MarginContainer,
|
||||
left: int,
|
||||
top: int,
|
||||
right: int,
|
||||
bottom: int,
|
||||
) -> void:
|
||||
container.add_theme_constant_override("margin_left", left)
|
||||
container.add_theme_constant_override("margin_top", top)
|
||||
container.add_theme_constant_override("margin_right", right)
|
||||
container.add_theme_constant_override("margin_bottom", bottom)
|
||||
1
ui/logbook_page.gd.uid
Normal file
1
ui/logbook_page.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://oo5eek8i04xy
|
||||
12
ui/logbook_page.tscn
Normal file
12
ui/logbook_page.tscn
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/logbook_page.gd" id="1_logbook"]
|
||||
|
||||
[node name="LogbookPage" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_logbook")
|
||||
|
|
@ -98,6 +98,10 @@ func consume_escape() -> bool:
|
|||
return false
|
||||
|
||||
|
||||
func is_composing_letter() -> bool:
|
||||
return _compose != null and _compose.visible
|
||||
|
||||
|
||||
func set_interactive(value: bool) -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_PASS if value else Control.MOUSE_FILTER_IGNORE
|
||||
for node: Node in find_children("*", "BaseButton", true, false):
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ enum CloseReason {
|
|||
@onready var _bag_sprite_detail_name: Label = %BagSpriteDetailName
|
||||
@onready var _bag_sprite_detail_data: Label = %BagSpriteDetailData
|
||||
@onready var _logbook_page: Control = %LogbookPage
|
||||
@onready var _catalog_logbook: LogbookPage = %CatalogLogbook
|
||||
@onready var _mail_page: MailPage = %MailPage
|
||||
@onready var _profile_page: ProfilePage = %ProfilePage
|
||||
@onready var _players_page: PlayersPage = %PlayersPage
|
||||
|
|
@ -376,10 +377,7 @@ func _ready() -> void:
|
|||
_fish_field.gui_input.connect(_on_fish_field_gui_input)
|
||||
_apply_bag_styles()
|
||||
_bag_item_field.gui_input.connect(_on_bag_field_gui_input)
|
||||
_apply_logbook_styles()
|
||||
_apply_mail_notification_style()
|
||||
_logbook_previous.pressed.connect(_request_logbook_page.bind(-1))
|
||||
_logbook_next.pressed.connect(_request_logbook_page.bind(1))
|
||||
resized.connect(_update_shell_layout)
|
||||
_show_section_immediate(_current_section)
|
||||
call_deferred("_update_shell_layout")
|
||||
|
|
@ -456,6 +454,7 @@ func setup(
|
|||
)
|
||||
_profile_page.setup(network_profile_service)
|
||||
_players_page.setup(network_player_list)
|
||||
_catalog_logbook.setup(collection_log, inventory, catalog)
|
||||
_network_mail_service.unread_count_changed.connect(
|
||||
_on_mail_unread_count_changed
|
||||
)
|
||||
|
|
@ -475,8 +474,6 @@ func setup(
|
|||
_fish_selection.clear()
|
||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
||||
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
||||
if not _wallet.balance_changed.is_connected(_on_wallet_balance_changed):
|
||||
_wallet.balance_changed.connect(_on_wallet_balance_changed)
|
||||
if not _fishing_spot.bite_activated.is_connected(_on_bite_activated):
|
||||
|
|
@ -495,7 +492,7 @@ func setup(
|
|||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.echo:
|
||||
return
|
||||
if _handle_inventory_shortcut(event):
|
||||
if _handle_direct_page_shortcut(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
if visible:
|
||||
|
|
@ -513,24 +510,32 @@ func _input(event: InputEvent) -> void:
|
|||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _handle_inventory_shortcut(event: InputEvent) -> bool:
|
||||
func _handle_direct_page_shortcut(event: InputEvent) -> bool:
|
||||
var key_event := event as InputEventKey
|
||||
if (
|
||||
key_event == null
|
||||
or not key_event.pressed
|
||||
or _is_text_input_active()
|
||||
or _shortcut_blocked_by_modal_state()
|
||||
):
|
||||
return false
|
||||
var physical_key: Key = key_event.physical_keycode
|
||||
var inventory_requested: bool = physical_key == KEY_I
|
||||
var logbook_requested: bool = physical_key == KEY_L
|
||||
var tackle_requested: bool = (
|
||||
physical_key == KEY_V
|
||||
or event.is_action_pressed("open_tacklebox")
|
||||
)
|
||||
if not inventory_requested and not tackle_requested:
|
||||
if not inventory_requested and not tackle_requested and not logbook_requested:
|
||||
return false
|
||||
var requested_section: Section = (
|
||||
Section.TACKLE_BOX if tackle_requested else _last_inventory_section
|
||||
Section.LOGBOOK
|
||||
if logbook_requested
|
||||
else (
|
||||
Section.TACKLE_BOX
|
||||
if tackle_requested
|
||||
else _last_inventory_section
|
||||
)
|
||||
)
|
||||
if visible:
|
||||
if (
|
||||
|
|
@ -552,6 +557,20 @@ func _handle_inventory_shortcut(event: InputEvent) -> bool:
|
|||
return true
|
||||
|
||||
|
||||
func _shortcut_blocked_by_modal_state() -> bool:
|
||||
return (
|
||||
_sale_confirmation.visible
|
||||
or (
|
||||
_current_section == Section.MAIL
|
||||
and _mail_page.is_composing_letter()
|
||||
)
|
||||
or (
|
||||
_current_section == Section.PROFILE
|
||||
and _profile_page.has_modal_confirmation()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _is_text_input_active() -> bool:
|
||||
var focus_owner: Control = get_viewport().gui_get_focus_owner()
|
||||
return (
|
||||
|
|
@ -729,7 +748,6 @@ func _show_section_immediate(section: Section) -> void:
|
|||
_content_shell.visible = false
|
||||
_inventory_section.visible = false
|
||||
_bag_section.visible = false
|
||||
_logbook_section.visible = section == Section.LOGBOOK
|
||||
_content_shell.set_background_visible(true)
|
||||
_header.visible = section != Section.COOLER
|
||||
_separator.visible = section != Section.COOLER
|
||||
|
|
@ -745,6 +763,9 @@ func _show_section_immediate(section: Section) -> void:
|
|||
_refresh_tackle_box()
|
||||
if section != Section.LOGBOOK:
|
||||
_cancel_logbook_page_transition(true)
|
||||
_catalog_logbook.deactivate()
|
||||
else:
|
||||
_catalog_logbook.activate()
|
||||
if section == Section.MAIL:
|
||||
_mail_page.activate()
|
||||
else:
|
||||
|
|
@ -788,7 +809,7 @@ func _focus_current_section() -> void:
|
|||
elif _current_section == Section.TACKLE_BOX:
|
||||
_tackle_sub_tab.grab_focus()
|
||||
elif _current_section == Section.LOGBOOK:
|
||||
_logbook_tab.grab_focus()
|
||||
_catalog_logbook.focus_initial()
|
||||
elif _current_section == Section.MAIL:
|
||||
_mail_tab.grab_focus()
|
||||
elif _current_section == Section.PLAYERS:
|
||||
|
|
@ -812,8 +833,6 @@ func _process(delta: float) -> void:
|
|||
_motion_elapsed,
|
||||
bag_motion_enabled,
|
||||
)
|
||||
if _current_section == Section.LOGBOOK:
|
||||
_advance_logbook_page_controls(delta)
|
||||
|
||||
|
||||
func _configure_navigation_focus() -> void:
|
||||
|
|
@ -899,7 +918,7 @@ func _configure_active_page_focus() -> void:
|
|||
Section.TACKLE_BOX:
|
||||
_bait_filter.grab_focus()
|
||||
Section.LOGBOOK:
|
||||
_configure_logbook_focus()
|
||||
_catalog_logbook.focus_initial()
|
||||
Section.MAIL:
|
||||
_mail_page.activate()
|
||||
Section.PROFILE:
|
||||
|
|
@ -1268,7 +1287,6 @@ func _set_navigation_target(section: Section) -> void:
|
|||
func _update_shell_layout() -> void:
|
||||
if not is_node_ready():
|
||||
return
|
||||
_cancel_logbook_page_transition(true)
|
||||
var compact: bool = false
|
||||
_compact_layout = compact
|
||||
var reference_size := DESKTOP_REFERENCE_SIZE
|
||||
|
|
@ -1468,45 +1486,6 @@ func _update_shell_layout() -> void:
|
|||
_players_page.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||
_players_page.position = Vector2.ZERO
|
||||
_players_page.size = reference_size
|
||||
_book_backing.position = (
|
||||
Vector2(14.0, 164.0) if compact else Vector2(58.0, 128.0)
|
||||
)
|
||||
_book_backing.size = (
|
||||
Vector2(612.0, 232.0) if compact else Vector2(1164.0, 522.0)
|
||||
)
|
||||
_right_page.visible = not compact
|
||||
_book_gutter.visible = not compact
|
||||
_left_entry_field.vertical = not compact
|
||||
_right_entry_field.vertical = true
|
||||
_left_heading.text = "field notes" if not compact else "field notes • page"
|
||||
_left_heading.add_theme_font_size_override(
|
||||
"font_size",
|
||||
17 if compact else 20,
|
||||
)
|
||||
_right_heading.add_theme_font_size_override("font_size", 20)
|
||||
_logbook_empty_state.position = (
|
||||
Vector2(120.0, 240.0) if compact else Vector2(402.0, 314.0)
|
||||
)
|
||||
_logbook_empty_state.size = (
|
||||
Vector2(400.0, 52.0) if compact else Vector2(476.0, 52.0)
|
||||
)
|
||||
_logbook_previous.apply_layout(
|
||||
Vector2(58.0, 332.0) if compact else Vector2(116.0, 622.0),
|
||||
Vector2(66.0, 62.0) if compact else Vector2(76.0, 72.0),
|
||||
0.18,
|
||||
)
|
||||
_logbook_next.apply_layout(
|
||||
Vector2(582.0, 332.0) if compact else Vector2(1164.0, 622.0),
|
||||
Vector2(66.0, 62.0) if compact else Vector2(76.0, 72.0),
|
||||
0.18,
|
||||
)
|
||||
_logbook_page_status.position = (
|
||||
Vector2(255.0, 308.0) if compact else Vector2(555.0, 608.0)
|
||||
)
|
||||
_logbook_page_status.size = (
|
||||
Vector2(130.0, 46.0) if compact else Vector2(170.0, 56.0)
|
||||
)
|
||||
_refresh_logbook_page(false)
|
||||
_sale_confirmation.position = (
|
||||
Vector2(70.0, 128.0) if compact else Vector2(320.0, 200.0)
|
||||
)
|
||||
|
|
@ -1518,7 +1497,6 @@ func _update_shell_layout() -> void:
|
|||
512.0 if compact else 420.0,
|
||||
)
|
||||
_bag_grid.columns = 2 if compact else 3
|
||||
_logbook_grid.columns = 3 if compact else 4
|
||||
_bag_list.custom_minimum_size.x = 300.0 if compact else 360.0
|
||||
_bag_detail.custom_minimum_size.x = 176.0 if compact else 220.0
|
||||
_content_stage.custom_minimum_size.y = 220.0 if compact else 260.0
|
||||
|
|
@ -1986,6 +1964,9 @@ func _set_content_interactive(interactive: bool) -> void:
|
|||
if interactive and _current_section == Section.LOGBOOK
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
_catalog_logbook.set_interactive(
|
||||
interactive and _current_section == Section.LOGBOOK
|
||||
)
|
||||
_mail_page.set_interactive(
|
||||
interactive and _current_section == Section.MAIL
|
||||
)
|
||||
|
|
@ -2040,12 +2021,6 @@ func _set_content_interactive(interactive: bool) -> void:
|
|||
if bag_interactive
|
||||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
var logbook_interactive: bool = (
|
||||
interactive
|
||||
and _current_section == Section.LOGBOOK
|
||||
and not _logbook_page_transitioning
|
||||
)
|
||||
_update_logbook_page_control_state(logbook_interactive)
|
||||
|
||||
|
||||
func _cancel_presentation_tween() -> void:
|
||||
|
|
@ -2128,7 +2103,6 @@ func _refresh_all() -> void:
|
|||
_refresh_economy_summary()
|
||||
_refresh_inventory()
|
||||
_refresh_bag()
|
||||
_refresh_logbook()
|
||||
|
||||
|
||||
func _on_bag_changed() -> void:
|
||||
|
|
@ -2447,11 +2421,6 @@ func _on_inventory_changed() -> void:
|
|||
_refresh_economy_summary()
|
||||
_revalidate_confirmation()
|
||||
_refresh_inventory()
|
||||
_refresh_logbook()
|
||||
|
||||
|
||||
func _on_fish_discovered(_fish_id: StringName) -> void:
|
||||
_refresh_logbook()
|
||||
|
||||
|
||||
func _on_wallet_balance_changed(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=22 format=3]
|
||||
[gd_scene load_steps=23 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/player_menu.gd" id="1_menu"]
|
||||
[ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"]
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
[ext_resource type="Script" path="res://ui/mail_page.gd" id="18_mail_page"]
|
||||
[ext_resource type="PackedScene" path="res://ui/profile_page.tscn" id="19_profile_page"]
|
||||
[ext_resource type="Script" path="res://ui/players_page.gd" id="20_players_page"]
|
||||
[ext_resource type="PackedScene" path="res://ui/logbook_page.tscn" id="21_logbook_page"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_collection"]
|
||||
|
||||
|
|
@ -694,6 +695,9 @@ offset_right = 1280.0
|
|||
offset_bottom = 720.0
|
||||
mouse_filter = 1
|
||||
|
||||
[node name="CatalogLogbook" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage" instance=ExtResource("21_logbook_page")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="MailPage" type="Control" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
|
|
@ -721,6 +725,7 @@ script = ExtResource("20_players_page")
|
|||
|
||||
[node name="BookBacking" type="PanelContainer" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 58.0
|
||||
offset_top = 128.0
|
||||
|
|
@ -838,6 +843,7 @@ vertical_alignment = 1
|
|||
|
||||
[node name="LogbookPrevious" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(76, 72)
|
||||
layout_mode = 0
|
||||
offset_left = 78.0
|
||||
|
|
@ -859,6 +865,7 @@ deformation_amplitude = 0.006
|
|||
|
||||
[node name="LogbookNext" type="Button" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(76, 72)
|
||||
layout_mode = 0
|
||||
offset_left = 1126.0
|
||||
|
|
@ -881,6 +888,7 @@ deformation_amplitude = 0.006
|
|||
|
||||
[node name="LogbookPageStatus" parent="ResponsivePlayerMenuStage/PlayerMenuPresentationScaleRoot/LogbookPage" instance=ExtResource("10_status_scene")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 555.0
|
||||
offset_top = 524.0
|
||||
|
|
|
|||
|
|
@ -100,6 +100,13 @@ func request_close_confirmation() -> bool:
|
|||
return true
|
||||
|
||||
|
||||
func has_modal_confirmation() -> bool:
|
||||
return (
|
||||
_discard_confirmation != null
|
||||
and _discard_confirmation.visible
|
||||
)
|
||||
|
||||
|
||||
func _build_ui() -> void:
|
||||
var paper := PanelContainer.new()
|
||||
paper.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue