hy-farm/godot/FarmGodot/scripts/ui/farm/land_grid_view.gd
2026-05-29 19:54:56 +08:00

238 lines
7.6 KiB
GDScript

class_name LandGridView
extends RefCounted
var _owner: Control
var _game_view: Control
var _game_config
var _crop_phase_rules
var _get_land_node: Callable
var _render_add_land_indicator: Callable
var _land_count := 12
var _land_texture_size := Vector2(172.0, 102.0)
var _textures: Dictionary = {}
var _plant_layer: Control
var _plant_state_layer: Control
func setup(options: Dictionary) -> void:
_owner = options.get("owner") as Control
_game_view = options.get("game_view") as Control
_game_config = options.get("game_config")
_crop_phase_rules = options.get("crop_phase_rules")
_get_land_node = options.get("get_land_node", Callable())
_render_add_land_indicator = options.get("render_add_land_indicator", Callable())
_land_count = int(options.get("land_count", _land_count))
_land_texture_size = options.get("land_texture_size", _land_texture_size)
_textures = options.get("textures", {})
_create_layers()
func plant_layer() -> Control:
return _plant_layer
func plant_state_layer() -> Control:
return _plant_state_layer
func render(lands_by_position: Dictionary) -> void:
_clear_children(_plant_layer)
_clear_children(_plant_state_layer)
for position in range(_land_count):
var land_node := _get_land_node_for(position)
if land_node == null:
continue
var land_data: Dictionary = lands_by_position.get(position, {})
land_node.texture = _get_land_texture(land_data)
if not land_data.is_empty() and int(land_data.get("crop_id", 0)) > 0:
_render_plant_on_land(position, land_data)
_render_disease_state(position, land_data)
if _render_add_land_indicator.is_valid():
_render_add_land_indicator.call()
func build_signature(lands_by_position: Dictionary) -> String:
var parts := PackedStringArray()
for position in range(_land_count):
var land_data: Dictionary = lands_by_position.get(position, {})
if land_data.is_empty():
parts.append("%s:locked" % position)
continue
var crop_id := int(land_data.get("crop_id", 0))
if crop_id <= 0:
parts.append("%s:empty:%s" % [position, _land_level(land_data)])
continue
parts.append("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s" % [
position,
crop_id,
_get_plant_phase(land_data),
int(land_data.get("crop_num", 0)),
int(land_data.get("has_gather", 0)),
int(land_data.get("has_bug", 0)),
int(land_data.get("has_grass", 0)),
int(land_data.get("has_dry", 0)),
int(land_data.get("crop_min_num", 0)),
",".join(_friend_interaction_types(land_data)),
])
return "|".join(parts)
func get_plant_phase(land_data: Dictionary) -> int:
return _get_plant_phase(land_data)
func get_crop_phase_times(crop_id: int) -> Array[int]:
if _crop_phase_rules == null:
return [1, 1, 1]
return _crop_phase_rules.get_crop_phase_times(crop_id)
func _create_layers() -> void:
if _game_view == null:
return
_plant_layer = Control.new()
_plant_layer.name = "PlantLayer"
_plant_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
_plant_layer.set_anchors_preset(Control.PRESET_FULL_RECT)
_game_view.add_child(_plant_layer)
_plant_state_layer = Control.new()
_plant_state_layer.name = "PlantStateLayer"
_plant_state_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
_plant_state_layer.set_anchors_preset(Control.PRESET_FULL_RECT)
_game_view.add_child(_plant_state_layer)
func _get_land_node_for(position: int) -> TextureRect:
if not _get_land_node.is_valid():
return null
return _get_land_node.call(position) as TextureRect
func _get_land_texture(land_data: Dictionary) -> Texture2D:
if land_data.is_empty():
return _textures.get("locked")
match _land_level(land_data):
1:
return _textures.get("level_1")
2:
return _textures.get("level_2")
3:
return _textures.get("level_3")
_:
return _textures.get("level_0")
func _render_plant_on_land(position: int, land_data: Dictionary) -> void:
var land_node := _get_land_node_for(position)
if land_node == null or _plant_layer == null:
return
var phase := _get_plant_phase(land_data)
var texture := _get_plant_texture(int(land_data.get("crop_id", 0)), phase)
if texture == null:
return
var plant := TextureRect.new()
plant.name = "Plant%s" % position
plant.texture = texture
plant.size = texture.get_size()
plant.mouse_filter = Control.MOUSE_FILTER_IGNORE
plant.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
plant.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
var land_center := land_node.position + land_node.size * 0.5
var offset_y := -plant.size.y if phase == 0 else -plant.size.y + 10.0
plant.position = land_center + Vector2(-plant.size.x * 0.5 - 10.0, offset_y)
_plant_layer.add_child(plant)
func _render_disease_state(position: int, land_data: Dictionary) -> void:
if _plant_state_layer == null:
return
var phase := _get_plant_phase(land_data)
if phase < 0 or phase > 2:
return
var disease_textures: Array[Texture2D] = []
if int(land_data.get("has_grass", 0)) == 1:
disease_textures.append(_textures.get("state_grass"))
if int(land_data.get("has_bug", 0)) == 1:
disease_textures.append(_textures.get("state_bug"))
if int(land_data.get("has_dry", 0)) == 1:
disease_textures.append(_textures.get("state_dry"))
if disease_textures.is_empty():
return
var land_node := _get_land_node_for(position)
if land_node == null:
return
var state_group := Control.new()
state_group.name = "PlantState%s" % position
state_group.mouse_filter = Control.MOUSE_FILTER_IGNORE
state_group.size = Vector2(33.0 * disease_textures.size(), 40.0)
var land_center := land_node.position + land_node.size * 0.5
state_group.position = Vector2(
land_center.x - state_group.size.x * 0.5 - 13.0,
land_center.y + (-100.0 if phase == 2 else -70.0)
)
for index in range(disease_textures.size()):
var texture := disease_textures[index]
if texture == null:
continue
var icon := TextureRect.new()
icon.name = "StateIcon%s" % index
icon.texture = texture
icon.position = Vector2(33.0 * index, -10.0 if disease_textures.size() == 3 and index == 1 else 0.0)
icon.size = texture.get_size()
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
state_group.add_child(icon)
_plant_state_layer.add_child(state_group)
if _owner == null:
return
var tween := _owner.create_tween().bind_node(state_group).set_loops()
tween.tween_property(state_group, "position:y", state_group.position.y - 4.0, 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
tween.tween_property(state_group, "position:y", state_group.position.y, 0.5).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
func _friend_interaction_types(land_data: Dictionary) -> PackedStringArray:
var result := PackedStringArray()
var raw_types = land_data.get("interaction_type_list", [])
if not raw_types is Array:
return result
for raw_type in raw_types:
result.append(str(int(raw_type)))
return result
func _get_plant_phase(land_data: Dictionary) -> int:
if _crop_phase_rules == null:
return int(land_data.get("crop_phase", -1))
return _crop_phase_rules.get_phase(land_data)
func _get_plant_texture(crop_id: int, phase: int) -> Texture2D:
var path := "res://assets/egret/plant/plant_p0.png"
if phase == 4:
path = "res://assets/egret/plant/plant_p4.png"
elif phase > 0 and _game_config != null:
var plant_graphic_id := int(_game_config.get_crop_graphic_id(crop_id, phase))
path = "res://assets/egret/plant/%s.png" % plant_graphic_id
if not ResourceLoader.exists(path):
path = "res://assets/egret/plant/plant_p0.png"
return load(path)
func _land_level(land_data: Dictionary) -> int:
return clampi(int(land_data.get("level", land_data.get("landLv", 0))), 0, 3)
func _clear_children(parent: Node) -> void:
if parent == null:
return
for child in parent.get_children():
parent.remove_child(child)
child.queue_free()