extends CharacterBody2D class_name Player const MAX_REACH = 100 const HOLDING_ITEM_SPRITE_SIZE = 20. signal player_updated(player: Player) signal upgraded var terrain : Terrain var planet : Planet : get(): return terrain if terrain is Planet else null @export var speed = 350 var has_just_received_instruction : bool = false # pour récupérer les zones dans les action_area, une frame doit être passée depuis la création de la zone var data : PlayerData var controlling_player : bool = true : set(v): controlling_player = v velocity = Vector2.ZERO var instruction : Instruction = null @onready var preview_zone : ActionZone = setup_action_zone(Vector2.ZERO, null) @onready var action_zone : ActionZone = setup_action_zone(Vector2.ZERO, null) func _ready(): data = GameInfo.game_data.player_data data.inventory.updated.connect(_on_inventory_updated) player_updated.emit(self) Pointer.player = self func _input(_event) -> void: if Input.is_action_pressed("change_item_left"): data.inventory.change_current_item(1) if Input.is_action_pressed("change_item_right"): data.inventory.change_current_item(-1) for i in range(1, 10): if Input.is_action_pressed("item_" + str(i)): data.inventory.set_current_item(i - 1) # Méthode déclenchée par la classe planet func _start_pass_day(): controlling_player = false instruction = null # Méthode déclenchée par la classe planet func _pass_day(): full_recharge() # Méthode déclenchée par la classe planet func _end_pass_day(): controlling_player = true func _process(_delta): if controlling_player: var old_velocity=velocity calculate_direction() if instruction and instruction.can_be_done(self) and not has_just_received_instruction: instruction.do(self) instruction = null move_preview_zone(get_global_mouse_position()) has_just_received_instruction = false # Sound if old_velocity.length()==0 and velocity.length()!=0: play_sfx("move") else: velocity = Vector2.ZERO move_and_slide() func _on_inventory_updated(_inventory: Inventory): setup_preview_zone(data.inventory.get_item()) var item : Item = data.inventory.get_item() if item: var item_texture = item.icon %ItemSprite.texture = item_texture %ItemSprite.scale = Vector2( 1./(item_texture.get_width()/HOLDING_ITEM_SPRITE_SIZE), 1./(item_texture.get_height()/HOLDING_ITEM_SPRITE_SIZE) ) %HideEyes.visible = item != null %ItemSprite.visible = item != null emit_signal("player_updated", self) func calculate_direction(): var input_direction: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down") if input_direction.length() != 0: instruction = null if instruction: input_direction = self.global_position.direction_to(instruction.position) velocity = input_direction * speed if input_direction.x: flip_character(input_direction.x > 0) func flip_character(face_right = true): $Sprite.flip_h = not face_right %ItemSprite.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1) %HideEyes.position.x = abs(%ItemSprite.position.x) * (1 if face_right else -1) func can_interact(interactable : Interactable): return interactable.can_interact(self) func try_interact(interactable : Interactable): if interactable: has_just_received_instruction = true instruction = InteractableInstruction.new( interactable ) func try_move(move_to : Vector2): instruction = MoveInstruction.new(move_to) func pick_item(item : Item) -> Item: play_sfx("pick") if data.inventory.is_full(): drop_item() var available_slot_ind = data.inventory.get_best_available_slot_ind() if ( available_slot_ind == data.inventory.current_item_ind && data.inventory.items[available_slot_ind] != null ): var current_item : Item = data.inventory.get_item() data.inventory.set_item(item, available_slot_ind) return current_item else : if data.inventory.set_item(item, available_slot_ind): data.inventory.set_current_item(available_slot_ind); return null func drop_item(): var item_to_drop = data.inventory.pop_item() if item_to_drop: terrain.drop_item(item_to_drop, global_position) play_sfx("drop") func delete_item(item: Item): data.inventory.remove_item(item) func try_use_item(item : Item, use_position : Vector2): has_just_received_instruction = true setup_action_zone(use_position, item) instruction = ItemActionInstruction.new( use_position, item ) func preview_could_use_item(item : Item) -> bool: return could_use_item_on_zone(item, preview_zone) func could_use_item_on_zone(item : Item, zone: ActionZone) -> bool: return ( data.inventory.has_item(item) and item.can_use(self, zone) ) func can_use_item_on_zone(item : Item, zone: ActionZone) -> bool: return ( could_use_item_on_zone(item, zone) and has_energy_to_use_item(item) ) func has_energy_to_use_item(item : Item): return (data.energy - item.energy_usage) >= 0 func use_item(item : Item): if can_use_item_on_zone(item, action_zone): var is_item_used = await item.use(self, action_zone) if is_item_used: data.energy -= item.energy_usage if item.is_one_time_use(): data.inventory.remove_item(item) func upgrade_max_energy(amount = 1): data.max_energy += amount upgraded.emit() player_updated.emit(self) func upgrade_inventory_size(amount = 1): data.inventory.items.resize(data.inventory.items.size() + amount) upgraded.emit() player_updated.emit(self) func recharge(amount : int = data.max_energy): data.energy += + amount upgraded.emit() func full_recharge(): data.energy = max(data.energy, data.max_energy) func generate_action_zone(item : Item) -> ActionZone: var zone = ActionZone.new(item) if zone.area: get_parent().add_child(zone.area) return zone func setup_preview_zone(item : Item) -> ActionZone: if preview_zone and preview_zone.item == item: return preview_zone elif preview_zone: preview_zone.destroy() if item: preview_zone = generate_action_zone(item) else: preview_zone = null return preview_zone func setup_action_zone(zone_position : Vector2, item: Item) -> ActionZone: if action_zone: action_zone.destroy() action_zone = generate_action_zone(item) action_zone.move_to_position(zone_position) return action_zone func move_preview_zone(zone_position : Vector2): if preview_zone: preview_zone.move_to_position(zone_position) func play_sfx(sound : String): match sound: "dig": $Audio/AudioStreamPlayer_dig.play() "harvest": $Audio/AudioStreamPlayer_harvest.play() "pick": $Audio/AudioStreamPlayer_pick_up.play() "drop": $Audio/AudioStreamPlayer_drop.play() "move": $Audio/AudioStreamPlayer_movement.play() class Instruction: var position : Vector2 func _init(_pos : Vector2): position = _pos func can_be_done(player : Player): return player.global_position.distance_to(position) < 10 func do(_player : Player): pass class MoveInstruction extends Instruction: pass class ItemActionInstruction extends Instruction: var item = Item func _init(_pos : Vector2, _item : Item): position = _pos item = _item func can_be_done(player : Player): return player.global_position.distance_to(position) < player.MAX_REACH func do(player : Player): player.use_item(item) class InteractableInstruction extends Instruction: var interactable = Interactable func _init(_interactable : Interactable): interactable = _interactable position = interactable.global_position func can_be_done(player : Player): return player.global_position.distance_to(position) < player.MAX_REACH func do(player : Player): interactable.interact(player) class ActionZone: var item : Item = null var area : Area2D var affected_areas : Array[InspectableEntity]= [] func _init(_i : Item): item = _i if item and item.get_usage_zone_radius() > 0: area = Area2D.new() var collision_shape = CollisionShape2D.new() var circle_shape = CircleShape2D.new() circle_shape.radius = item.get_usage_zone_radius() collision_shape.shape = circle_shape area.add_child(collision_shape) func clear_preview_on_affected_area(): for a in affected_areas: if a: a.affect_preview(false) func update_preview_on_affected_area(): var detected_areas = get_affected_areas() clear_preview_on_affected_area() var new_affected_areas : Array[InspectableEntity] = [] for a in detected_areas: if a is InspectableEntity and item.get_usage_object_affected(a): a.affect_preview(true) new_affected_areas.append(a) affected_areas = new_affected_areas func get_affected_areas() -> Array[Area2D]: var empty_array : Array[Area2D] = [] return empty_array if area == null else area.get_overlapping_areas() func destroy(): clear_preview_on_affected_area() if area: area.queue_free() func get_global_position() -> Vector2: return Vector2.ZERO if area == null else area.global_position func move_to_position(pos : Vector2): if area: update_preview_on_affected_area() area.global_position = pos func get_points_in_zone(point_factor = 10) -> Array[Vector2]: var points : Array[Vector2] = [] var radius = item.get_usage_zone_radius() for x in range(-radius, radius, point_factor): for y in range(-radius, radius, point_factor): if Vector2(x, y).length() <= radius: points.append(area.global_position + Vector2(x, y)) return points