forked from xiangwang25/whale-town-front-v2
117 lines
4.4 KiB
GDScript
117 lines
4.4 KiB
GDScript
extends Area2D
|
|
class_name CafeCompanionTarget
|
|
|
|
# ============================================================================
|
|
# CafeCompanionTarget.gd - 咖啡店陪伴机器人点击组件
|
|
# ============================================================================
|
|
# 挂在咖啡店 NPC 或被雇佣玩家节点下,负责把鼠标点击转成
|
|
# “选择陪伴机器人”事件;真正聊天必须由付费时长购买后开启。
|
|
# ============================================================================
|
|
|
|
@export var servicePointId: String = ""
|
|
@export var companionId: String = ""
|
|
@export var companionType: String = "npc"
|
|
@export var personaName: String = ""
|
|
@export var ownerUserId: String = ""
|
|
@export var employmentEndsAt: String = ""
|
|
@export var selfTarget: bool = false
|
|
|
|
var _lastClickMsec: int = 0
|
|
|
|
func _ready() -> void:
|
|
var interactable: InteractableComponent = InteractableComponent.new()
|
|
interactable.interaction_distance = 160.0
|
|
add_child(interactable)
|
|
input_pickable = true
|
|
set_process_unhandled_input(true)
|
|
|
|
func is_interaction_active(_component: InteractableComponent) -> bool:
|
|
return not servicePointId.strip_edges().is_empty() and not companionId.strip_edges().is_empty()
|
|
|
|
func build_interaction_actions(_component: InteractableComponent, _player: Node2D) -> Array[InteractionAction]:
|
|
var actions: Array[InteractionAction] = []
|
|
var display_name: String = _resolved_persona_name()
|
|
var action_id: String = "cafe_companion:%s" % servicePointId
|
|
actions.append(InteractionAction.create(action_id, "与 %s 交流" % display_name, 45, Callable(self, "_try_emit_target_selected")))
|
|
return actions
|
|
|
|
func _input_event(_viewport: Viewport, event: InputEvent, _shapeIdx: int) -> void:
|
|
if not (event is InputEventMouseButton):
|
|
return
|
|
var mouseEvent := event as InputEventMouseButton
|
|
if mouseEvent.button_index != MOUSE_BUTTON_LEFT or not mouseEvent.pressed:
|
|
return
|
|
|
|
get_viewport().set_input_as_handled()
|
|
_try_emit_target_selected()
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if not (event is InputEventMouseButton):
|
|
return
|
|
var mouseEvent := event as InputEventMouseButton
|
|
if mouseEvent.button_index != MOUSE_BUTTON_LEFT or not mouseEvent.pressed:
|
|
return
|
|
if not _contains_global_point(get_global_mouse_position()):
|
|
return
|
|
|
|
get_viewport().set_input_as_handled()
|
|
_try_emit_target_selected()
|
|
|
|
func _try_emit_target_selected() -> void:
|
|
var now := Time.get_ticks_msec()
|
|
if _lastClickMsec > 0 and now - _lastClickMsec < 120:
|
|
return
|
|
_lastClickMsec = now
|
|
_emit_target_selected()
|
|
|
|
func _emit_target_selected() -> void:
|
|
var eventSystem := get_node_or_null("/root/EventSystem")
|
|
if eventSystem == null:
|
|
return
|
|
|
|
var payload := {
|
|
"cafe_id": "whale_cafe",
|
|
"service_point_id": servicePointId,
|
|
"companion_id": companionId,
|
|
"companion_type": companionType,
|
|
"persona_name": _resolved_persona_name(),
|
|
"owner_user_id": ownerUserId,
|
|
"employment_ends_at": employmentEndsAt,
|
|
"target_path": str(get_parent().get_path()) if get_parent() != null else "",
|
|
}
|
|
eventSystem.call("emit_event", EventNames.CAFE_COMPANION_SELF_SELECTED if selfTarget else EventNames.CAFE_COMPANION_SELECTED, payload)
|
|
|
|
func _resolved_persona_name() -> String:
|
|
var normalizedName := personaName.strip_edges()
|
|
if not normalizedName.is_empty():
|
|
return normalizedName
|
|
var parent := get_parent()
|
|
if parent != null and parent.get("npcName") != null:
|
|
return str(parent.get("npcName")).strip_edges()
|
|
return companionId
|
|
|
|
func _contains_global_point(globalPoint: Vector2) -> bool:
|
|
for child in get_children():
|
|
var shapeNode := child as CollisionShape2D
|
|
if shapeNode == null or shapeNode.disabled or shapeNode.shape == null:
|
|
continue
|
|
if _shape_contains_point(shapeNode, globalPoint):
|
|
return true
|
|
return false
|
|
|
|
func _shape_contains_point(shapeNode: CollisionShape2D, globalPoint: Vector2) -> bool:
|
|
var localPoint := shapeNode.to_local(globalPoint)
|
|
var shape := shapeNode.shape
|
|
if shape is RectangleShape2D:
|
|
var rectangle := shape as RectangleShape2D
|
|
return Rect2(rectangle.size * -0.5, rectangle.size).has_point(localPoint)
|
|
if shape is CircleShape2D:
|
|
var circle := shape as CircleShape2D
|
|
return localPoint.length() <= circle.radius
|
|
if shape is CapsuleShape2D:
|
|
var capsule := shape as CapsuleShape2D
|
|
var halfHeight: float = maxf(0.0, capsule.height * 0.5 - capsule.radius)
|
|
var clampedY: float = clampf(localPoint.y, -halfHeight, halfHeight)
|
|
return Vector2(localPoint.x, localPoint.y - clampedY).length() <= capsule.radius
|
|
return false
|