diff options
Diffstat (limited to 'player.gd')
-rw-r--r-- | player.gd | 581 |
1 files changed, 436 insertions, 145 deletions
@@ -2,12 +2,18 @@ class_name Player extends CharacterBody2D -const SPEED = 100.0 -const JUMP_VELOCITY = -225.0 +const SPEED = 80.0 +const JUMP_VELOCITY = -200.0 const AIR_SPEED = SPEED * 0.75 -const FLY_VELOCITY = -100 +const FLY_VELOCITY = -80 +const HURT_VELOCITY = SPEED * 5 +const MAX_GRAVITY = 300.0 +const MAX_GRAVITY_AIR = 100.0 var previous_velocity: Vector2 = Vector2.ZERO +var jump_height_modifier: float +var last_direction: float = 1.0 +var max_gravity: float = MAX_GRAVITY var current_state: Dictionary = state_idle(): set(new_state): @@ -16,21 +22,54 @@ var current_state: Dictionary = state_idle(): new_state.enter.call() current_state = new_state +var max_hp = 2 +var hp = max_hp : + set(value): + hp = value + get_tree().current_scene.get_node("CanvasLayer/PanelContainer/Label").text = "HP: " + str(hp) + +var sprite_small_star_base: Sprite2D +var air_shot_base: Area2D +var star_shot_base: Area2D + +var is_inflated = false +var inhale_min_duration = 0.5 +var does_inhale_transition = false + +var entered_door: Node2D + func _ready(): current_state = state_idle() + + sprite_small_star_base = $SpriteSmallStar.duplicate() + $SpriteSmallStar.queue_free() + + air_shot_base = $AirShot.duplicate() + $AirShot.queue_free() + + star_shot_base = $StarShot.duplicate() + $StarShot.queue_free() + + hp = max_hp # to set UI labels + + $CollisionArea.add_child($CollisionShape2D.duplicate()) + func _physics_process(delta: float) -> void: # Add the gravity. if not is_on_floor(): velocity += get_gravity() * delta + velocity.y = min(velocity.y, max_gravity) previous_velocity = velocity current_state.process.call(delta) + func apply_directional_movement(speed = SPEED): var direction = Input.get_axis("ui_left", "ui_right") if direction: + last_direction = direction if direction < 0: $AnimatedSprite2D.flip_h = true else: @@ -40,10 +79,104 @@ func apply_directional_movement(speed = SPEED): return direction +func apply_jump_height_modification(): + if Input.is_action_pressed("jump"): + jump_height_modifier += 10 + if jump_height_modifier > 0: + jump_height_modifier = 0 + if Input.is_action_just_released("jump"): + velocity.y -= jump_height_modifier + + +func spawn_small_star(): + var star := sprite_small_star_base.duplicate() + star.global_position = global_position + get_tree().current_scene.add_child(star) + var tween = get_tree().create_tween() + var target_x = randi_range(-1, 1) * 16 + var target_y = (randi_range(-1, 1) if target_x != 0 else 1) * 16 + tween.tween_property( + star, + "global_position", + star.global_position + Vector2(target_x, target_y), + 0.1 + ) + tween.tween_callback(func(): + await get_tree().create_timer(0.2).timeout + star.queue_free() + ) + +func air_shot(direction: float): + var shot = air_shot_base.duplicate() as Area2D + shot.global_position = global_position + get_tree().current_scene.add_child(shot) + + %SoundAirShot.play() + shot.get_node("Sprite2D").flip_h = direction < 0 + + var tween = get_tree().create_tween() + tween.tween_property( + shot, + "global_position", + shot.global_position + Vector2(sign(direction) * 64, 0), + 0.3 + ).set_ease(Tween.EASE_OUT) + tween.tween_callback(func(): + await get_tree().create_timer(0.3).timeout + shot.queue_free() + ) + +func star_shot(direction: float): + var shot = star_shot_base.duplicate() as Area2D + shot.global_position = global_position + get_tree().current_scene.add_child(shot) + + %SoundStarShot.play() + shot.get_node("AnimatedSprite2D").play() + + var tween = get_tree().create_tween() + + shot.body_entered.connect(func(body): + %ShotObstacleHit.play() + tween.stop() + shot.queue_free() + body.queue_free() + ) + + tween.tween_property( + shot, + "global_position", + shot.global_position + Vector2(sign(direction) * get_viewport_rect().size.x, 0), + 0.5 + ) + tween.tween_callback(func(): + await get_tree().create_timer(0.3).timeout + if is_instance_valid(shot): + shot.queue_free() + ) + + +func check_door_enter(): + var areas = $CollisionArea.get_overlapping_areas() + if areas.size() > 0 and areas[0].is_in_group("door"): + if Input.is_action_just_pressed("up"): + entered_door = areas[0].get_parent() + return areas[0].get_parent() + + return null + + +func _on_hurt_area_body_entered(body: Node2D) -> void: + if body.is_in_group("enemy"): + current_state.id = "" # to force trigger state change into hurt + current_state = state_hurt({"collider": body}) + + func state_idle(): return { "id": "idle", "enter": func(): + is_inflated = false $AnimatedSprite2D.play("idle") , "process": func(_delta): @@ -54,7 +187,9 @@ func state_idle(): if direction: current_state = state_walk() - if Input.is_action_just_pressed("jump"): + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): current_state = state_jump() if not is_on_floor(): @@ -85,7 +220,9 @@ func state_walk(): else: current_state = state_idle() - if Input.is_action_just_pressed("jump"): + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): current_state = state_jump() if not is_on_floor(): @@ -94,6 +231,9 @@ func state_walk(): if Input.is_action_pressed("duck"): current_state = state_duck() + + if Input.is_action_just_pressed("inhale_exhale"): + current_state = state_inhale() , "exit": func(): pass @@ -105,19 +245,25 @@ func state_jump(): return { "id": "jump", "enter": func(): - $SoundJump.play() + %SoundJump.play() $AnimatedSprite2D.play("jump") velocity.y = JUMP_VELOCITY + jump_height_modifier = JUMP_VELOCITY , "process": func(_delta): - apply_directional_movement(AIR_SPEED) + apply_jump_height_modification() + apply_directional_movement() move_and_slide() if Input.is_action_just_pressed("jump"): velocity.y = FLY_VELOCITY + %SoundFly.play() current_state = state_fly_idle() - if velocity.y >= 0: + elif Input.is_action_just_pressed("inhale_exhale"): + current_state = state_inhale() + + elif velocity.y >= 0: current_state = state_fall() , "exit": func(): @@ -138,20 +284,24 @@ func state_fall(): elif Input.is_action_just_pressed("jump"): velocity.y = FLY_VELOCITY + %SoundFly.play() current_state = state_fly_idle() - if velocity.y > abs(JUMP_VELOCITY) * 2: - get_node("AnimatedSprite2D").play("fall") + if Input.is_action_just_pressed("inhale_exhale"): + current_state = state_inhale() + + if velocity.y >= MAX_GRAVITY: + $AnimatedSprite2D.play("fall") apply_directional_movement(AIR_SPEED) move_and_slide() if is_on_floor(): - if previous_velocity.y > abs(JUMP_VELOCITY): - #$SoundFall.volume_db = linear_to_db( - #clamp(abs(previous_velocity.y) / abs(JUMP_VELOCITY*2), 0.5, 1.0) - #) - $SoundFall.play() + if $AnimatedSprite2D.animation == "fall": + %SoundFall2.play() + else: + %SoundFall.play() + spawn_small_star() current_state = state_idle() , "exit": func(): @@ -180,14 +330,18 @@ func state_fly_idle(): return { "id": "fly_idle", "enter": func(): + max_gravity = MAX_GRAVITY_AIR $AnimatedSprite2D.play("fly") , "process": func(delta): velocity -= (get_gravity() * delta) / 1.5 - if Input.is_action_just_pressed("jump"): + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): velocity.y = FLY_VELOCITY $AnimatedSprite2D.play("fly") + %SoundFly.play() velocity.x = move_toward(velocity.x, 0, SPEED) move_and_slide() @@ -197,15 +351,11 @@ func state_fly_idle(): current_state = state_fly_walk() if Input.is_action_just_pressed("inhale_exhale"): - $SoundExhale.play() - current_state = state_idle() - - if is_on_floor(): + air_shot(last_direction) current_state = state_idle() - # current_state = state_inflated_idle() , "exit": func(): - pass + max_gravity = MAX_GRAVITY , } @@ -214,15 +364,20 @@ func state_fly_walk(): return { "id": "fly_walk", "enter": func(): + max_gravity = MAX_GRAVITY_AIR $AnimatedSprite2D.play("fly") , "process": func(delta): velocity -= (get_gravity() * delta) / 1.5 + velocity.y = min(velocity.y, 100) - if Input.is_action_just_pressed("jump"): + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): velocity.y = FLY_VELOCITY + %SoundFly.play() - var direction = apply_directional_movement() + var direction = apply_directional_movement(AIR_SPEED) if direction: $AnimatedSprite2D.play("fly") move_and_slide() @@ -230,15 +385,11 @@ func state_fly_walk(): current_state = state_fly_idle() if Input.is_action_just_pressed("inhale_exhale"): - $SoundExhale.play() - current_state = state_walk() - - if is_on_floor(): + air_shot(last_direction) current_state = state_walk() - # current_state = state_inflated_walk() , "exit": func(): - pass + max_gravity = MAX_GRAVITY , } @@ -253,137 +404,277 @@ func state_inhale(): return { "id": "inhale", "enter": func(): + $InhaleMinDurationTimer.wait_time = inhale_min_duration + $InhaleMinDurationTimer.start() + #$AnimationPlayer.play("inhale") $AnimatedSprite2D.play("inhale") - $SoundInhale.play() - $SoundInhale.finished.connect(func(): + %SoundInhale.play() + %SoundInhale.finished.connect(func(): #$SoundInhaleContinue.play() - $SoundInhale.play() + %SoundInhale.play() + ) + + %InhaleArea.position.x = abs(%InhaleArea.position.x) * sign(last_direction) + + %InhaleParticles.process_material.gravity.x = - ( + abs(%InhaleParticles.process_material.gravity.x) * sign(last_direction) ) + %InhaleParticles.position.x = abs(%InhaleParticles.position.x) * sign(last_direction) + %InhaleParticles.restart() + %InhaleParticles.emitting = true , "process": func(_delta): - if Input.is_action_just_released("inhale_exhale"): + velocity.x = move_toward(velocity.x, 0, SPEED) + move_and_slide() + + var bodies = %InhaleArea.get_overlapping_bodies() + if bodies.size() > 0: + does_inhale_transition = true + for body in bodies: + body.queue_free() + # todo: stop process? -> remove collision -> inhale aka move towards center player -> queue_free + await get_tree().create_timer(inhale_min_duration).timeout + current_state = state_inflated_idle() + return + + if ( + $InhaleMinDurationTimer.is_stopped() and not does_inhale_transition and + (not Input.is_action_pressed("inhale_exhale") or Input.is_action_just_released("inhale_exhale")) + ): current_state = state_idle() , "exit": func(): - $SoundInhale.stop() + %SoundInhale.stop() + %InhaleParticles.emitting = false + does_inhale_transition = false , } func state_inflated_idle(): - return {} + return { + "id": "inflated_idle", + "enter": func(): + is_inflated = true + $AnimatedSprite2D.play("inflated_idle") + , + "process": func(_delta): + velocity.x = move_toward(velocity.x, 0, SPEED) + move_and_slide() + + var direction = Input.get_axis("ui_left", "ui_right") + if direction: + current_state = state_inflated_walk() + + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): + current_state = state_inflated_jump() + + if not is_on_floor(): + #current_state = state_inflated_fall() + current_state = state_inflated_jump() + + if Input.is_action_pressed("duck"): + current_state = state_digest() + + if Input.is_action_just_pressed("inhale_exhale"): + star_shot(last_direction) + current_state = state_idle() + , + "exit": func(): + is_inflated = false + , + } func state_inflated_walk(): - return {} + return { + "id": "inflated_walk", + "enter": func(): + is_inflated = true + $AnimatedSprite2D.play("inflated_walk") + , + "process": func(_delta): + var direction = apply_directional_movement() + if direction: + move_and_slide() + else: + current_state = state_inflated_idle() + + if check_door_enter(): + current_state = state_enter_door() + elif Input.is_action_just_pressed("jump"): + current_state = state_inflated_jump() + + if not is_on_floor(): + $CoyoteTimer.start() + current_state = state_inflated_fall() + + if Input.is_action_pressed("duck"): + current_state = state_duck() + + if Input.is_action_just_pressed("inhale_exhale"): + star_shot(last_direction) + current_state = state_idle() + , + "exit": func(): + is_inflated = false + , + } + + +func state_inflated_jump(): + return { + "id": "inflated_jump", + "enter": func(): + is_inflated = true + %SoundJump.play() + $AnimatedSprite2D.play("inflated_jump") + velocity.y = JUMP_VELOCITY + jump_height_modifier = JUMP_VELOCITY + , + "process": func(_delta): + apply_jump_height_modification() + apply_directional_movement() + move_and_slide() + + if Input.is_action_just_pressed("inhale_exhale"): + star_shot(last_direction) + current_state = state_fall() + + elif velocity.y >= 0: + current_state = state_inflated_fall() + , + "exit": func(): + is_inflated = false + , + } + + +func state_inflated_fall(): + return { + "id": "fall", + "enter": func(): + is_inflated = true + $AnimatedSprite2D.play("inflated_jump") + , + "process": func(_delta): + if not $CoyoteTimer.is_stopped() and Input.is_action_just_pressed("jump"): + current_state = state_inflated_jump() + + if Input.is_action_just_pressed("inhale_exhale"): + star_shot(last_direction) + current_state = state_fall() + + apply_directional_movement(AIR_SPEED) + move_and_slide() + + if is_on_floor(): + %SoundFall.play() + spawn_small_star() + current_state = state_inflated_idle() + , + "exit": func(): + is_inflated = false + , + } + + +func state_digest(): + return { + "id": "digest", + "enter": func(): + %SoundDigest.play() + is_inflated = false + , + "process": func(_delta): + current_state = state_duck() + , + "exit": func(): + pass + , + } -#class StateIdle: - #var player: Player - # - #func _init(_player): - #player = _player - #func _get_class(): - #return "StateIdle" - # - #func enter(): - #player.get_node("AnimatedSprite2D").play("idle") - # - #func process(): - #player.velocity.x = move_toward(player.velocity.x, 0, SPEED) - #player.move_and_slide() - # - #var direction = Input.get_axis("ui_left", "ui_right") - #if direction: - #player.current_state = StateWalk.new(player) - # - #if Input.is_action_just_pressed("ui_accept"): - #player.current_state = StateJump.new(player) - # - #if not player.is_on_floor(): - #player.current_state = StateFall.new(player) - # - #func exit(): - #pass -# -#class StateWalk: - #var player: Player - # - #func _init(_player): - #player = _player - #func _get_class(): - #return "StateWalk" - # - #func enter(): - #player.get_node("AnimatedSprite2D").play("walk") - # - #func process(): - #var direction = player.apply_directional_movement() - #if direction: - #player.move_and_slide() - #else: - #player.current_state = StateIdle.new(player) - # - #if Input.is_action_just_pressed("ui_accept"): - #player.current_state = StateJump.new(player) - # - #if not player.is_on_floor(): - #player.get_node("CoyoteTimer").start() - #player.current_state = StateFall.new(player) - # - #func exit(): - #pass -# -#class StateJump: - #var player: Player - # - #func _init(_player): - #player = _player - #func _get_class(): - #return "StateJump" - # - #func enter(): - #player.get_node("SoundJump").play() - #player.get_node("AnimatedSprite2D").play("jump") - #player.velocity.y = JUMP_VELOCITY - # - #func process(): - #player.apply_directional_movement(AIR_SPEED) - #player.move_and_slide() - # - #if player.velocity.y >= 0: - #player.current_state = StateFall.new(player) - # - #func exit(): - #pass -# -#class StateFall: - #var player: Player - #var is_falling = false - # - #func _init(_player): - #player = _player - #func _get_class(): - #return "StateFall" - # - #func enter(): - #player.get_node("AnimatedSprite2D").play("jump") - # - #func process(): - #if not player.get_node("CoyoteTimer").is_stopped() and Input.is_action_just_pressed("ui_accept"): - #player.current_state = StateJump.new(player) - # - #if player.velocity.y > abs(JUMP_VELOCITY) * 2: - #is_falling = true - #player.get_node("AnimatedSprite2D").play("fall") - # - #player.apply_directional_movement(AIR_SPEED) - #player.move_and_slide() - # - #if player.is_on_floor(): - #player.get_node("SoundFall").play() - #player.current_state = StateIdle.new(player) - # - #func exit(): - #pass +func state_hurt(data): + return { + "id": "hurt", + "enter": func(): + if data.collider.is_in_group("enemy"): + data.collider.queue_free() + + hp -= 1 + if hp == 0: + %SoundNoHp.play() + #current_state = state_game_over() + else: + %SoundHurt.play() + + if is_inflated: + $AnimatedSprite2D.play("inflated_jump") + else: + $AnimatedSprite2D.play("jump") + + var impulse = sign(global_position - data.collider.global_position) * HURT_VELOCITY + velocity.x = impulse.x + velocity.y = -100 + + move_and_slide() + velocity.x = 0 + + var tween = get_tree().create_tween() + tween.tween_property($AnimatedSprite2D, "self_modulate", Color(1, 1, 1, 0.2), 0.1) + tween.tween_property($AnimatedSprite2D, "self_modulate", Color(1, 1, 1, 0.8), 0.1) + tween.tween_property($AnimatedSprite2D, "self_modulate", Color(1, 1, 1, 0.2), 0.1) + tween.tween_property($AnimatedSprite2D, "self_modulate", Color(1, 1, 1, 0.8), 0.1) + tween.tween_callback(func(): + $AnimatedSprite2D.self_modulate = Color(1, 1, 1, 1) + if is_inflated: + current_state = state_inflated_idle() + else: + current_state = state_idle() + ) + , + "process": func(_delta): + move_and_slide() + , + "exit": func(): + pass + , + } + +func state_enter_door(): + return { + "id": "enter_door", + "enter": func(): + SoundManager.play("EnterDoor") + $AnimatedSprite2D.play("enter_door") + + # TODO + var canvas_layer = CanvasLayer.new() + var color_rect = ColorRect.new() + color_rect.size = get_viewport_rect().size + color_rect.color = Color(1,1,1,0) + canvas_layer.add_child(color_rect) + get_tree().current_scene.add_child(canvas_layer) + var tween = get_tree().create_tween() + tween.tween_property( + color_rect, + "color", + Color(1,1,1,1), + 0.3 + ) + tween.tween_callback(func(): + $AnimatedSprite2D.play("exit_door") + Game.transition_scene_with_door(entered_door) + ) + , + "process": func(_delta): + pass + , + "exit": func(): + pass + , + } |