Files
Princesse-Lactose-Godot/princesse.gd
Thomas Lavocat e963ececf9 princesse/comamndes: ajout du dash
La princesse peut dasher à volonté pour l'instant. Il reste à relier ça
avec la consomation de fromage pour les pêts.
2023-04-30 20:21:34 +02:00

451 lines
16 KiB
GDScript

class_name Princess extends CharacterBody2D
## The player listens for input actions appended with this suffix.[br]
## Used to separate controls for multiple players in splitscreen.
@export var action_suffix := ""
var gravity: int = ProjectSettings.get("physics/2d/default_gravity")
@onready var wall_detect_left := $wall_detect_left as RayCast2D
@onready var wall_detect_right := $wall_detect_right as RayCast2D
@onready var ground_far_detect := $ground_far_detect as RayCast2D
@onready var animation := $AnimatedSprite2D as AnimatedSprite2D
@onready var camera := $Camera2D as Camera2D
################################################################################
#
# Constantes de déplacement à pimper dans l'inspecteur
#
################################################################################
@export var WALKING_SPEED = 155
@export var FALLING_SPEED = 230
@export var JUMPING_SPEED = 220
var DASH_SPEED = WALKING_SPEED * 2
@export var X_SPEED_TABLE = [0, 0.1, 0.15, 0.2, 0.3, 0.6, 0.9, 1]
@export var X_SPEED_DECEL = [0, 0.1, 0.6, 1]
@export var FALL_SPEED_TABLE = [0, 0.1, 0.15, 0.2, 0.3, 0.6, 0.9, 1]
@export var JUMP_SPEED_TABLE = [0, 0.1, 0.2, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
@export var KICK_SPEED_TABLE = [0.6, 0.7, 0.8, 0.9, 1, 1, 1, 1, 1, 1]
@export var DASH_SPEED_TABLE = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1, 1, 1, 1, 2, 2, 2]
# Nombre d'incrément à rajouter lorsque la touche de saut est maintenue
@export var JUMPING_COUNTER_REFILL = 2
# Tous les combien d'incréments rajouter un refill sur le compteur
@export var JUMPING_KEY_COUNTER_THRESHOLD = 3
@export var KICK_JUMP_LIMITER = 0.75
# Nombre de frames coyote durant lesquelle le joueur peut encore sauter
# sans encore être au sol
@export var COYOTE_LENGTH := 15
# Différence de vitesse de ralentissement entre le sol et l'air
@export var WALK_INCR_GROUND : int = 1
@export var WALK_INCR_AIR : int = 3
signal cheese_collected()
################################################################################
#
# Etat de la princesse
#
################################################################################
# Direction dans laquelle est positioné le personnage.
var direction : int = 1
var walking : bool = false # si le joueur veut marche ou non
var walking_step: int = -1 # où la princesse en est dans son tableau d'accel
var walk_incr_reserve : int = 0 # utilisé pour la différence ralentissement air/sol
var init_decel: bool = true # utilisé pour initialiser la décélération
var init_direction_change = true # utilisé pour initialiser le changement de direction
# variables d'état relatives au saut
var jumping : bool = false # Princesse est-elle en train de sauter ?
var need_jump : bool = false # Le joueur veut-il sauter et peut il ?
var jumping_step : int = -1 # Où en est la princesse dans son tableau d'accel
var jump_key_counter : int = 0 # Où en est-on du refil ?
# variables d'état relatives au kick mural
var kicking : bool = false
var kick_step : int = -1
var kick_direction : int = 1
# Coyote time
var coyote_ground = []
# variable d'état relative à la gravité
var falling_step : int = -1 # Où en est la princess dans son acceleration de chute ?
# Variables d'état relative à l'accroche au mur
var pressing_wall = false # Princesse est elle en contact avec un mur ?
var grab_wall = false # Je joueur veut-il et peut-il s'accrocher au mur ?
# Variables d'état relative au dash
var dashing : bool = false
var dash_step : int = -1
var dash_direction_x : int = 1
var dash_direction_y : int = 1
################################################################################
#
# Gestion d'avec quoi Princesse collisionne
#
################################################################################
const PLATFORM_LAYER = 1 << (5 - 1) # collision layer 5 -> plateformes
const PICS_BLOCK_LAYER = 1 << (6 - 1) # collision layer 6 -> pics
const CHEESE_LAYER = 1 << (6 - 1) # collision layer 7 -> fromages
var layer_of_collision = null
func init_walk_state():
walk_incr_reserve = 0
walking_step = -1
init_decel = false
func walk(direction:int) -> float:
# Fait marcher le personnage.
if kicking or dashing:
init_walk_state()
return velocity.x
if velocity.x == 0 and not walking:
init_walk_state()
# Si le personnage est dans l'air, il aura une friction plus faible lors de
# la décélération.
var threshold = WALK_INCR_AIR
if is_on_floor():
threshold = WALK_INCR_GROUND
# Un changement de direction implique de perdre la vélocité dans la direction
# précédente avant de repartir dans la direction que l'on veut.
# ça ne change rien pour le nombre de frames nécéssaires pour accélérer dans
# tous les cas. Mais le feeling est meilleur.
var direction_change: bool = (direction > 0 and velocity.x < 0) or (direction < 0 and velocity.x > 0)
if direction_change:
if init_direction_change:
init_direction_change = false
walking_step = -1
else:
init_direction_change = true
# Si le joueur décide de marcher alors, le compteur de pas doit s'incrémenter
# Si le joueur ne veut plus marcher, alors, le compteur de pas décrémente
# La vitese choisie est le numéro d'étape dans le tableau correspondant
# X_SPEED_TABLE pour l'accélération
# X_SPEED_DECEL pour la décélération
var table = X_SPEED_TABLE
if not walking:
table = X_SPEED_DECEL
# dans le cas où le personnage est propulsé par une force extérieure et
# qu'il n'y a pas d'input du joueur. Lorsque le joueur va vouloir reprendre
# la main, il faut démarrer à la bonne vélocité
var abs_v_x = abs(velocity.x)
if abs_v_x != 0:
if walking_step == -1:
# trouver l'indice le plus proche de la vitesse courante
for index in range(0, table.size()-1):
var speed_i = table[walking_step] * WALKING_SPEED
var speed_i1 = table[walking_step+1] * WALKING_SPEED
# lorsque l'on a trouvé l'indice le bon endroit dans le tableau, alors
# on renvoie l'indice trouvé
if abs_v_x == speed_i:
walking_step = index
if abs_v_x == speed_i1:
walking_step = index+1
if abs_v_x > speed_i and abs_v_x < speed_i1:
walking_step = index+1
# si rien n'est trouvé, alors on initialise l'indice au maximum possible
if walking_step == -1:
walking_step = table.size() - 1
if walking:
walking_step = min(walking_step+1, X_SPEED_TABLE.size() -1)
init_decel = true
else:
# Lors de la première frame de la décélération, initialiser la valeur
# du compteur d'incrément en haut du tableau de décélération
if init_decel:
walking_step = min(walking_step, X_SPEED_DECEL.size() - 1)
init_decel = false
# Si le compteur d'incrément est supérieur ou égal à zéro, c'est qu'il
# faut bouger, donc il est nécéssaire en premier temps de récupérer la vitesse
# à l'indice courant. Puis si le joueur ne veut plus accélérer, alors, appliquer
# la décélération
if walking_step >= 0:
var speed = table[walking_step] * WALKING_SPEED
if not walking:
walk_incr_reserve += 1
# appliquer le nombre de frames nécéssaire pour décrémenter, dépend
# de où est le personnage (air vs sol)
if walk_incr_reserve >= threshold:
walk_incr_reserve = 0
walking_step-=1
if direction_change and walking:
# dans le cas du changement de direction,
return speed * direction + velocity.x
else:
return speed * direction
else:
init_walk_state()
return velocity.x
func fall() -> float:
# fait tomber princesse
if jumping or kicking or dashing:
falling_step = -1
return velocity.y
if is_on_floor_only():
if get_floor_normal()[0] < 0: # pente à gauche
if direction >= 0:# on va à droite, désactive la gravité
falling_step = -1
return velocity.y
else: # on va à gauche, gravité à fond
falling_step = FALL_SPEED_TABLE.size()-1
else: # pente à droite
if direction > 0:# on va à droite, active la gravité à fond
falling_step = FALL_SPEED_TABLE.size()-1
else: # on va à gauche, désactive la gravité
falling_step = -1
return velocity.y
else:
if grab_wall:
falling_step = max(falling_step-1, 1)
else:
falling_step = min(falling_step+1, FALL_SPEED_TABLE.size()-1)
return FALL_SPEED_TABLE[falling_step] * FALLING_SPEED
func jump() -> float:
# fait sauter princesse
if not jumping or kicking or dashing:
return velocity.y
if not is_on_ceiling() and jump_key_counter > 0 and jump_key_counter % JUMPING_KEY_COUNTER_THRESHOLD == 0:
jumping_step = min(
jumping_step + JUMPING_COUNTER_REFILL,
JUMP_SPEED_TABLE.size() -1
)
if jumping_step > 0:
jumping_step -= 1
return JUMP_SPEED_TABLE[jumping_step] * JUMPING_SPEED * -1
else:
end_jump()
return velocity.y
func end_jump():
# termine le saut de Princesse
jumping = false
jumping_step = -1
jump_key_counter = 0
func kick() -> void:
# fait kicker la princesse
if not kicking or dashing:
kick_step -= 1
kicking = false
return
if kick_step > 0:
kick_step -= 1
velocity.y = KICK_SPEED_TABLE[kick_step] * JUMPING_SPEED * -1 * KICK_JUMP_LIMITER
velocity.x = KICK_SPEED_TABLE[kick_step] * WALKING_SPEED * kick_direction
velocity.rotated(45)
else:
kick_step = -1
kicking=false
velocity.rotated(0)
func dash() -> void:
# fait dasher la princesse
if not dashing:
return
if dash_step >= 0:
dash_step -= 1
var doubley = 1
var doublex = 1
if abs(dash_direction_y) and not abs(dash_direction_x):
doubley=1.1
if abs(dash_direction_x) and not abs(dash_direction_y):
doublex=1.1
velocity.y = DASH_SPEED_TABLE[dash_step] * DASH_SPEED * dash_direction_y * doubley
velocity.x = DASH_SPEED_TABLE[dash_step] * DASH_SPEED * dash_direction_x * doublex
else:
cancel_dash()
func cancel_dash() -> void:
dash_step = -1
dashing=false
func choose_animation_orientation() -> void:
# Oriente l'animation correctement en fonction de laquelle on joue et de
# la direction du personnage
if not is_zero_approx(velocity.x):
if velocity.x > 0.0:
animation.scale.x = 1.0
else:
animation.scale.x = -1.0
if grab_wall:
if wall_detect_left.is_colliding():
animation.scale.x = 1
elif wall_detect_right.is_colliding():
animation.scale.x = -1
func play_animation() -> void:
# Joue l'animation de Princesse et dans le bon sens
choose_animation_orientation()
var anim := get_new_animation()
if anim != animation.animation:
animation.animation = anim
animation.play()
func move_and_handle_collisions() -> void:
# Bouge Princesse et réagis aux éléments avec lesquels elle rentre en collision
move_and_slide()
for i in get_slide_collision_count():
var collider : KinematicCollision2D= get_slide_collision(i)
var tile_rid = collider.get_collider_rid()
layer_of_collision = PhysicsServer2D.body_get_collision_layer(tile_rid)
if layer_of_collision == PICS_BLOCK_LAYER:
get_tree(). reload_current_scene()
if dashing and not is_on_floor():
cancel_dash()
func get_coyote(table: Array):
var result = false
for i in table:
result = result or i
return result
func read_input() -> void:
# Lis les commandes du joueur pour piloter Princesse
# Le joueur veut-il et peut-il s'accrocher au mur ?
grab_wall = pressing_wall and Input.is_action_pressed("grab" + action_suffix)
# Le joueur veut-il sauter ou kicker ?
if Input.is_action_just_pressed("jump" + action_suffix):
# Peut-il sauter ?
if ground_far_detect.is_colliding() or (is_on_floor() or get_coyote(coyote_ground)):
need_jump=true
# Peut-il kicker ?
if grab_wall:
if not kicking:
kicking=true
kick_step = KICK_SPEED_TABLE.size()-1
if wall_detect_left.is_colliding():
kick_direction = 1
else:
kick_direction = -1
# Dès qu'il peut et veut sauter, déclencher le saut
if need_jump and (is_on_floor() or get_coyote(coyote_ground)):
need_jump = false
if not jumping:
jumping = true
jumping_step = JUMP_SPEED_TABLE.size()-1
jump_key_counter = 0
# Si il continue d'appuyer, lui rallonger son saut
if Input.is_action_pressed("jump" + action_suffix):
if jumping:
jump_key_counter += 1
else:
jump_key_counter = 0
# Le joueur veut-il marcher ?
walking = (
Input.is_action_pressed("move_left" + action_suffix) or
Input.is_action_pressed("move_right" + action_suffix)
)
# Calculer dans quelle direction il veut marcher
if walking:
var axis = Input.get_axis(
"move_left" + action_suffix,
"move_right" + action_suffix
)
if not is_zero_approx(axis):
if axis < 0:
direction = -1
else:
direction = 1
else:
if kicking:
direction = kick_direction
if Input.is_action_just_pressed("dash" + action_suffix):
if not dashing:
dashing = true
dash_step = DASH_SPEED_TABLE.size()-1
var axis_x = Input.get_axis(
"move_left" + action_suffix,
"move_right" + action_suffix
)
if not is_zero_approx(axis_x):
if axis_x < 0:
dash_direction_x = -1
else:
dash_direction_x = 1
else:
dash_direction_x = 0
var axis_y = Input.get_axis(
"move_up" + action_suffix,
"move_down" + action_suffix
)
if not is_zero_approx(axis_y):
if axis_y < 0:
dash_direction_y = -1
else:
dash_direction_y = 1
else:
dash_direction_y = 0
print("dash x", axis_x, "y", axis_y)
func compute_state() -> void:
# Met à jour une partie de l'état de la princesse
# gestion du coyote time sur le contact au sol
coyote_ground.append(is_on_floor())
if coyote_ground.size() > COYOTE_LENGTH:
coyote_ground.remove_at(0)
# gestion de l'état de la pression au mur
pressing_wall = not is_on_floor() and (wall_detect_left.is_colliding() or wall_detect_right.is_colliding())
func get_new_animation() -> String:
# Renvoie la bonne annimation en fonction de l'état de la princesse
var animation_new: String
if is_on_floor():
if walking_step > 0:
animation_new = "walk"
else:
animation_new = "idle"
else:
if velocity.y > 0.0:
if grab_wall:
animation_new = "wall_stick"
else:
if walking_step > 0:
animation_new = "falling_diagonals"
else:
animation_new = "falling_straight"
else:
animation_new = "jumping"
return animation_new
func _physics_process(delta: float) -> void:
compute_state()
read_input()
velocity.y = jump()
velocity.y = fall()
velocity.x = walk(direction)
kick()
dash()
move_and_handle_collisions()
play_animation()
func reload_fart() -> void:
Input.start_joy_vibration(0, 0, 1, 0.1)