extends Node2D signal died() signal health_changed(amount) const DeathParticles = preload("res://objects/enemy/death_particles_famira.tscn") export var push_speed: float = 30.0 export var chase_speed: float = 65.0 export var breath_dps: float = 14.25 export var breath_knockback: float = 16.0 export var punch_damage: float = 5.25 export var punch_knockback: float = 40.0 export var hurting: bool = false export var hp: float = 100.0 export var sg2083_beam_dps: float = 10.0 export var sg2083_path: NodePath export var defense: float = 0.45 export var credits: bool = false onready var animation_player: AnimationPlayer = $"%AnimationPlayer" onready var state_chart: StateChart = $StateChart onready var in_range_cast: RayCast2D = $InRangeCast onready var out_range_cast: RayCast2D = $OutRangeCast onready var shoot_range_cast: RayCast2D = $ShootRangeCast onready var grumble = $Grumble func _ready(): if credits: animation_player.play("roar") func _physics_process(delta: float) -> void: if !credits: var sg2083 := get_node(sg2083_path) if sg2083 and sg2083.state == 0: state_chart.set_guard_property("player_dead", true) state_chart.send_event("player_dead") else: state_chart.set_guard_property("player_dead", false) if not out_range_cast.is_colliding(): state_chart.send_event("out_of_range") elif in_range_cast.is_colliding(): state_chart.send_event("in_range") if fmod(Game.time, 15.0) == 0.0: Audio.play_sound(Audio.a_grumble,Audio.ac_boss) func start_push() -> void: state_chart.send_event("start_push") func stop_push() -> void: state_chart.send_event("stop_push") func hurt(amount: float) -> void: if hp <= 0.0: return hp -= amount * defense hp = max(hp, 0.0) emit_signal("health_changed", hp) if hp <= 0.0: state_chart.send_event("die") emit_signal("died") func _attack() -> void: var sg2083 := get_node(sg2083_path) if sg2083.has_method("hurt"): var r = RandomNumberGenerator.new() r.randomize() var snd = r.randi_range(0,2) match snd: 0: Audio.play_sound(Audio.a_mech_hurt_1,Audio.ac_die) 1: Audio.play_sound(Audio.a_mech_hurt_2,Audio.ac_die) 2: Audio.play_sound(Audio.a_mech_hurt_3,Audio.ac_die) sg2083.hurt(punch_damage) sg2083.knock_back(punch_knockback) func _on_Roar_state_entered() -> void: animation_player.play("roar", 0.25) func _roar() -> void: if credits == false: Audio.play_sound(Audio.a_famira, Audio.ac_boss) func _on_Chasing_state_entered() -> void: animation_player.play("chasing", 0.25) func _on_Punching_state_entered() -> void: animation_player.play("punching", 0.25) func _on_PunchCooldown_state_entered() -> void: animation_player.play("RESET", 0.5) func _on_Shooting_state_entered() -> void: animation_player.play("shooting") func _on_PushedBack_state_entered() -> void: animation_player.play("pushed", 0.25) func _on_Dying_state_entered() -> void: animation_player.play("die", 0.25) Audio.play_sound(Audio.a_famira_die, Audio.ac_boss) var tween = $Tween tween.interpolate_property(Audio.ac_music, "volume_db", Audio.ac_music.volume_db, -99, 16, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() func _on_Chasing_state_physics_processing(delta) -> void: position.x -= chase_speed * delta func _on_PushedBack_state_physics_processing(delta) -> void: hurt(sg2083_beam_dps * delta) position.x += push_speed * delta func _on_Shooting_state_physics_processing(delta) -> void: if hurting and shoot_range_cast.is_colliding(): var sg2083 := get_node(sg2083_path) if sg2083.has_method("hurt"): var distance := shoot_range_cast.to_local(shoot_range_cast.get_collision_point()).x var weight := inverse_lerp(shoot_range_cast.cast_to.x, 0.0, distance) if !Audio.ac_climb.is_playing() && sg2083.state != sg2083.State.DUCK: Audio.play_sound(Audio.a_critical_heat,Audio.ac_climb) sg2083.hurt(breath_dps * weight * delta, true) sg2083.knock_back(breath_knockback * weight * delta, true) func _on_Hitbox_area_entered(area: Area2D) -> void: if area.is_in_group("hurt_famira"): hurt(area.damage) area.queue_free()