bcp de characters

This commit is contained in:
E250429F
2026-09-03 09:43:13 +02:00
parent a061aff066
commit 025a866eef
1175 changed files with 85837 additions and 286 deletions
+5 -4
View File
@@ -5,7 +5,8 @@ extends Camera3D
var sensitivity = 0.008
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion and not get_tree().paused:
player.rotate_y(-event.relative.x * sensitivity)
player.camera_pov.rotate_x(-event.relative.y * sensitivity)
player.camera_pov.rotation.x=clamp(player.camera_pov.rotation.x,deg_to_rad(-90),deg_to_rad(90))
if !player.third_person:
if event is InputEventMouseMotion and not get_tree().paused:
player.rotate_y(-event.relative.x * sensitivity)
player.camera_pov.rotate_x(-event.relative.y * sensitivity)
player.camera_pov.rotation.x=clamp(player.camera_pov.rotation.x,deg_to_rad(-90),deg_to_rad(90))
+15
View File
@@ -0,0 +1,15 @@
extends Node3D
@export var window_inside : bool = false
@onready var window: MeshInstance3D = $window
@onready var window_001: MeshInstance3D = $window_001
func _ready() -> void:
if !window_inside:
var material = window.get_active_material(0)
material.emission_enabled = false
var material1 = window_001.get_active_material(0)
material1.emission_enabled = true
+1
View File
@@ -0,0 +1 @@
uid://dr3423c5e1enj
+20
View File
@@ -0,0 +1,20 @@
extends Node3D
@onready var cinematic_camera: Node3D = $cinematic_camera
@onready var car_anim_player: AnimationPlayer = $cinematic_camera/car/AnimationPlayer
@onready var player_anim_player: AnimationPlayer = $cinematic_camera/player_driving/AnimationPlayer
func _ready() -> void:
Dialogic.start("timeline")
Dialogic.timeline_ended.connect( change_scene )
func change_scene():
Transition.change_scene("res://scenes/main.scn")
func _process(delta: float) -> void:
car_anim_player.play("driving")
player_anim_player.play("driving")
if cinematic_camera.global_position.x < 490:
cinematic_camera.global_position.x += 16 * delta
else:
cinematic_camera.global_position.x += 10 * delta
+1
View File
@@ -0,0 +1 @@
uid://c2tdcxorciwkd
+78
View File
@@ -0,0 +1,78 @@
extends CharacterBody3D
@export var have_dialog : bool = false
@export var alrdy_talking : bool = false
@export var can_wander : bool = false
@onready var sprite_label: Sprite3D = $Sprite3D
@onready var anim_player: AnimationPlayer = $character/AnimationPlayer
# navigation
@export var dest_array:Array[Marker3D]
@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D
@onready var idle_timer: Timer = $Timer
enum NPCSTATE {
IDLE,
WALKING
}
var cur_state : NPCSTATE
var cur_destination: Marker3D
var speed = 2
func _ready() -> void:
sprite_label.visible = false
if alrdy_talking:
await get_tree().create_timer( randf_range(1.0, 2.5) ).timeout
anim_player.play("RootAction")
if can_wander:
_set_state( NPCSTATE.IDLE )
func _on_timer_timeout() -> void:
decide_next_state()
func _set_state(new_state:NPCSTATE):
cur_state = new_state
match cur_state:
NPCSTATE.IDLE:
idle_timer.start( randf_range(1, 2) )
anim_player.play("RootAction", -1, 10)
NPCSTATE.WALKING:
anim_player.play("RootAction_001", -1, 2.5)
func _physics_process(delta: float) -> void:
match cur_state:
NPCSTATE.IDLE:
pass
NPCSTATE.WALKING:
var next_path_pos:Vector3 = navigation_agent.get_next_path_position()
var new_vel : Vector3 = global_position.direction_to( next_path_pos ) * speed
velocity = new_vel
var look_at_target : Vector3 = Vector3( next_path_pos.x, global_position.y, next_path_pos.z )
if not global_position.is_equal_approx( look_at_target ):
look_at( look_at_target )
move_and_slide()
func decide_next_state():
if cur_state == NPCSTATE.IDLE:
cur_destination = dest_array.pick_random()
navigation_agent.target_position = cur_destination.global_position
_set_state( NPCSTATE.WALKING )
func _on_area_3d_body_entered(body: Node3D) -> void:
if have_dialog and body.is_in_group("player"):
sprite_label.visible = true
body.set_dialog_mode( global_position )
func _on_area_3d_body_exited(body: Node3D) -> void:
if have_dialog and body.is_in_group("player"):
sprite_label.visible = false
body.set_dialog_mode()
func _on_navigation_agent_3d_navigation_finished() -> void:
_set_state( NPCSTATE.IDLE )
+1
View File
@@ -0,0 +1 @@
uid://fknk7s075mxu
+8
View File
@@ -0,0 +1,8 @@
extends MultiMeshInstance3D
func _ready() -> void:
var characters_seating:Array = get_tree().get_nodes_in_group("char_seat")
for char in characters_seating:
char.get_node("AnimationPlayer").play("mixamo_com")
await get_tree().create_timer( randf_range(0.1, 1.5) ).timeout
@@ -0,0 +1 @@
uid://bgau5civo1ppi
+67
View File
@@ -0,0 +1,67 @@
extends CharacterBody3D
@onready var sprite_label: Sprite3D = $Sprite3D
@onready var label: Label = $Sprite3D/SubViewport/Label
@onready var area_3d: Area3D = $Area3D
@onready var anim_player: AnimationPlayer = $character/AnimationPlayer
@onready var camera_zoom: Camera3D = $CameraZoom
@onready var player: CharacterBody3D = $"../../Player"
@onready var area_3d_zoom: Area3D = $Area3DZoom
@onready var indicateur_tarte: CSGTorus3D = $"../indicateur_tarte"
@onready var client_spawner: Node3D = $"../client_spawner"
var zoom_active : bool = false
func zoom_dialog():
label.text = Sauvegarde.get_text("coco" + str( 0 ) )
camera_zoom.make_current()
zoom_active = true
anim_player.play("RootAction")
func _process(delta: float) -> void:
if zoom_active:
camera_zoom.position.z += 25 * delta
if camera_zoom.position.z > -4:
zoom_active = false
await get_tree().create_timer( 6 ).timeout
player.camera.make_current()
player.can_play = true
anim_player.stop()
func dialog():
rotation.y = deg_to_rad( 180 )
anim_player.play("RootAction")
for i in range(1,3):
label.text = Sauvegarde.get_text("coco" + str( i ) )
await get_tree().create_timer( 1 ).timeout
rotation.y = deg_to_rad( -90 )
anim_player.stop()
sprite_label.visible = false
player.set_dialog_mode()
indicateur_tarte.visible = true
client_spawner.start_timer()
await get_tree().create_timer( 10 ).timeout
indicateur_tarte.visible = false
func _on_area_3d_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
sprite_label.visible = true
player.set_dialog_mode( global_position, 0)
dialog()
area_3d.queue_free()
func _on_area_3d_zoom_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
player.can_play = false
area_3d_zoom.queue_free()
zoom_dialog()
+1
View File
@@ -0,0 +1 @@
uid://bv2vqmllmek6f
+33
View File
@@ -0,0 +1,33 @@
extends CharacterBody3D
@onready var sprite_label: Sprite3D = $Sprite3D
@onready var label: Label = $Sprite3D/SubViewport/Label
@onready var area_3d: Area3D = $Area3D
@onready var doggo_anim_player: AnimationPlayer = $dog/AnimationPlayer
@onready var anim_player: AnimationPlayer = $character/AnimationPlayer
var player
func _ready() -> void:
doggo_anim_player.play("sleep")
func dialog():
anim_player.play("RootAction")
for i in range(1, 7):
label.text = Sauvegarde.get_text("neige" + str( i ) )
await get_tree().create_timer( 7 ).timeout
anim_player.stop()
sprite_label.visible = false
player.set_dialog_mode()
func _on_area_3d_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
player = body
sprite_label.visible = true
player.set_dialog_mode( global_position, 0)
dialog()
area_3d.queue_free()
+1
View File
@@ -0,0 +1 @@
uid://b8kl67fi5cpas
+127
View File
@@ -0,0 +1,127 @@
extends CharacterBody3D
# --- ÉTATS DU CLIENT ---
enum State { ENTERING, WAITING, LEAVING }
var current_state: State = State.ENTERING
# --- PARAMÈTRES ---
@export var speed: float = 12
@export var entry_path: Array[Marker3D]
@export var exit_path: Array[Marker3D]
var current_path_index: int = 0
var possible_pies = [Sauvegarde.get_text("pie1"), Sauvegarde.get_text("pie2"), Sauvegarde.get_text("pie3")]
var possible_toppings = [Sauvegarde.get_text("topping1"), Sauvegarde.get_text("topping2"), "Aucun"]
var pie_icons = {
Sauvegarde.get_text("pie1"): "res://assets/img/ui/client/orange.png",
Sauvegarde.get_text("pie2"): "res://assets/img/ui/client/yellow.png",
Sauvegarde.get_text("pie3"): "res://assets/img/ui/client/purple.png",
}
var topping_icons = {
Sauvegarde.get_text("topping1"): "res://assets/img/ui/client/meringue.png",
Sauvegarde.get_text("topping2"): "res://assets/img/ui/client/croisillon.png",
}
var desired_pie: String = ""
var desired_topping: String = ""
var ready_to_take_pie : bool = false
# --- RÉFÉRENCES UI ---
@onready var sprite: Sprite3D = $Sprite3D
@onready var label: RichTextLabel = $Sprite3D/SubViewport/RichTextLabel
func _ready() -> void:
sprite.visible = false
desired_pie = possible_pies.pick_random()
desired_topping = possible_toppings.pick_random()
func _physics_process(delta: float) -> void:
if not is_on_floor():
velocity.y -= 9.8 * delta
match current_state:
State.ENTERING:
move_along_path(entry_path, delta)
State.WAITING:
velocity.x = move_toward(velocity.x, 0, speed)
velocity.z = move_toward(velocity.z, 0, speed)
State.LEAVING:
move_along_path(exit_path, delta)
move_and_slide()
# --- FONCTION DE DÉPLACEMENT ---
func move_along_path(path_array: Array[Marker3D], delta: float) -> void:
if path_array.is_empty() or current_path_index >= path_array.size():
if current_state == State.ENTERING:
start_waiting()
elif current_state == State.LEAVING:
queue_free()
return
var target = path_array[current_path_index].global_position
var direction = global_position.direction_to(target)
direction.y = 0
direction = direction.normalized()
if global_position.distance_to(Vector3(target.x, global_position.y, target.z)) > 0.5:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
var target_angle = atan2(velocity.x, velocity.z)
rotation.y = lerp_angle(rotation.y, target_angle, 10.0 * delta)
else:
current_path_index += 1
# --- ACTIONS DU CLIENT ---
func start_waiting() -> void:
ready_to_take_pie = true
current_state = State.WAITING
velocity = Vector3.ZERO
var p_icon = pie_icons[ desired_pie ]
var order_text = Sauvegarde.get_text("desire") + desired_pie
order_text += " [img=60]" + p_icon + "[/img]"
if desired_topping != "Aucun":
var t_icon = topping_icons[ desired_topping ]
order_text += "\n+ " + desired_topping + " [img=80]" + t_icon + "[/img]"
set_dialogue(order_text)
sprite.visible = true
# pour tests
#await get_tree().create_timer( 3 ).timeout
#leave_store()
# FONCTION MISE À JOUR : Le joueur doit donner la tarte ET le nappage
func give_item(pie_given: String, topping_given: String) -> void:
if current_state != State.WAITING:
return
#print("pie_given: ", pie_given, " | topping_given:", topping_given)
#print("desired_pie: ", desired_pie, " | desired_topping:", desired_topping)
# On vérifie les deux conditions
if pie_given == desired_pie and topping_given == desired_topping:
set_dialogue( Sauvegarde.get_text("goodpie") )
elif pie_given == desired_pie and topping_given != desired_topping:
set_dialogue( Sauvegarde.get_text("mixpie") )
else:
set_dialogue( Sauvegarde.get_text("badpie") )
leave_store()
func leave_store() -> void:
ready_to_take_pie = false
await get_tree().create_timer(2.0).timeout
sprite.visible = false
current_path_index = 0
current_state = State.LEAVING
func set_dialogue(text: String) -> void:
label.text = text
+1
View File
@@ -0,0 +1 @@
uid://biax3djgh5pyc
+25
View File
@@ -0,0 +1,25 @@
extends Node3D
@export var client : PackedScene
@export var entry_path: Array[Marker3D]
@export var exit_path: Array[Marker3D]
@onready var sprite_3d: Sprite3D = $Sprite3D
@onready var timer: Timer = $Timer
@onready var water_mill: MultiMeshInstance3D = $".."
func _ready() -> void:
sprite_3d.visible = false
func _on_timer_timeout() -> void:
var new_client = client.instantiate()
water_mill.add_child(new_client)
new_client.global_position = global_position
new_client.entry_path = entry_path
new_client.exit_path = exit_path
func start_timer():
timer.start()
+1
View File
@@ -0,0 +1 @@
uid://rm8aavdetmri
+117
View File
@@ -0,0 +1,117 @@
extends CanvasLayer
@onready var game_area: ColorRect = $BackGround/GameArea
@onready var target_rect: ColorRect = $BackGround/GameArea/TargetRect
@onready var feather: ColorRect = $BackGround/GameArea/Feather
@onready var player: CharacterBody3D = $".."
@onready var frog: CharacterBody3D = $"../../frog"
var cpt_col : int = -1
var is_playing : bool = false
var is_flashing : bool = false
var default_target_color : Color = Color(1.0, 1.0, 1.0, 0.6)
var tween : Tween
func _ready():
hide()
target_rect.color = default_target_color
func _input(event):
if Input.is_action_just_pressed("ui_accept") and player.can_play:
toggle_minigame()
# Contrôle de la plume avec la souris (uniquement sur Y)
if is_playing and event is InputEventMouseMotion:
var local_mouse_y = game_area.get_local_mouse_position().y
feather.position.y = clamp(local_mouse_y, 0, game_area.size.y - feather.size.y)
func toggle_minigame():
is_playing = !is_playing
if is_playing:
show()
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
player.camera_pov.make_current()
target_rect.position.y = game_area.size.y - target_rect.size.y
await get_tree().create_timer( 2 ).timeout
if is_playing:
player.can_play = false
start_pattern()
else:
player.can_play = true
player.camera.make_current()
hide()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if tween:
tween.kill()
if cpt_col == -1:
return
cpt_col/= 2
if cpt_col < 2:
frog.size = 1
elif cpt_col == 2 or cpt_col == 3:
if frog.size > 1.5:
frog.size = 1.5
else:
if frog.size > 2:
frog.size = 2
func start_pattern():
if tween:
tween.kill()
if is_playing:
tween = create_tween()
else:
return
var bottom_y = game_area.size.y - target_rect.size.y
var top_y = 0.0
tween.tween_property(target_rect, "position:y", top_y, 5)
tween.tween_interval(4.0)
tween.tween_property(target_rect, "position:y", bottom_y, 5)
tween.tween_interval(4.0)
await tween.finished
if cpt_col == -1:
cpt_col = 0
toggle_minigame()
func _process(delta):
if not is_playing:
return
# On vérifie si le centre de la plume est dans le rectangle
var feather_center = feather.position.y + (feather.size.y / 2.0)
var rect_top = target_rect.position.y
var rect_bottom = target_rect.position.y + target_rect.size.y
var is_inside = feather_center >= rect_top and feather_center <= rect_bottom
# Si elle est dehors et qu'on n'est pas déjà en train de clignoter en rouge
if not is_inside and not is_flashing and !player.can_play : # can_play juste pour le début
trigger_error()
func trigger_error():
is_flashing = true
cpt_col += 1
# Passage en rouge
target_rect.color = Color.RED
# On attend 0.5 secondes
await get_tree().create_timer(0.5).timeout
# Retour à la normale
target_rect.color = default_target_color
is_flashing = false
+1
View File
@@ -0,0 +1 @@
uid://dfli5tvn7us86
+89
View File
@@ -0,0 +1,89 @@
extends CharacterBody3D
@export var player: Node3D
@export var follow_speed: float = 4.0
@export var right_offset: float = 2.5
@export var forward_offset: float = 1
@export var car_cinematic : bool = false
@onready var anim_player: AnimationPlayer = $frog/AnimationPlayer
var size : float = 0.5
@onready var arms: MeshInstance3D = $frog/Armature/Skeleton3D/arms
@onready var body: MeshInstance3D = $frog/Armature/Skeleton3D/body
@onready var eyes: MeshInstance3D = $frog/Armature/Skeleton3D/eyes
@onready var feet: MeshInstance3D = $frog/Armature/Skeleton3D/feet
@onready var hands: MeshInstance3D = $frog/Armature/Skeleton3D/hands
@onready var legs: MeshInstance3D = $frog/Armature/Skeleton3D/legs
@onready var mesh = [ arms, body, eyes, feet, hands, legs ]
# Variables pour la gravité
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
func _ready() -> void:
if !car_cinematic:
scale = Vector3( size, size, size )
func _process(delta: float) -> void:
if car_cinematic:
if anim_player.current_animation != "idle":
anim_player.play("idle")
else:
scale_frog()
# transparent blue color
var dist = global_transform.origin.distance_to(player.camera.global_transform.origin)
for m in mesh:
for i in m.get_surface_override_material_count():
var mat = m.get_active_material(i)
if mat:
mat.set_shader_parameter("object_dist_to_camera", dist)
func _physics_process(delta):
if not player or car_cinematic:
return
# Appliquer la gravité
if not is_on_floor():
velocity.y -= gravity * delta
var target_position = player.global_position + (player.global_transform.basis.x * right_offset) + (-player.global_transform.basis.z * forward_offset)
global_position.y = target_position.y
var direction = global_position.direction_to(target_position)
var distance = global_position.distance_to(target_position)
if distance > 15: # tp
target_position = player.global_position + (player.global_transform.basis.x * right_offset) + (-player.global_transform.basis.z * (-forward_offset*4) )
global_position = target_position
velocity.x = 0
velocity.z = 0
elif distance > 0.5: # walk
velocity.x = direction.x * follow_speed
velocity.z = direction.z * follow_speed
else: # idle
velocity.x = 0
velocity.z = 0
var look_target = player.global_position
look_target.y = global_position.y
look_at(look_target, Vector3.UP)
var real_speed = Vector2(get_real_velocity().x, get_real_velocity().z).length()
# Si frog n'est pas bloquée par un mur
if real_speed > 0.1:
size += 0.0
if anim_player.current_animation != "walk":
anim_player.play("walk", 0.2)
else:
if anim_player.current_animation != "idle":
anim_player.play("idle", 0.2)
move_and_slide()
func scale_frog():
var tween = create_tween()
var final_scale = Vector3(size, size, size)
tween.tween_property(self, "scale", final_scale, 2)
+1
View File
@@ -0,0 +1 @@
uid://csfno2e8stbfd
+17
View File
@@ -0,0 +1,17 @@
extends Node
func _ready() -> void:
#Dialogic.timeline_ended.connect(set_player_cam)
pass
func change_cinematic_cam(camera_name:String):
var all_cinematic_cams:Array = get_tree().get_nodes_in_group("cinematic_cams")
for cam in all_cinematic_cams:
if cam.name == camera_name:
cam.make_current()
func set_player_cam():
return
var player = get_tree().get_first_node_in_group("player")
if player:
player.set_default_cam()
+1
View File
@@ -0,0 +1 @@
uid://badkx3jvh8r0y
+108 -17
View File
@@ -1,8 +1,7 @@
extends CharacterBody3D
var SPEED = 20
var JUMP_VELOCITY = 8
var SPEED = 7
@onready var arm: Node3D = $arm
@onready var model: Node3D = $model
@@ -24,16 +23,24 @@ var footstep_sounds: Array[AudioStream] = [
preload("res://assets/audio/footsteps/dirt_step_4.wav")
]
var footstep_timer: float = 0.0
const FOOTSTEP_DELAY: float = 0.15
const FOOTSTEP_DELAY: float = 0.24
@onready var pause: Control = $pause
# animations
@onready var anim_player: AnimationPlayer = $character/AnimationPlayer
# boulangerie MOULIN
var item : int = 0 # 0 rien, 1 abricot, 2 citron, 3 mirtylle
var topping : int = 0 # 0 rien, 1 meringue, 2 croisillon
@onready var area_tarte: Area3D = $Area3DTarte
@onready var tarte_holded: StaticBody3D = $tarte
var can_play : bool = true
func _ready():
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
label_tuto.text = Sauvegarde.get_text("tuto_1")
if third_person:
camera.make_current()
@@ -48,28 +55,70 @@ func _input(_event):
else:
pause.resume()
func _process(delta: float) -> void:
if Input.is_action_just_pressed("give"):
var objects_in_front = area_tarte.get_overlapping_bodies()
# check if there is a client in the box
for obj in objects_in_front:
if obj.is_in_group("client"):
if obj.get_parent().ready_to_take_pie:
var l = get_conversion_pie()
obj.get_parent().give_item( l[0], l[1] )
reset_pie()
return
# no client was found
for obj in objects_in_front:
if obj.is_in_group("tarte"):
set_item_topping( obj.item, obj.topping, obj.reset)
return
func _physics_process(delta: float) -> void:
# Add the gravity.
#print("global position; ", global_position)
if not is_on_floor():
velocity += get_gravity() * delta
# Handle jump
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
#var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
#if direction:
#velocity.x = direction.x * SPEED
#velocity.z = direction.z * SPEED
#else:
#velocity.x = move_toward(velocity.x, 0, SPEED)
#velocity.z = move_toward(velocity.z, 0, SPEED)
if input_dir.length() < 0.1:
input_dir = Vector2.ZERO
var direction = (spring_arm.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
# On ne déplace et on ne tourne le joueur que si une vraie direction est donnée
if can_play:
if direction.length() > 0.1:
anim_player.play("RootAction_001", -1, 3)
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
# 2. CORRECTION DE LA ROTATION INVERSÉE
# Si le joueur tourne toujours à l'envers, enlève les signes "-" devant velocity.x et velocity.z
var target_angle = atan2(-velocity.x, -velocity.z)
# Le 10.0 est bien, il permet au modèle 3D de se tourner vite sans faire de coup sec.
rotation.y = lerp_angle(rotation.y, target_angle, 10.0 * delta)
else:
anim_player.stop()
# Le joueur s'arrête doucement
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
anim_player.stop()
# footstep sounds
if is_on_floor() and Vector2(velocity.x, velocity.z).length() > 0.5:
if is_on_floor() and Vector2(velocity.x, velocity.z).length() > 0.5 and can_play:
footstep_timer -= delta
if footstep_timer <= 0.0:
play_random_footstep()
@@ -80,10 +129,52 @@ func _physics_process(delta: float) -> void:
if can_play:
move_and_slide()
# PARTIE BOULANGERIE MOULIN
func set_item_topping( new_item : int, new_topping : int, reset:bool ):
if reset:
tarte_holded.visible = false
else:
tarte_holded.visible = true
if new_item != 0 or reset:
item = new_item
tarte_holded.item = new_item
if new_topping != 0 or reset:
topping = new_topping
tarte_holded.topping = topping
tarte_holded.setup_color_and_name()
func reset_pie():
item = 0
tarte_holded.item = 0
topping = 0
tarte_holded.topping = 0
tarte_holded.visible = false
func get_conversion_pie():
var possible_pies = [Sauvegarde.get_text("pie1"), Sauvegarde.get_text("pie2"), Sauvegarde.get_text("pie3")]
var possible_toppings = [Sauvegarde.get_text("topping1"), Sauvegarde.get_text("topping2"), "Aucun"]
if item != 0:
return [ possible_pies[ item - 1 ], possible_toppings[ topping -1 ] ]
return [ "", possible_toppings[ topping -1 ] ]
func play_random_footstep( pitch=[0.85, 1.15] ) -> void:
var random_index = randi() % footstep_sounds.size()
audio_footstep.stream = footstep_sounds[random_index]
audio_footstep.pitch_scale = randf_range( pitch[0], pitch[1] )
audio_footstep.play()
func set_dialog_mode( npc_pos=Vector3.ZERO, force_pos=-1):
can_play = !can_play
spring_arm.set_dialog_mode( npc_pos, force_pos )
func set_default_cam():
if third_person:
camera.set_current(true)
else:
camera_pov.set_current(true)
+86 -7
View File
@@ -2,11 +2,90 @@ extends SpringArm3D
@onready var player: CharacterBody3D = $"../.."
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _unhandled_input(event: InputEvent) -> void:
if player.third_person and event is InputEventMouseMotion :
player.rotation.y -= event.relative.x * 0.005
player.rotation.y = wrapf( player.rotation.y, 0.0, TAU )
# --- Paramètres de mouvement ---
@export var max_follow_speed: float = 3.0
@export var speed_acceleration: float = 2.0
@export var height_offset: float = 2.5
@export var wait_time: float = 0.5
# --- Paramètres de Dialogue ---
@export var dialog_zoom_length: float = 2.0
@export var dialog_side_offset: float = 1.0
@export var transition_speed: float = 4.0 # Gère la vitesse du zoom, du décalage ET de la rotation en dialog
@onready var normal_spring_length: float = spring_length
var delay_timer: float = 0.0
var current_follow_speed: float = 0.0
var current_side_offset: float = 0.0
var target_side_offset: float = 0.0
var is_in_dialog: bool = false
func _ready() -> void:
top_level = true
func set_dialog_mode(npc_position: Vector3 = Vector3.ZERO, force_pos=-1) -> void:
is_in_dialog = !is_in_dialog
if is_in_dialog:
var direction_to_npc = (npc_position - player.global_position).normalized()
var right_dot = global_transform.basis.x.dot(direction_to_npc)
rotation.x -= event.relative.y * 0.005
rotation.x = clamp( rotation.x, -PI/2, PI/4 )
if right_dot >= 0 :
target_side_offset = dialog_side_offset
else:
target_side_offset = -dialog_side_offset
if force_pos == 0 :
target_side_offset = dialog_side_offset
elif force_pos == 1 :
target_side_offset = -dialog_side_offset
else:
target_side_offset = 0.0
func _physics_process(delta: float) -> void:
if not is_instance_valid(player):
return
var target_rotation_y = player.global_rotation.y
# ==========================================
# 1. GESTION DE LA ROTATION (Avant la position !)
# ==========================================
if is_in_dialog:
delay_timer = 0.3
current_follow_speed = 0.5
# La caméra tourne doucement pour se mettre dans le dos du joueur.
# On utilise transition_speed pour que ça se finisse en même temps que le zoom.
global_rotation.y = lerp_angle(global_rotation.y, target_rotation_y, transition_speed * delta)
else:
if player.can_play:
# Comportement normal d'exploration (Le vieux Zelda)
var angle_diff = abs(angle_difference(global_rotation.y, target_rotation_y))
if angle_diff > 0.02 and player.velocity.length() > 0.1:
delay_timer += delta
else:
delay_timer = 0.3
current_follow_speed = 0.5
if delay_timer >= wait_time:
current_follow_speed = move_toward(current_follow_speed, max_follow_speed, speed_acceleration * delta)
global_rotation.y = lerp_angle(global_rotation.y, target_rotation_y, current_follow_speed * delta)
# ==========================================
# 2. GESTION DU ZOOM ET DU DECALAGE
# ==========================================
var target_length = dialog_zoom_length if is_in_dialog else normal_spring_length
spring_length = lerp(spring_length, target_length, transition_speed * delta)
current_side_offset = lerp(current_side_offset, target_side_offset, transition_speed * delta)
# On récupère le vecteur "Droite" MAINTENANT, car la rotation vient juste d'être mise à jour au-dessus.
var right_direction = global_transform.basis.x
# On applique la position finale (Joueur + Hauteur + Décalage latéral)
global_position = player.global_position + Vector3(0, height_offset, 0) + (right_direction * current_side_offset)
+55
View File
@@ -0,0 +1,55 @@
extends StaticBody3D
@export var item : int
@export var topping : int
@export var reset : bool = false
@onready var creme_abricot: MeshInstance3D = $tarte/creme_abricot
@onready var creme_citron: MeshInstance3D = $tarte/creme_citron
@onready var creme_myrtille: MeshInstance3D = $tarte/creme_myrtille
@onready var meringue: MeshInstance3D = $tarte/meringue
@onready var croisillon: MeshInstance3D = $tarte/croisillon
@onready var label: Label = $Sprite3D/SubViewport/Label
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
setup_color_and_name()
func setup_color_and_name():
_reset()
if item != 0:
if item == 1:
creme_abricot.visible = true
label.text = Sauvegarde.get_text("pie1")
label.add_theme_color_override("font_color", Color.ORANGE)
elif item == 2:
creme_citron.visible = true
label.text = Sauvegarde.get_text("pie2")
label.add_theme_color_override("font_color", Color.LEMON_CHIFFON)
elif item == 3:
creme_myrtille.visible = true
label.text = Sauvegarde.get_text("pie3")
label.add_theme_color_override("font_color", Color.BLUE_VIOLET)
if topping != 0:
if topping == 1:
meringue.visible = true
label.text = Sauvegarde.get_text("topping1")
label.add_theme_color_override("font_color", Color.WHITE)
elif topping == 2:
croisillon.visible = true
label.text = Sauvegarde.get_text("topping2")
label.add_theme_color_override("font_color", Color.NAVAJO_WHITE)
if reset:
label.text = Sauvegarde.get_text("toppingreset")
func _reset():
creme_abricot.visible = false
creme_citron.visible = false
creme_myrtille.visible = false
meringue.visible = false
croisillon.visible = false
+1
View File
@@ -0,0 +1 @@
uid://bp52fs0u3dch3
+11
View File
@@ -0,0 +1,11 @@
extends CanvasLayer
@onready var animation_player: AnimationPlayer = $AnimationPlayer
func change_scene(target_scene_path: String) -> void:
animation_player.play("fade")
await animation_player.animation_finished
get_tree().change_scene_to_file( target_scene_path )
animation_player.play_backwards("fade")
+1
View File
@@ -0,0 +1 @@
uid://cc8d1t2rvmf6h
+262
View File
@@ -0,0 +1,262 @@
@tool
extends Node
class_name WaterSurfaceGenerator
@export var river_material: ShaderMaterial
@export var path: Path3D:
set(value):
_path = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _path
@export var width: float = 5.0:
set(value):
_width = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _width
@export var steps: int = 30:
set(value):
_steps = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _steps
@export var smoothness: float = 0.5:
set(value):
_smoothness = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _smoothness
@export var path_rotation_accurate: bool = true:
set(value):
_path_rotation_accurate = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _path_rotation_accurate
@export var double_sided: bool = false:
set(value):
_double_sided = value
if Engine.is_editor_hint():
_update_mesh()
get:
return _double_sided
@export var width_subdivisions: int = 0:
set(value):
_width_subdivisions = max(0, value)
if Engine.is_editor_hint():
_update_mesh()
get:
return _width_subdivisions
var _path: Path3D
var _width: float = 5.0
var _steps: int = 30
var _smoothness: float = 0.5
var _path_rotation_accurate: bool = true
var _double_sided: bool = true
var _width_subdivisions: int = 0
var _mesh_instance: MeshInstance3D
var _curve: Curve3D
func _ready():
_mesh_instance = MeshInstance3D.new()
_mesh_instance.visible = true
add_child(_mesh_instance)
_mesh_instance.owner = owner if owner else self
call_deferred("_update_mesh")
func _process(_delta):
if Engine.is_editor_hint():
_check_curve_changes()
func _check_curve_changes():
if not _path:
return
var new_curve = _path.curve
if new_curve != _curve:
_curve = new_curve
_update_mesh()
func _update_mesh():
if not _mesh_instance:
return
if not _path:
return
if not _path.curve:
return
if _path.curve.point_count < 2:
return
var mesh = _generate_river_mesh(_path.curve, _width, _steps, _smoothness, _path_rotation_accurate, _width_subdivisions)
if mesh:
_post_process_tangents(mesh, _path.curve, _path_rotation_accurate, _width_subdivisions)
_mesh_instance.mesh = mesh
if river_material:
river_material.set_shader_parameter("double_sided", _double_sided)
_mesh_instance.material_override = river_material
else:
var material = StandardMaterial3D.new()
material.cull_mode = BaseMaterial3D.CULL_DISABLED if _double_sided else BaseMaterial3D.CULL_BACK
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
material.albedo_color = Color(0.5, 0.7, 1.0, 0.5)
_mesh_instance.material_override = material
else:
_mesh_instance.mesh = null
func _post_process_tangents(mesh: ArrayMesh, curve: Curve3D, path_rotation_accurate: bool, width_subdivisions: int):
if not mesh or mesh.get_surface_count() == 0:
return
var arrays = mesh.surface_get_arrays(0)
var vertices = arrays[ArrayMesh.ARRAY_VERTEX]
var tangents = arrays[ArrayMesh.ARRAY_TANGENT]
var curve_length = curve.get_baked_length()
var closed_loop = curve.closed
var vertices_per_segment = width_subdivisions + 2
var vertex_count = vertices.size() / vertices_per_segment
var actual_steps = vertex_count - 1 if not closed_loop else vertex_count
var curve_tangents = []
for i in range(vertex_count):
var t = float(i) / float(actual_steps)
var offset = t * curve_length
var forward_vector: Vector3
if path_rotation_accurate:
if curve.has_method("sample_baked_tangent"):
forward_vector = curve.sample_baked_tangent(offset).normalized()
else:
var t_forward = min(t + 0.01, 1.0)
var t_backward = max(t - 0.01, 0.0)
var pos_forward = curve.sample_baked(t_forward * curve_length)
var pos_backward = curve.sample_baked(t_backward * curve_length)
forward_vector = (pos_forward - pos_backward).normalized()
else:
var t_forward = min(t + 0.01, 1.0)
var t_backward = max(t - 0.01, 0.0)
var pos_forward = curve.sample_baked(t_forward * curve_length)
var pos_backward = curve.sample_baked(t_backward * curve_length)
forward_vector = (pos_forward - pos_backward).normalized()
curve_tangents.append(forward_vector)
for i in range(vertex_count):
var tangent = curve_tangents[i]
for j in range(vertices_per_segment):
var vertex_idx = i * vertices_per_segment + j
var t_idx = vertex_idx * 4
tangents[t_idx] = tangent.x
tangents[t_idx + 1] = tangent.y
tangents[t_idx + 2] = tangent.z
tangents[t_idx + 3] = 1.0
arrays[ArrayMesh.ARRAY_TANGENT] = tangents
mesh.clear_surfaces()
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
static func _generate_river_mesh(curve: Curve3D, width: float, steps: int, smoothness: float, path_rotation_accurate: bool, width_subdivisions: int = 0) -> ArrayMesh:
if not curve or curve.point_count < 2:
return null
if curve.bake_interval == 0:
curve.bake_interval = 0.1
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
var curve_length = curve.get_baked_length()
var subdivisions = int(smoothness * 4.0)
var actual_steps = steps * (subdivisions + 1)
var vertex_count = actual_steps + 1
var first_tangent: Vector3
var closed_loop = curve.closed
for i in range(vertex_count):
var t = float(i) / float(actual_steps)
var offset = t * curve_length
var position = curve.sample_baked(offset)
var forward_vector: Vector3
if path_rotation_accurate:
if curve.has_method("sample_baked_tangent"):
forward_vector = curve.sample_baked_tangent(offset).normalized()
else:
var t_forward = min(t + 0.01, 1.0)
var t_backward = max(t - 0.01, 0.0)
var pos_forward = curve.sample_baked(t_forward * curve_length)
var pos_backward = curve.sample_baked(t_backward * curve_length)
forward_vector = (pos_forward - pos_backward).normalized()
else:
var t_forward = min(t + 0.01, 1.0)
var t_backward = max(t - 0.01, 0.0)
var pos_forward = curve.sample_baked(t_forward * curve_length)
var pos_backward = curve.sample_baked(t_backward * curve_length)
forward_vector = (pos_forward - pos_backward).normalized()
if i == 0:
first_tangent = forward_vector
elif closed_loop and i == vertex_count - 1:
forward_vector = first_tangent
var right_vector = forward_vector.cross(Vector3.UP).normalized()
var uv_t = offset / curve_length
var vertices_per_segment = width_subdivisions + 2
for j in range(vertices_per_segment):
var width_t = float(j) / float(width_subdivisions + 1)
var vertex_pos = position - right_vector * width * 0.5 + right_vector * width * width_t
st.set_normal(Vector3.UP)
st.set_uv(Vector2(uv_t, width_t))
st.add_vertex(vertex_pos)
var triangle_count = vertex_count - 1
var vertices_per_segment = width_subdivisions + 2
for i in range(triangle_count):
var base_idx = i * vertices_per_segment
for j in range(width_subdivisions + 1):
var v0 = base_idx + j
var v1 = base_idx + j + 1
var v2 = base_idx + vertices_per_segment + j
var v3 = base_idx + vertices_per_segment + j + 1
st.add_index(v0)
st.add_index(v2)
st.add_index(v1)
st.add_index(v1)
st.add_index(v2)
st.add_index(v3)
st.generate_tangents()
return st.commit()
+1
View File
@@ -0,0 +1 @@
uid://sux5bo8rvnto