# Hurry Curry! - a game about cooking # Copyright (C) 2025 Hurry Curry! contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, version 3 of the License only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # class_name Player extends Node3D const PLAYER_SIZE: float = 0.4 const SPEED: float = 25. var game: Game var id := -1 var rotation_ = 0. var rotation_anim = 0. var position_ = Vector2(0, 0) var position_anim = Vector2(0, 0) var boosting := false var walking := false var username: String var movement_base: Node3D = Node3D.new() var character: Character = preload("res://player/character/character.tscn").instantiate() var chat_bubble: ChatBubble = preload("res://player/chat_bubble.tscn").instantiate() var item_bubble: ItemBubble = preload("res://player/item_bubble.tscn").instantiate() var effect: Effect = preload("res://player/particles/effect.tscn").instantiate() var marker: Marker = preload("res://player/marker/marker.tscn").instantiate() var clear_timer: Timer = Timer.new() var is_despawning: bool = false var hand = [null, null] var hand_base var character_style: Dictionary var player_class: String var is_customer: bool var is_chef: bool var current_item_message = null var _anim_angle: float = 0.0 const DEFAULT_HAND_BASE_POSITION_CENTER: Vector3 = Vector3(0, .425, .4) const DEFAULT_HAND_BASE_POSITION_LEFT: Vector3 = Vector3(.3, .425, .4) const DEFAULT_HAND_BASE_POSITION_RIGHT: Vector3 = Vector3(-.3, .425, .4) func _init(_id: int, name_: String, pos: Vector2, character_style_: Dictionary, player_class_: String, game_: Game): id = _id character_style = character_style_ player_class = player_class_ game = game_ if name_ != "": name = name_ username = name_ add_child(movement_base) movement_base.add_child(character) position_ = pos position_anim = pos movement_base.position = Vector3(pos.x, 0, pos.y) if Global.hand_count == 1: var center = Node3D.new() center.name = "HandBaseCenter" center.position = DEFAULT_HAND_BASE_POSITION_CENTER hand_base = [center] else: var left = Node3D.new() var right = Node3D.new() left.name = "HandBaseLeft" right.name = "HandBaseRight" left.position = DEFAULT_HAND_BASE_POSITION_LEFT right.position = DEFAULT_HAND_BASE_POSITION_RIGHT hand_base = [left, right] for h in hand_base: movement_base.add_child(h) movement_base.add_child(chat_bubble) movement_base.add_child(item_bubble) movement_base.add_child(effect) clear_timer.one_shot = true add_child(clear_timer) marker.visible = false add_child(marker) is_customer = player_class == "customer" is_chef = player_class == "chef" movement_base.scale = Vector3.ONE * 0.0001 func _ready(): character.set_style(character_style, player_class) clear_timer.timeout.connect(clear_message) Settings.hook_changed_init("gameplay.usernames", false, update_username_tag) func update_position(new_position: Vector2, new_rotation: float, new_boosting: bool): position_ = new_position rotation_ = new_rotation boosting = new_boosting func update_username_tag(state): var tag: Label3D = character.username_tag tag.text = username tag.visible = state and is_chef func set_item(i: Item, h: int): if hand[h] != null: hand[h].remove() # if i != null: # @warning_ignore("static_called_on_instance") # hand_base_position[h] = DEFAULT_HAND_BASE_POSITION_LEFT - Vector3(0.,i.height() * 0.5, 0.) # @warning_ignore("static_called_on_instance") # hand_base_position[1] = DEFAULT_HAND_BASE_POSITION_RIGHT - Vector3(0.,i.height() * 0.5, 0.) character.holding = i != null hand[h] = i if hand[h] != null: hand[h].owned_by = hand_base[h] func remove_item(h: int): var i = hand[h] if i == null: push_error("holding nothing") hand[h] = null character.holding = false return i func progress(position__: float, speed: float, warn: bool, h: int): if hand[h] != null: hand[h].progress(position__, speed, warn) func finish(h: int): if hand[h] != null: hand[h].finish() func take_item(tile: Tile, h: int): if hand[h] != null: push_error("already holding an item") var i = tile.take_item() i.take() set_item(i, h) func put_item(tile: Tile, h: int): var i = remove_item(h) i.put() tile.put_item(i) func pass_to(player: Player, hfrom: int, hto: int): var i = remove_item(hfrom) i.player_owned_timer = 0 if player.hand != null: push_error("target is already holding an item") player.set_item(i, hto) func _process(delta): _anim_angle = fmod(_anim_angle + delta, TAU) position_anim = G.interpolate(position_anim, position_, delta * 10) rotation_anim = G.interpolate_angle(rotation_anim, rotation_, delta * 10) movement_base.position.x = position_anim.x movement_base.position.z = position_anim.y movement_base.rotation.y = rotation_anim walking = walking or position_.distance_squared_to(position_anim) > 0.001 character.walking = walking character.boosting = boosting walking = false if is_despawning: movement_base.scale /= exp(delta * 8) if movement_base.scale.length() < 0.01: self.queue_free() else: movement_base.scale = Vector3.ONE * G.interpolate(movement_base.scale.x, 1, delta * 8) func clear_message(): item_bubble.remove_item() chat_bubble.remove_text() effect.clear_effect() current_item_message = null func item_message(item_name: String, timeout_initial: float, timeout_remaining: float): item_bubble.set_item(item_name, timeout_initial, timeout_remaining) clear_timer.start(timeout_remaining) current_item_message = item_name func text_message(message: Game.TextMessage): chat_bubble.set_text(message.text) clear_timer.start(message.timeout_remaining) func effect_message(effect_name: String): effect.set_effect(effect_name) clear_timer.start(5)