150 lines
4.6 KiB
GDScript
150 lines
4.6 KiB
GDScript
class_name PantryStockVisual
|
|
extends Node3D
|
|
|
|
enum StockLevel { EMPTY, LOW, STOCKED }
|
|
|
|
const LOW_STOCK_THRESHOLD := 4.0
|
|
const REFILL_RESPONSE_START := Vector3(0.0, 0.08, -0.5)
|
|
const REFILL_RESPONSE_END := Vector3(0.0, 0.36, -0.5)
|
|
|
|
@onready var empty_root: Node3D = $EmptyStock
|
|
@onready var low_stock_root: Node3D = $LowStock
|
|
@onready var full_stock_root: Node3D = $FullStock
|
|
@onready var refill_response_root: Node3D = $RefillResponse
|
|
@onready var refill_response_visuals: Array[MeshInstance3D] = [
|
|
$RefillResponse/Ring,
|
|
$RefillResponse/MoteLeft,
|
|
$RefillResponse/MoteCenter,
|
|
$RefillResponse/MoteRight,
|
|
]
|
|
|
|
var displayed_amount := -1.0
|
|
var stock_level := StockLevel.EMPTY
|
|
var transition_tween: Tween
|
|
var refill_tween: Tween
|
|
|
|
|
|
func _ready() -> void:
|
|
refill_response_root.visible = false
|
|
|
|
|
|
func set_food_amount(amount: float, animate: bool = true) -> void:
|
|
var normalized_amount := maxf(amount, 0.0)
|
|
var next_level := _get_stock_level(normalized_amount)
|
|
if next_level == StockLevel.EMPTY:
|
|
_stop_refill_response()
|
|
var was_initialized := displayed_amount >= 0.0
|
|
var was_empty := was_initialized and displayed_amount <= 0.001
|
|
if was_initialized and next_level == stock_level:
|
|
displayed_amount = normalized_amount
|
|
return
|
|
var should_animate := animate and was_initialized
|
|
displayed_amount = normalized_amount
|
|
stock_level = next_level
|
|
if should_animate:
|
|
_transition_to_level(next_level)
|
|
else:
|
|
_apply_level_immediately(next_level)
|
|
if was_empty and normalized_amount > 0.001 and animate:
|
|
_play_refill_response()
|
|
|
|
|
|
func get_stock_level() -> StockLevel:
|
|
return stock_level
|
|
|
|
|
|
func _get_stock_level(amount: float) -> StockLevel:
|
|
if amount <= 0.001:
|
|
return StockLevel.EMPTY
|
|
if amount < LOW_STOCK_THRESHOLD:
|
|
return StockLevel.LOW
|
|
return StockLevel.STOCKED
|
|
|
|
|
|
func _apply_level_immediately(level: StockLevel) -> void:
|
|
_stop_transition()
|
|
_set_root_visible(empty_root, level == StockLevel.EMPTY)
|
|
_set_root_visible(low_stock_root, level in [StockLevel.LOW, StockLevel.STOCKED])
|
|
_set_root_visible(full_stock_root, level == StockLevel.STOCKED)
|
|
|
|
|
|
func _transition_to_level(level: StockLevel) -> void:
|
|
_stop_transition()
|
|
transition_tween = create_tween().set_parallel(true)
|
|
_transition_root(empty_root, level == StockLevel.EMPTY)
|
|
_transition_root(low_stock_root, level in [StockLevel.LOW, StockLevel.STOCKED])
|
|
_transition_root(full_stock_root, level == StockLevel.STOCKED)
|
|
transition_tween.chain().tween_callback(_finish_transition.bind(level))
|
|
|
|
|
|
func _transition_root(root: Node3D, should_show: bool) -> void:
|
|
if should_show:
|
|
if not root.visible:
|
|
root.visible = true
|
|
root.scale = Vector3.ONE * 0.62
|
|
(
|
|
transition_tween
|
|
. tween_property(root, "scale", Vector3.ONE, 0.28)
|
|
. set_trans(Tween.TRANS_BACK)
|
|
. set_ease(Tween.EASE_OUT)
|
|
)
|
|
return
|
|
if not root.visible:
|
|
return
|
|
(
|
|
transition_tween
|
|
. tween_property(root, "scale", Vector3.ONE * 0.18, 0.16)
|
|
. set_trans(Tween.TRANS_SINE)
|
|
. set_ease(Tween.EASE_IN)
|
|
)
|
|
|
|
|
|
func _finish_transition(level: StockLevel) -> void:
|
|
transition_tween = null
|
|
_set_root_visible(empty_root, level == StockLevel.EMPTY)
|
|
_set_root_visible(low_stock_root, level in [StockLevel.LOW, StockLevel.STOCKED])
|
|
_set_root_visible(full_stock_root, level == StockLevel.STOCKED)
|
|
|
|
|
|
func _set_root_visible(root: Node3D, is_visible: bool) -> void:
|
|
root.visible = is_visible
|
|
root.scale = Vector3.ONE
|
|
|
|
|
|
func _stop_transition() -> void:
|
|
if transition_tween != null:
|
|
transition_tween.kill()
|
|
transition_tween = null
|
|
|
|
|
|
func _play_refill_response() -> void:
|
|
_stop_refill_response()
|
|
refill_response_root.visible = true
|
|
refill_response_root.position = REFILL_RESPONSE_START
|
|
refill_response_root.scale = Vector3.ONE * 0.45
|
|
_set_refill_transparency(0.0)
|
|
refill_tween = create_tween()
|
|
refill_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|
|
refill_tween.tween_property(refill_response_root, "scale", Vector3.ONE, 0.24)
|
|
refill_tween.parallel().tween_method(_set_refill_transparency, 0.0, 1.0, 0.18)
|
|
refill_tween.tween_property(refill_response_root, "position", REFILL_RESPONSE_END, 0.5)
|
|
refill_tween.parallel().tween_method(_set_refill_transparency, 1.0, 0.0, 0.5)
|
|
refill_tween.tween_callback(_stop_refill_response)
|
|
|
|
|
|
func _set_refill_transparency(value: float) -> void:
|
|
for visual in refill_response_visuals:
|
|
visual.transparency = clampf(1.0 - value, 0.0, 1.0)
|
|
|
|
|
|
func _stop_refill_response() -> void:
|
|
if refill_tween != null:
|
|
refill_tween.kill()
|
|
refill_tween = null
|
|
if refill_response_root == null:
|
|
return
|
|
refill_response_root.visible = false
|
|
refill_response_root.position = REFILL_RESPONSE_START
|
|
refill_response_root.scale = Vector3.ONE * 0.45
|
|
_set_refill_transparency(0.0)
|