Improve Logbook presentation
This commit is contained in:
parent
984f7c641c
commit
c956301194
5 changed files with 578 additions and 107 deletions
|
|
@ -5,6 +5,12 @@ const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const FishDataType = preload("res://fish/fish_data.gd")
|
const FishDataType = preload("res://fish/fish_data.gd")
|
||||||
|
const LogbookPortraitType = preload(
|
||||||
|
"res://ui/components/logbook_portrait.gd"
|
||||||
|
)
|
||||||
|
const InventoryNotepadType = preload(
|
||||||
|
"res://ui/components/inventory_notepad.gd"
|
||||||
|
)
|
||||||
const LogbookPageScene = preload("res://ui/logbook_page.tscn")
|
const LogbookPageScene = preload("res://ui/logbook_page.tscn")
|
||||||
const CatalogResource: FishPoolType = preload(
|
const CatalogResource: FishPoolType = preload(
|
||||||
"res://fish/pools/fish_catalog.tres"
|
"res://fish/pools/fish_catalog.tres"
|
||||||
|
|
@ -48,6 +54,12 @@ func _validate_catalog() -> void:
|
||||||
for index: int in expected_ids.size():
|
for index: int in expected_ids.size():
|
||||||
var fish := CatalogResource.get_fish_by_id(expected_ids[index])
|
var fish := CatalogResource.get_fish_by_id(expected_ids[index])
|
||||||
assert(fish != null)
|
assert(fish != null)
|
||||||
|
assert(LogbookCatalog.facts_for(fish.id) != "unknown")
|
||||||
|
assert(not LogbookCatalog.facts_for(fish.id).is_empty())
|
||||||
|
assert(
|
||||||
|
LogbookCatalog.facts_for(fish.id)
|
||||||
|
== LogbookCatalog.facts_for(fish.id).to_lower()
|
||||||
|
)
|
||||||
var expected_category: WaterType.Type = (
|
var expected_category: WaterType.Type = (
|
||||||
WaterType.Type.SALT_WATER
|
WaterType.Type.SALT_WATER
|
||||||
if expected_ids[index] in [&"bass", &"sunfish"]
|
if expected_ids[index] in [&"bass", &"sunfish"]
|
||||||
|
|
@ -75,6 +87,14 @@ func _validate_page() -> void:
|
||||||
page.setup(collection, inventory, CatalogResource)
|
page.setup(collection, inventory, CatalogResource)
|
||||||
page.activate()
|
page.activate()
|
||||||
await process_frame
|
await process_frame
|
||||||
|
assert(page.get("_category") == WaterType.Type.FRESH_WATER)
|
||||||
|
assert((page.get("_catalog_grid") as GridContainer).columns == 5)
|
||||||
|
var initial_detail_body := page.get("_detail_body") as VBoxContainer
|
||||||
|
assert(not initial_detail_body.get_parent() is ScrollContainer)
|
||||||
|
assert(
|
||||||
|
page.call("_entry_label_text", "flathead catfish")
|
||||||
|
== "flathead\ncatfish"
|
||||||
|
)
|
||||||
|
|
||||||
var shared_material: Material
|
var shared_material: Material
|
||||||
var silhouette_count: int = 0
|
var silhouette_count: int = 0
|
||||||
|
|
@ -101,9 +121,13 @@ func _validate_page() -> void:
|
||||||
assert(entry.icon == null)
|
assert(entry.icon == null)
|
||||||
assert(entry.accessibility_name == "Unknown catalog entry")
|
assert(entry.accessibility_name == "Unknown catalog entry")
|
||||||
assert(_has_visible_unknown_name(entry))
|
assert(_has_visible_unknown_name(entry))
|
||||||
var portrait := _find_unknown_portrait(entry)
|
var portrait: LogbookPortraitType = _find_portrait(entry)
|
||||||
assert(portrait != null)
|
assert(portrait != null)
|
||||||
assert(portrait.texture == fish.display_texture)
|
assert(portrait.source_texture == fish.display_texture)
|
||||||
|
assert(
|
||||||
|
portrait.custom_minimum_size
|
||||||
|
== LogbookPortraitType.ENTRY_FRAME_SIZE
|
||||||
|
)
|
||||||
assert(
|
assert(
|
||||||
portrait.expand_mode
|
portrait.expand_mode
|
||||||
== TextureRect.EXPAND_IGNORE_SIZE
|
== TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
|
@ -154,10 +178,17 @@ func _validate_page() -> void:
|
||||||
var entries: Dictionary = page.get("_entry_buttons")
|
var entries: Dictionary = page.get("_entry_buttons")
|
||||||
var known := entries.get(&"bluegill") as Button
|
var known := entries.get(&"bluegill") as Button
|
||||||
assert(known != null)
|
assert(known != null)
|
||||||
assert(known.text == bluegill.display_name)
|
assert(known.custom_minimum_size.x == known.custom_minimum_size.y)
|
||||||
assert(known.icon == bluegill.display_texture)
|
_validate_entry_bubble_styles(known)
|
||||||
assert(_find_unknown_portrait(known) == null)
|
assert(known.text.is_empty())
|
||||||
|
assert(known.icon == null)
|
||||||
|
assert(_has_visible_name(known, bluegill.display_name))
|
||||||
|
var known_portrait: LogbookPortraitType = _find_portrait(known)
|
||||||
|
assert(known_portrait != null)
|
||||||
|
assert(known_portrait.source_texture == bluegill.display_texture)
|
||||||
|
assert(known_portrait.material == null)
|
||||||
assert(known.button_pressed)
|
assert(known.button_pressed)
|
||||||
|
_validate_handwritten_logbook_font(page)
|
||||||
|
|
||||||
var fish_catch := FishCatchType.new()
|
var fish_catch := FishCatchType.new()
|
||||||
fish_catch.fish = bluegill
|
fish_catch.fish = bluegill
|
||||||
|
|
@ -174,9 +205,30 @@ func _validate_page() -> void:
|
||||||
page.call("_select_entry", &"bluegill", &"bluegill")
|
page.call("_select_entry", &"bluegill", &"bluegill")
|
||||||
await process_frame
|
await process_frame
|
||||||
assert(_detail_text(page).contains("number owned\n1"))
|
assert(_detail_text(page).contains("number owned\n1"))
|
||||||
assert(_detail_text(page).contains("number caught\nUnknown"))
|
assert(_detail_text(page).contains("number caught\nunknown"))
|
||||||
assert(_detail_text(page).contains("body of water\nFresh Water"))
|
assert(_detail_text(page).contains("body of water\nfresh water"))
|
||||||
assert(_detail_text(page).contains("catalog number\n#001"))
|
assert(_detail_text(page).contains("catalog number\n#001"))
|
||||||
|
assert(_detail_text(page).contains("fish facts\n"))
|
||||||
|
assert(_detail_text(page).contains(LogbookCatalog.facts_for(&"bluegill")))
|
||||||
|
_validate_detail_field_fonts(page)
|
||||||
|
_validate_handwritten_numeric_scale(page)
|
||||||
|
var portrait_button := page.get("_detail_portrait_button") as Button
|
||||||
|
assert(portrait_button != null)
|
||||||
|
portrait_button.pressed.emit()
|
||||||
|
await process_frame
|
||||||
|
var portrait_overlay := page.get("_portrait_overlay") as Control
|
||||||
|
assert(portrait_overlay.visible)
|
||||||
|
var overlay_artwork := (
|
||||||
|
page.get("_portrait_overlay_artwork") as LogbookPortraitType
|
||||||
|
)
|
||||||
|
assert(overlay_artwork.source_texture == bluegill.display_texture)
|
||||||
|
assert(
|
||||||
|
overlay_artwork.custom_minimum_size.x <= 860.0
|
||||||
|
and overlay_artwork.custom_minimum_size.y <= 480.0
|
||||||
|
)
|
||||||
|
(page.get("_portrait_overlay_backdrop") as Button).pressed.emit()
|
||||||
|
await process_frame
|
||||||
|
assert(not portrait_overlay.visible)
|
||||||
|
|
||||||
inventory.remove_catch_by_id(fish_catch.catch_id)
|
inventory.remove_catch_by_id(fish_catch.catch_id)
|
||||||
await process_frame
|
await process_frame
|
||||||
|
|
@ -194,26 +246,110 @@ func _detail_text(page: LogbookPage) -> String:
|
||||||
return "\n".join(values)
|
return "\n".join(values)
|
||||||
|
|
||||||
|
|
||||||
func _find_unknown_portrait(entry: Button) -> TextureRect:
|
func _find_portrait(entry: Button) -> LogbookPortraitType:
|
||||||
var pending: Array[Node] = [entry]
|
var pending: Array[Node] = [entry]
|
||||||
while not pending.is_empty():
|
while not pending.is_empty():
|
||||||
var node: Node = pending.pop_back()
|
var node: Node = pending.pop_back()
|
||||||
if node is TextureRect:
|
if node is LogbookPortraitType:
|
||||||
return node as TextureRect
|
return node as LogbookPortraitType
|
||||||
pending.append_array(node.get_children())
|
pending.append_array(node.get_children())
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
func _has_visible_unknown_name(entry: Button) -> bool:
|
func _has_visible_unknown_name(entry: Button) -> bool:
|
||||||
|
return _has_visible_name(entry, "???")
|
||||||
|
|
||||||
|
|
||||||
|
func _has_visible_name(entry: Button, expected_name: String) -> bool:
|
||||||
var pending: Array[Node] = [entry]
|
var pending: Array[Node] = [entry]
|
||||||
while not pending.is_empty():
|
while not pending.is_empty():
|
||||||
var node: Node = pending.pop_back()
|
var node: Node = pending.pop_back()
|
||||||
if node is Label and (node as Label).text == "???":
|
if node is Label and (node as Label).text == expected_name:
|
||||||
return true
|
return true
|
||||||
pending.append_array(node.get_children())
|
pending.append_array(node.get_children())
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_handwritten_logbook_font(page: LogbookPage) -> void:
|
||||||
|
var detail_body := page.get("_detail_body") as VBoxContainer
|
||||||
|
for node: Node in detail_body.find_children("*", "Label", true, false):
|
||||||
|
var label := node as Label
|
||||||
|
if label.text in [
|
||||||
|
"fish facts",
|
||||||
|
"catalog number",
|
||||||
|
"rarity",
|
||||||
|
"body of water",
|
||||||
|
"time of day",
|
||||||
|
"weight range",
|
||||||
|
"value range",
|
||||||
|
"number caught",
|
||||||
|
"number owned",
|
||||||
|
]:
|
||||||
|
continue
|
||||||
|
assert(
|
||||||
|
label.get_theme_font("font")
|
||||||
|
== InventoryNotepadType.HANDWRITTEN_FONT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_detail_field_fonts(page: LogbookPage) -> void:
|
||||||
|
var detail_body := page.get("_detail_body") as VBoxContainer
|
||||||
|
var field_names: Array[String] = [
|
||||||
|
"fish facts",
|
||||||
|
"catalog number",
|
||||||
|
"rarity",
|
||||||
|
"body of water",
|
||||||
|
"time of day",
|
||||||
|
"weight range",
|
||||||
|
"value range",
|
||||||
|
"number caught",
|
||||||
|
"number owned",
|
||||||
|
]
|
||||||
|
for node: Node in detail_body.find_children("*", "Label", true, false):
|
||||||
|
var label := node as Label
|
||||||
|
if label.text not in field_names:
|
||||||
|
continue
|
||||||
|
assert(label.get_theme_font("font") == UtilityPageStyle.TuffyFont)
|
||||||
|
if label.text != "fish facts":
|
||||||
|
assert(
|
||||||
|
label.horizontal_alignment
|
||||||
|
== HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_handwritten_numeric_scale(page: LogbookPage) -> void:
|
||||||
|
var detail_body := page.get("_detail_body") as VBoxContainer
|
||||||
|
var text_value: Label
|
||||||
|
var numeric_value: Label
|
||||||
|
for node: Node in detail_body.find_children("*", "Label", true, false):
|
||||||
|
var label := node as Label
|
||||||
|
if label.text == "common":
|
||||||
|
text_value = label
|
||||||
|
elif label.text == "#001":
|
||||||
|
numeric_value = label
|
||||||
|
assert(text_value != null)
|
||||||
|
assert(numeric_value != null)
|
||||||
|
assert(
|
||||||
|
numeric_value.get_theme_font_size("font_size")
|
||||||
|
< text_value.get_theme_font_size("font_size")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_entry_bubble_styles(entry: Button) -> void:
|
||||||
|
var normal := entry.get_theme_stylebox("normal") as StyleBoxFlat
|
||||||
|
var hover := entry.get_theme_stylebox("hover") as StyleBoxFlat
|
||||||
|
var pressed := entry.get_theme_stylebox("pressed") as StyleBoxFlat
|
||||||
|
assert(normal != null)
|
||||||
|
assert(hover != null)
|
||||||
|
assert(pressed != null)
|
||||||
|
assert(is_zero_approx(normal.bg_color.a))
|
||||||
|
assert(hover.bg_color.a > 0.0)
|
||||||
|
assert(pressed.bg_color.a > 0.0)
|
||||||
|
assert(hover.border_width_left == 0)
|
||||||
|
assert(pressed.border_width_left == 0)
|
||||||
|
assert(hover.corner_radius_top_left == 51)
|
||||||
|
|
||||||
|
|
||||||
func _texture_has_transparency(texture: Texture2D) -> bool:
|
func _texture_has_transparency(texture: Texture2D) -> bool:
|
||||||
var image: Image = texture.get_image()
|
var image: Image = texture.get_image()
|
||||||
return image != null and image.detect_alpha() != Image.ALPHA_NONE
|
return image != null and image.detect_alpha() != Image.ALPHA_NONE
|
||||||
|
|
|
||||||
79
ui/components/logbook_portrait.gd
Normal file
79
ui/components/logbook_portrait.gd
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
class_name LogbookPortrait
|
||||||
|
extends TextureRect
|
||||||
|
|
||||||
|
const ENTRY_FRAME_SIZE := Vector2(86.0, 40.0)
|
||||||
|
const DETAIL_FRAME_SIZE := Vector2(240.0, 132.0)
|
||||||
|
|
||||||
|
static var _normalized_textures: Dictionary[String, Texture2D] = {}
|
||||||
|
|
||||||
|
var source_texture: Texture2D
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||||
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
focus_mode = Control.FOCUS_NONE
|
||||||
|
size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||||
|
size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||||
|
|
||||||
|
|
||||||
|
func configure(
|
||||||
|
portrait_texture: Texture2D,
|
||||||
|
frame_size: Vector2,
|
||||||
|
portrait_material: Material = null,
|
||||||
|
) -> void:
|
||||||
|
source_texture = portrait_texture
|
||||||
|
custom_minimum_size = frame_size
|
||||||
|
material = portrait_material
|
||||||
|
texture = _normalize_visible_bounds(portrait_texture)
|
||||||
|
|
||||||
|
|
||||||
|
func configure_fitted(
|
||||||
|
portrait_texture: Texture2D,
|
||||||
|
maximum_size: Vector2,
|
||||||
|
portrait_material: Material = null,
|
||||||
|
) -> void:
|
||||||
|
source_texture = portrait_texture
|
||||||
|
material = portrait_material
|
||||||
|
texture = _normalize_visible_bounds(portrait_texture)
|
||||||
|
if texture == null:
|
||||||
|
custom_minimum_size = Vector2.ZERO
|
||||||
|
return
|
||||||
|
var texture_size: Vector2 = texture.get_size()
|
||||||
|
if texture_size.x <= 0.0 or texture_size.y <= 0.0:
|
||||||
|
custom_minimum_size = Vector2.ZERO
|
||||||
|
return
|
||||||
|
var fit_scale: float = minf(
|
||||||
|
maximum_size.x / texture_size.x,
|
||||||
|
maximum_size.y / texture_size.y,
|
||||||
|
)
|
||||||
|
custom_minimum_size = texture_size * fit_scale
|
||||||
|
|
||||||
|
|
||||||
|
static func _normalize_visible_bounds(
|
||||||
|
portrait_texture: Texture2D,
|
||||||
|
) -> Texture2D:
|
||||||
|
if portrait_texture == null:
|
||||||
|
return null
|
||||||
|
var cache_key: String = portrait_texture.resource_path
|
||||||
|
if cache_key.is_empty():
|
||||||
|
cache_key = "instance_%d" % portrait_texture.get_instance_id()
|
||||||
|
var cached: Texture2D = _normalized_textures.get(cache_key) as Texture2D
|
||||||
|
if cached != null:
|
||||||
|
return cached
|
||||||
|
var image: Image = portrait_texture.get_image()
|
||||||
|
if image == null or image.is_empty():
|
||||||
|
_normalized_textures[cache_key] = portrait_texture
|
||||||
|
return portrait_texture
|
||||||
|
var full_rect := Rect2i(Vector2i.ZERO, image.get_size())
|
||||||
|
var visible_rect: Rect2i = image.get_used_rect()
|
||||||
|
if visible_rect.size == Vector2i.ZERO or visible_rect == full_rect:
|
||||||
|
_normalized_textures[cache_key] = portrait_texture
|
||||||
|
return portrait_texture
|
||||||
|
var normalized := AtlasTexture.new()
|
||||||
|
normalized.atlas = portrait_texture
|
||||||
|
normalized.region = Rect2(visible_rect)
|
||||||
|
_normalized_textures[cache_key] = normalized
|
||||||
|
return normalized
|
||||||
1
ui/components/logbook_portrait.gd.uid
Normal file
1
ui/components/logbook_portrait.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://qkduawll2pm2
|
||||||
|
|
@ -16,6 +16,44 @@ const CATALOG_ORDER: Array[StringName] = [
|
||||||
&"catfish_white",
|
&"catfish_white",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Short presentation notes are kept here with the catalog contract rather
|
||||||
|
# than mixed into gameplay balance resources. Add facts deliberately when a
|
||||||
|
# species joins the catalog.
|
||||||
|
const FISH_FACTS: Dictionary[StringName, String] = {
|
||||||
|
&"bluegill": (
|
||||||
|
"bluegill fathers sweep shallow bowls into the bottom for nests, "
|
||||||
|
+ "then stand guard. whole neighborhoods of nests can crowd together."
|
||||||
|
),
|
||||||
|
&"bass": (
|
||||||
|
"striped bass split their lives between salt and fresh water, "
|
||||||
|
+ "returning upriver to spawn. a long-lived striper may see three decades."
|
||||||
|
),
|
||||||
|
&"carp": (
|
||||||
|
"whisker-like barbels help a common carp investigate the bottom. "
|
||||||
|
+ "nosing through sediment can cloud the water and loosen plants."
|
||||||
|
),
|
||||||
|
&"sunfish": (
|
||||||
|
"ocean sunfish are built more like enormous swimming heads than "
|
||||||
|
+ "ordinary fish. they visit the surface and turn sideways to warm up."
|
||||||
|
),
|
||||||
|
&"catfish_blue": (
|
||||||
|
"a deep fork in the tail inspired the blue catfish's scientific "
|
||||||
|
+ "name. it rests deep by daylight and becomes more active after dark."
|
||||||
|
),
|
||||||
|
&"catfish_channel": (
|
||||||
|
"a channel catfish can taste through skin all over its body, "
|
||||||
|
+ "especially near its gills and whiskers. the male watches the eggs."
|
||||||
|
),
|
||||||
|
&"catfish_flathead": (
|
||||||
|
"flatheads favor hideouts made by logs, rocks, ledges, and other "
|
||||||
|
+ "underwater structure. as they grow, fish dominate their menu."
|
||||||
|
),
|
||||||
|
&"catfish_white": (
|
||||||
|
"white catfish began along the atlantic coast between new york "
|
||||||
|
+ "and florida. people later carried them far beyond that home range."
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
static func category_for(fish: FishDataType) -> WaterType.Type:
|
static func category_for(fish: FishDataType) -> WaterType.Type:
|
||||||
if fish == null:
|
if fish == null:
|
||||||
return WaterType.Type.OTHER
|
return WaterType.Type.OTHER
|
||||||
|
|
@ -41,6 +79,10 @@ static func catalog_number(fish_id: StringName) -> int:
|
||||||
return index + 1 if index >= 0 else 0
|
return index + 1 if index >= 0 else 0
|
||||||
|
|
||||||
|
|
||||||
|
static func facts_for(fish_id: StringName) -> String:
|
||||||
|
return str(FISH_FACTS.get(fish_id, "unknown"))
|
||||||
|
|
||||||
|
|
||||||
static func ordered_species(
|
static func ordered_species(
|
||||||
candidates: Array[FishDataType],
|
candidates: Array[FishDataType],
|
||||||
) -> Array[FishDataType]:
|
) -> Array[FishDataType]:
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,17 @@ const SILHOUETTE_SHADER: Shader = preload(
|
||||||
"res://ui/logbook_silhouette.gdshader"
|
"res://ui/logbook_silhouette.gdshader"
|
||||||
)
|
)
|
||||||
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
|
const OrganizerTabType = preload("res://ui/components/organizer_tab.gd")
|
||||||
|
const InventoryNotepadType = preload(
|
||||||
|
"res://ui/components/inventory_notepad.gd"
|
||||||
|
)
|
||||||
|
const LogbookPortraitType = preload(
|
||||||
|
"res://ui/components/logbook_portrait.gd"
|
||||||
|
)
|
||||||
|
|
||||||
const DETAIL_FADE_DURATION: float = 0.12
|
const DETAIL_FADE_DURATION: float = 0.12
|
||||||
const CATEGORY_FADE_DURATION: float = UIMotion.UTILITY_EXIT_DURATION
|
const CATEGORY_FADE_DURATION: float = UIMotion.UTILITY_EXIT_DURATION
|
||||||
|
const HANDWRITTEN_NUMERIC_SCALE: float = 0.8
|
||||||
|
const PORTRAIT_VIEW_MAX_SIZE := Vector2(860.0, 480.0)
|
||||||
const INK := Color("251b10")
|
const INK := Color("251b10")
|
||||||
const MUTED_INK := Color("6d5b45")
|
const MUTED_INK := Color("6d5b45")
|
||||||
const PAPER := Color("f2e6c9")
|
const PAPER := Color("f2e6c9")
|
||||||
|
|
@ -21,7 +29,7 @@ const LOGBOOK_TAB_LEFT_INSET: float = 34.0
|
||||||
var _collection_log: CollectionLogType
|
var _collection_log: CollectionLogType
|
||||||
var _inventory: FishInventoryType
|
var _inventory: FishInventoryType
|
||||||
var _catalog: FishPoolType
|
var _catalog: FishPoolType
|
||||||
var _category: WaterType.Type = WaterType.Type.OTHER
|
var _category: WaterType.Type = WaterType.Type.FRESH_WATER
|
||||||
var _selected_id: StringName
|
var _selected_id: StringName
|
||||||
var _selected_entry_key: StringName
|
var _selected_entry_key: StringName
|
||||||
var _active: bool = false
|
var _active: bool = false
|
||||||
|
|
@ -38,14 +46,16 @@ var _catalog_scroll: ScrollContainer
|
||||||
var _catalog_grid: GridContainer
|
var _catalog_grid: GridContainer
|
||||||
var _empty_state: Label
|
var _empty_state: Label
|
||||||
var _detail_body: VBoxContainer
|
var _detail_body: VBoxContainer
|
||||||
|
var _detail_portrait_button: Button
|
||||||
|
var _portrait_overlay: Control
|
||||||
|
var _portrait_overlay_backdrop: Button
|
||||||
|
var _portrait_overlay_artwork: LogbookPortraitType
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_silhouette_material = ShaderMaterial.new()
|
_silhouette_material = ShaderMaterial.new()
|
||||||
_silhouette_material.shader = SILHOUETTE_SHADER
|
_silhouette_material.shader = SILHOUETTE_SHADER
|
||||||
_build_interface()
|
_build_interface()
|
||||||
resized.connect(_update_responsive_layout)
|
|
||||||
_update_responsive_layout()
|
|
||||||
set_interactive(false)
|
set_interactive(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -79,6 +89,7 @@ func activate() -> void:
|
||||||
|
|
||||||
func deactivate() -> void:
|
func deactivate() -> void:
|
||||||
_active = false
|
_active = false
|
||||||
|
_hide_portrait_overlay(false)
|
||||||
set_interactive(false)
|
set_interactive(false)
|
||||||
_settle_tabs_for_close()
|
_settle_tabs_for_close()
|
||||||
_cancel_detail_tween()
|
_cancel_detail_tween()
|
||||||
|
|
@ -115,6 +126,9 @@ func set_interactive(value: bool) -> void:
|
||||||
func focus_initial() -> void:
|
func focus_initial() -> void:
|
||||||
if not _active or not _interactive:
|
if not _active or not _interactive:
|
||||||
return
|
return
|
||||||
|
if _portrait_overlay != null and _portrait_overlay.visible:
|
||||||
|
_portrait_overlay_backdrop.grab_focus()
|
||||||
|
return
|
||||||
var selected := _entry_buttons.get(_selected_id) as Button
|
var selected := _entry_buttons.get(_selected_id) as Button
|
||||||
if selected != null:
|
if selected != null:
|
||||||
selected.grab_focus()
|
selected.grab_focus()
|
||||||
|
|
@ -199,6 +213,7 @@ func _build_interface() -> void:
|
||||||
)
|
)
|
||||||
left_layout.add_child(_catalog_scroll)
|
left_layout.add_child(_catalog_scroll)
|
||||||
_catalog_grid = GridContainer.new()
|
_catalog_grid = GridContainer.new()
|
||||||
|
_catalog_grid.columns = 5
|
||||||
_catalog_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
_catalog_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
_catalog_grid.add_theme_constant_override("h_separation", 8)
|
_catalog_grid.add_theme_constant_override("h_separation", 8)
|
||||||
_catalog_grid.add_theme_constant_override("v_separation", 8)
|
_catalog_grid.add_theme_constant_override("v_separation", 8)
|
||||||
|
|
@ -219,16 +234,13 @@ func _build_interface() -> void:
|
||||||
var right_margin := MarginContainer.new()
|
var right_margin := MarginContainer.new()
|
||||||
_set_margins(right_margin, 22, 16, 22, 16)
|
_set_margins(right_margin, 22, 16, 22, 16)
|
||||||
right_page.add_child(right_margin)
|
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 = VBoxContainer.new()
|
||||||
_detail_body.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
_detail_body.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
_detail_body.add_theme_constant_override("separation", 8)
|
_detail_body.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
details_scroll.add_child(_detail_body)
|
_detail_body.add_theme_constant_override("separation", 16)
|
||||||
|
right_margin.add_child(_detail_body)
|
||||||
_show_no_selection()
|
_show_no_selection()
|
||||||
|
_build_portrait_overlay()
|
||||||
|
|
||||||
|
|
||||||
func _refresh_catalog() -> void:
|
func _refresh_catalog() -> void:
|
||||||
|
|
@ -284,82 +296,65 @@ func _refresh_catalog() -> void:
|
||||||
|
|
||||||
func _make_entry(fish: FishDataType, discovered: bool) -> Button:
|
func _make_entry(fish: FishDataType, discovered: bool) -> Button:
|
||||||
var entry := Button.new()
|
var entry := Button.new()
|
||||||
entry.custom_minimum_size = Vector2(220, 138)
|
entry.custom_minimum_size = Vector2(102, 102)
|
||||||
|
entry.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||||||
|
entry.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||||||
entry.toggle_mode = true
|
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("normal", _entry_style(false))
|
||||||
entry.add_theme_stylebox_override("hover", _entry_style(true))
|
entry.add_theme_stylebox_override("hover", _entry_style(true))
|
||||||
|
entry.add_theme_stylebox_override("hover_pressed", _entry_style(true))
|
||||||
entry.add_theme_stylebox_override("focus", _entry_style(true))
|
entry.add_theme_stylebox_override("focus", _entry_style(true))
|
||||||
entry.add_theme_stylebox_override("pressed", _entry_style(true))
|
entry.add_theme_stylebox_override("pressed", _entry_style(true))
|
||||||
if not discovered:
|
|
||||||
entry.text = ""
|
entry.text = ""
|
||||||
|
if not discovered:
|
||||||
entry.tooltip_text = ""
|
entry.tooltip_text = ""
|
||||||
entry.accessibility_name = "Unknown catalog entry"
|
entry.accessibility_name = "Unknown catalog entry"
|
||||||
_add_unknown_entry_content(entry, fish.display_texture)
|
_add_entry_content(entry, fish.display_texture, "???", true)
|
||||||
else:
|
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.tooltip_text = fish.display_name
|
||||||
entry.accessibility_name = fish.display_name
|
entry.accessibility_name = fish.display_name
|
||||||
|
_add_entry_content(
|
||||||
|
entry, fish.display_texture, fish.display_name, false
|
||||||
|
)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
|
||||||
func _add_unknown_entry_content(
|
func _add_entry_content(
|
||||||
entry: Button,
|
entry: Button,
|
||||||
portrait: Texture2D,
|
portrait: Texture2D,
|
||||||
|
entry_name: String,
|
||||||
|
unknown: bool,
|
||||||
) -> void:
|
) -> void:
|
||||||
var content_margin := MarginContainer.new()
|
var content_margin := MarginContainer.new()
|
||||||
content_margin.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
content_margin.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
content_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
content_margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
content_margin.add_theme_constant_override("margin_left", 7)
|
content_margin.add_theme_constant_override("margin_left", 5)
|
||||||
content_margin.add_theme_constant_override("margin_top", 7)
|
content_margin.add_theme_constant_override("margin_top", 8)
|
||||||
content_margin.add_theme_constant_override("margin_right", 7)
|
content_margin.add_theme_constant_override("margin_right", 5)
|
||||||
content_margin.add_theme_constant_override("margin_bottom", 7)
|
content_margin.add_theme_constant_override("margin_bottom", 2)
|
||||||
entry.add_child(content_margin)
|
entry.add_child(content_margin)
|
||||||
|
|
||||||
var content := VBoxContainer.new()
|
var content := VBoxContainer.new()
|
||||||
content.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
content.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
content.add_theme_constant_override("separation", 2)
|
content.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||||
|
content.add_theme_constant_override("separation", 4)
|
||||||
content_margin.add_child(content)
|
content_margin.add_child(content)
|
||||||
|
|
||||||
var portrait_view := TextureRect.new()
|
var portrait_view := LogbookPortraitType.new()
|
||||||
portrait_view.custom_minimum_size = Vector2(0.0, 64.0)
|
portrait_view.configure(
|
||||||
portrait_view.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
portrait,
|
||||||
portrait_view.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
LogbookPortraitType.ENTRY_FRAME_SIZE,
|
||||||
portrait_view.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
_silhouette_material if unknown else null,
|
||||||
portrait_view.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
)
|
||||||
portrait_view.texture = portrait
|
|
||||||
portrait_view.material = _silhouette_material
|
|
||||||
content.add_child(portrait_view)
|
content.add_child(portrait_view)
|
||||||
|
|
||||||
var unknown_name := Label.new()
|
var name_label := _label(_entry_label_text(entry_name), 16)
|
||||||
unknown_name.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
name_label.custom_minimum_size.y = 38.0
|
||||||
unknown_name.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
unknown_name.text = "???"
|
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
unknown_name.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
unknown_name.add_theme_font_size_override("font_size", 18)
|
name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
unknown_name.add_theme_color_override("font_color", INK)
|
content.add_child(name_label)
|
||||||
content.add_child(unknown_name)
|
|
||||||
|
|
||||||
var discovery_hint := Label.new()
|
|
||||||
discovery_hint.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
||||||
discovery_hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
||||||
discovery_hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
||||||
discovery_hint.text = "Catch this creature to learn more."
|
|
||||||
discovery_hint.add_theme_font_override(
|
|
||||||
"font", UtilityPageStyle.TuffyFont
|
|
||||||
)
|
|
||||||
discovery_hint.add_theme_font_size_override("font_size", 12)
|
|
||||||
discovery_hint.add_theme_color_override("font_color", MUTED_INK)
|
|
||||||
content.add_child(discovery_hint)
|
|
||||||
|
|
||||||
|
|
||||||
func _select_category(category: WaterType.Type) -> void:
|
func _select_category(category: WaterType.Type) -> void:
|
||||||
|
|
@ -499,45 +494,115 @@ func _refresh_details(animate: bool) -> void:
|
||||||
func _build_known_details(fish: FishDataType) -> void:
|
func _build_known_details(fish: FishDataType) -> void:
|
||||||
_clear_details()
|
_clear_details()
|
||||||
var catalog_number: int = LogbookCatalog.catalog_number(fish.id)
|
var catalog_number: int = LogbookCatalog.catalog_number(fish.id)
|
||||||
var title := _label(fish.display_name, 30)
|
var summary_columns := HBoxContainer.new()
|
||||||
|
summary_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
summary_columns.add_theme_constant_override("separation", 24)
|
||||||
|
_detail_body.add_child(summary_columns)
|
||||||
|
|
||||||
|
var portrait_column := VBoxContainer.new()
|
||||||
|
portrait_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
portrait_column.size_flags_stretch_ratio = 1.0
|
||||||
|
portrait_column.add_theme_constant_override("separation", 4)
|
||||||
|
summary_columns.add_child(portrait_column)
|
||||||
|
var title := _label(fish.display_name, 26)
|
||||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
_detail_body.add_child(title)
|
portrait_column.add_child(title)
|
||||||
if fish.display_texture != null:
|
if fish.display_texture != null:
|
||||||
var artwork := TextureRect.new()
|
_detail_portrait_button = Button.new()
|
||||||
artwork.custom_minimum_size = Vector2(0, 150)
|
_detail_portrait_button.custom_minimum_size = (
|
||||||
artwork.texture = fish.display_texture
|
LogbookPortraitType.DETAIL_FRAME_SIZE
|
||||||
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())
|
_detail_portrait_button.size_flags_horizontal = (
|
||||||
_add_detail_row("body of water", WaterType.label(fish.get_primary_water_type()))
|
Control.SIZE_SHRINK_CENTER
|
||||||
_add_detail_row("time of day", _availability_text(fish))
|
)
|
||||||
|
_detail_portrait_button.tooltip_text = "View larger artwork"
|
||||||
|
_detail_portrait_button.accessibility_name = (
|
||||||
|
"View larger artwork for %s" % fish.display_name
|
||||||
|
)
|
||||||
|
_detail_portrait_button.add_theme_stylebox_override(
|
||||||
|
"normal", _portrait_button_style(false)
|
||||||
|
)
|
||||||
|
_detail_portrait_button.add_theme_stylebox_override(
|
||||||
|
"hover", _portrait_button_style(true)
|
||||||
|
)
|
||||||
|
_detail_portrait_button.add_theme_stylebox_override(
|
||||||
|
"focus", _portrait_button_style(true)
|
||||||
|
)
|
||||||
|
_detail_portrait_button.add_theme_stylebox_override(
|
||||||
|
"pressed", _portrait_button_style(true)
|
||||||
|
)
|
||||||
|
_detail_portrait_button.pressed.connect(
|
||||||
|
_show_portrait_overlay.bind(fish.display_texture)
|
||||||
|
)
|
||||||
|
portrait_column.add_child(_detail_portrait_button)
|
||||||
|
var artwork_center := CenterContainer.new()
|
||||||
|
artwork_center.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
artwork_center.set_anchors_and_offsets_preset(
|
||||||
|
Control.PRESET_FULL_RECT
|
||||||
|
)
|
||||||
|
_detail_portrait_button.add_child(artwork_center)
|
||||||
|
var artwork := LogbookPortraitType.new()
|
||||||
|
artwork.configure(
|
||||||
|
fish.display_texture,
|
||||||
|
LogbookPortraitType.DETAIL_FRAME_SIZE,
|
||||||
|
)
|
||||||
|
artwork_center.add_child(artwork)
|
||||||
|
|
||||||
|
var facts_column := VBoxContainer.new()
|
||||||
|
facts_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
facts_column.size_flags_stretch_ratio = 1.0
|
||||||
|
facts_column.add_theme_constant_override("separation", 6)
|
||||||
|
summary_columns.add_child(facts_column)
|
||||||
|
var facts_heading := _field_label("fish facts", 16)
|
||||||
|
facts_column.add_child(facts_heading)
|
||||||
|
var facts := _label(LogbookCatalog.facts_for(fish.id), 16)
|
||||||
|
facts.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
|
facts.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
facts_column.add_child(facts)
|
||||||
|
|
||||||
|
var stats_columns := HBoxContainer.new()
|
||||||
|
stats_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
stats_columns.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
stats_columns.add_theme_constant_override("separation", 28)
|
||||||
|
_detail_body.add_child(stats_columns)
|
||||||
|
var left_stats := _make_stats_column()
|
||||||
|
var right_stats := _make_stats_column()
|
||||||
|
stats_columns.add_child(left_stats)
|
||||||
|
stats_columns.add_child(right_stats)
|
||||||
_add_detail_row(
|
_add_detail_row(
|
||||||
|
left_stats,
|
||||||
|
"catalog number",
|
||||||
|
"#%03d" % catalog_number if catalog_number > 0 else "unknown",
|
||||||
|
)
|
||||||
|
_add_detail_row(
|
||||||
|
left_stats,
|
||||||
|
"body of water",
|
||||||
|
WaterType.label(fish.get_primary_water_type()).to_lower(),
|
||||||
|
)
|
||||||
|
_add_detail_row(
|
||||||
|
left_stats,
|
||||||
"weight range",
|
"weight range",
|
||||||
"%.2f–%.2f lb" % [
|
"%.2f–%.2f lb" % [
|
||||||
fish.get_minimum_weight(),
|
fish.get_minimum_weight(),
|
||||||
fish.get_maximum_weight(),
|
fish.get_maximum_weight(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
_add_detail_row(left_stats, "number caught", "unknown")
|
||||||
|
_add_detail_row(right_stats, "rarity", fish.get_rarity_name().to_lower())
|
||||||
|
_add_detail_row(right_stats, "time of day", _availability_text(fish))
|
||||||
_add_detail_row(
|
_add_detail_row(
|
||||||
|
right_stats,
|
||||||
"value range",
|
"value range",
|
||||||
"%d–%d fish coin" % [
|
"%d–%d fish coin" % [
|
||||||
fish.sell_value_min,
|
fish.sell_value_min,
|
||||||
fish.sell_value_max,
|
fish.sell_value_max,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_add_detail_row("number caught", "Unknown")
|
|
||||||
_add_detail_row(
|
_add_detail_row(
|
||||||
|
right_stats,
|
||||||
"number owned",
|
"number owned",
|
||||||
str(_inventory.get_count(fish.id) if _inventory != null else 0),
|
str(_inventory.get_count(fish.id) if _inventory != null else 0),
|
||||||
)
|
)
|
||||||
_add_detail_row("facts", "Unknown")
|
|
||||||
|
|
||||||
|
|
||||||
func _show_no_selection() -> void:
|
func _show_no_selection() -> void:
|
||||||
|
|
@ -561,28 +626,42 @@ func _show_unknown() -> void:
|
||||||
_detail_body.add_child(instruction)
|
_detail_body.add_child(instruction)
|
||||||
|
|
||||||
|
|
||||||
func _add_detail_row(row_name: String, value: String) -> void:
|
func _add_detail_row(
|
||||||
|
parent: VBoxContainer,
|
||||||
|
row_name: String,
|
||||||
|
value: String,
|
||||||
|
) -> void:
|
||||||
var row := VBoxContainer.new()
|
var row := VBoxContainer.new()
|
||||||
row.add_theme_constant_override("separation", 1)
|
row.add_theme_constant_override("separation", 2)
|
||||||
var heading := _label(row_name, 15)
|
var heading := _field_label(row_name, 14)
|
||||||
|
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
heading.add_theme_color_override("font_color", MUTED_INK)
|
heading.add_theme_color_override("font_color", MUTED_INK)
|
||||||
var content := _label(value, 20)
|
var content := _handwritten_value_label(value.to_lower(), 16)
|
||||||
|
content.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
content.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
content.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
row.add_child(heading)
|
row.add_child(heading)
|
||||||
row.add_child(content)
|
row.add_child(content)
|
||||||
_detail_body.add_child(row)
|
parent.add_child(row)
|
||||||
|
|
||||||
|
|
||||||
|
func _make_stats_column() -> VBoxContainer:
|
||||||
|
var column := VBoxContainer.new()
|
||||||
|
column.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
column.size_flags_stretch_ratio = 1.0
|
||||||
|
column.add_theme_constant_override("separation", 12)
|
||||||
|
return column
|
||||||
|
|
||||||
|
|
||||||
func _availability_text(fish: FishDataType) -> String:
|
func _availability_text(fish: FishDataType) -> String:
|
||||||
if fish.availability == null:
|
if fish.availability == null:
|
||||||
return "Unknown"
|
return "unknown"
|
||||||
if fish.availability.allow_day and fish.availability.allow_night:
|
if fish.availability.allow_day and fish.availability.allow_night:
|
||||||
return "day and night"
|
return "day and night"
|
||||||
if fish.availability.allow_day:
|
if fish.availability.allow_day:
|
||||||
return "day"
|
return "day"
|
||||||
if fish.availability.allow_night:
|
if fish.availability.allow_night:
|
||||||
return "night"
|
return "night"
|
||||||
return "Unknown"
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
func _crossfade_details(rebuild: Callable) -> void:
|
func _crossfade_details(rebuild: Callable) -> void:
|
||||||
|
|
@ -626,6 +705,7 @@ func _clear_entries() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _clear_details() -> void:
|
func _clear_details() -> void:
|
||||||
|
_detail_portrait_button = null
|
||||||
for child: Node in _detail_body.get_children():
|
for child: Node in _detail_body.get_children():
|
||||||
_detail_body.remove_child(child)
|
_detail_body.remove_child(child)
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
|
|
@ -658,18 +738,23 @@ func _on_inventory_changed() -> void:
|
||||||
_refresh_details(false)
|
_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:
|
func _unknown_selection_key(fish: FishDataType) -> StringName:
|
||||||
var number: int = LogbookCatalog.catalog_number(fish.id)
|
var number: int = LogbookCatalog.catalog_number(fish.id)
|
||||||
return StringName("unknown_%d" % number)
|
return StringName("unknown_%d" % number)
|
||||||
|
|
||||||
|
|
||||||
func _label(text: String, font_size: int) -> Label:
|
func _label(text: String, font_size: int) -> Label:
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = text
|
||||||
|
label.add_theme_font_override(
|
||||||
|
"font", InventoryNotepadType.HANDWRITTEN_FONT
|
||||||
|
)
|
||||||
|
label.add_theme_font_size_override("font_size", font_size)
|
||||||
|
label.add_theme_color_override("font_color", INK)
|
||||||
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
func _field_label(text: String, font_size: int) -> Label:
|
||||||
var label := Label.new()
|
var label := Label.new()
|
||||||
label.text = text
|
label.text = text
|
||||||
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||||
|
|
@ -678,6 +763,33 @@ func _label(text: String, font_size: int) -> Label:
|
||||||
return label
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
func _handwritten_value_label(text: String, font_size: int) -> Label:
|
||||||
|
var adjusted_size: int = font_size
|
||||||
|
if _contains_ascii_digit(text):
|
||||||
|
adjusted_size = roundi(
|
||||||
|
float(font_size) * HANDWRITTEN_NUMERIC_SCALE
|
||||||
|
)
|
||||||
|
return _label(text, adjusted_size)
|
||||||
|
|
||||||
|
|
||||||
|
func _contains_ascii_digit(text: String) -> bool:
|
||||||
|
for codepoint: int in text.to_utf32_buffer():
|
||||||
|
if codepoint >= 48 and codepoint <= 57:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _entry_label_text(entry_name: String) -> String:
|
||||||
|
var split_index: int = entry_name.find(" ")
|
||||||
|
if split_index < 0:
|
||||||
|
return entry_name
|
||||||
|
return (
|
||||||
|
entry_name.substr(0, split_index)
|
||||||
|
+ "\n"
|
||||||
|
+ entry_name.substr(split_index + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _paper_style(color: Color, left: bool) -> StyleBoxFlat:
|
func _paper_style(color: Color, left: bool) -> StyleBoxFlat:
|
||||||
var style := StyleBoxFlat.new()
|
var style := StyleBoxFlat.new()
|
||||||
style.bg_color = color
|
style.bg_color = color
|
||||||
|
|
@ -690,16 +802,117 @@ func _paper_style(color: Color, left: bool) -> StyleBoxFlat:
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
func _entry_style(selected: bool) -> StyleBoxFlat:
|
func _build_portrait_overlay() -> void:
|
||||||
|
_portrait_overlay = Control.new()
|
||||||
|
_portrait_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
_portrait_overlay.z_index = 500
|
||||||
|
_portrait_overlay.visible = false
|
||||||
|
_portrait_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
add_child(_portrait_overlay)
|
||||||
|
|
||||||
|
_portrait_overlay_backdrop = Button.new()
|
||||||
|
_portrait_overlay_backdrop.set_anchors_and_offsets_preset(
|
||||||
|
Control.PRESET_FULL_RECT
|
||||||
|
)
|
||||||
|
_portrait_overlay_backdrop.text = ""
|
||||||
|
_portrait_overlay_backdrop.tooltip_text = "Return to logbook"
|
||||||
|
_portrait_overlay_backdrop.accessibility_name = "Return to logbook"
|
||||||
|
_portrait_overlay_backdrop.focus_neighbor_top = NodePath(".")
|
||||||
|
_portrait_overlay_backdrop.focus_neighbor_bottom = NodePath(".")
|
||||||
|
_portrait_overlay_backdrop.focus_neighbor_left = NodePath(".")
|
||||||
|
_portrait_overlay_backdrop.focus_neighbor_right = NodePath(".")
|
||||||
|
_portrait_overlay_backdrop.focus_next = NodePath(".")
|
||||||
|
_portrait_overlay_backdrop.focus_previous = NodePath(".")
|
||||||
|
var transparent_style := StyleBoxEmpty.new()
|
||||||
|
for state: StringName in [
|
||||||
|
&"normal", &"hover", &"focus", &"pressed", &"disabled",
|
||||||
|
]:
|
||||||
|
_portrait_overlay_backdrop.add_theme_stylebox_override(
|
||||||
|
state, transparent_style
|
||||||
|
)
|
||||||
|
_portrait_overlay_backdrop.pressed.connect(_hide_portrait_overlay)
|
||||||
|
_portrait_overlay_backdrop.gui_input.connect(
|
||||||
|
_on_portrait_overlay_backdrop_input
|
||||||
|
)
|
||||||
|
_portrait_overlay.add_child(_portrait_overlay_backdrop)
|
||||||
|
|
||||||
|
var center := CenterContainer.new()
|
||||||
|
center.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
center.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
_portrait_overlay.add_child(center)
|
||||||
|
var card := PanelContainer.new()
|
||||||
|
card.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
card.add_theme_stylebox_override("panel", _portrait_view_style())
|
||||||
|
center.add_child(card)
|
||||||
|
var margin := MarginContainer.new()
|
||||||
|
_set_margins(margin, 22, 22, 22, 22)
|
||||||
|
card.add_child(margin)
|
||||||
|
_portrait_overlay_artwork = LogbookPortraitType.new()
|
||||||
|
margin.add_child(_portrait_overlay_artwork)
|
||||||
|
|
||||||
|
|
||||||
|
func _show_portrait_overlay(portrait_texture: Texture2D) -> void:
|
||||||
|
if portrait_texture == null or _portrait_overlay == null:
|
||||||
|
return
|
||||||
|
_portrait_overlay_artwork.configure_fitted(
|
||||||
|
portrait_texture,
|
||||||
|
PORTRAIT_VIEW_MAX_SIZE,
|
||||||
|
)
|
||||||
|
_portrait_overlay.visible = true
|
||||||
|
_portrait_overlay.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
_portrait_overlay_backdrop.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _hide_portrait_overlay(restore_focus: bool = true) -> void:
|
||||||
|
if _portrait_overlay == null or not _portrait_overlay.visible:
|
||||||
|
return
|
||||||
|
_portrait_overlay.visible = false
|
||||||
|
_portrait_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
if (
|
||||||
|
restore_focus
|
||||||
|
and _detail_portrait_button != null
|
||||||
|
and is_instance_valid(_detail_portrait_button)
|
||||||
|
and _active
|
||||||
|
and _interactive
|
||||||
|
):
|
||||||
|
_detail_portrait_button.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_portrait_overlay_backdrop_input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("ui_cancel"):
|
||||||
|
_hide_portrait_overlay()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
func _portrait_button_style(highlighted: bool) -> StyleBoxFlat:
|
||||||
var style := StyleBoxFlat.new()
|
var style := StyleBoxFlat.new()
|
||||||
style.bg_color = Color("fff5dc") if selected else Color("eadcbd")
|
style.bg_color = (
|
||||||
style.border_color = Color("247a8b") if selected else Color("9b7a4f")
|
Color(1.0, 0.96, 0.86, 0.48)
|
||||||
style.set_border_width_all(3 if selected else 2)
|
if highlighted
|
||||||
style.set_corner_radius_all(8)
|
else Color.TRANSPARENT
|
||||||
style.content_margin_left = 8
|
)
|
||||||
style.content_margin_top = 8
|
style.set_border_width_all(0)
|
||||||
style.content_margin_right = 8
|
style.set_corner_radius_all(12)
|
||||||
style.content_margin_bottom = 8
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
func _portrait_view_style() -> StyleBoxFlat:
|
||||||
|
var style := StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color("fff5dc")
|
||||||
|
style.set_border_width_all(0)
|
||||||
|
style.set_corner_radius_all(18)
|
||||||
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
func _entry_style(highlighted: bool) -> StyleBoxFlat:
|
||||||
|
var style := StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color("fff5dc") if highlighted else Color.TRANSPARENT
|
||||||
|
style.set_border_width_all(0)
|
||||||
|
style.set_corner_radius_all(51)
|
||||||
|
style.content_margin_left = 5
|
||||||
|
style.content_margin_top = 5
|
||||||
|
style.content_margin_right = 5
|
||||||
|
style.content_margin_bottom = 5
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue