ufo.gd 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. extends CharacterBody2D
  2. class_name UFO
  3. @export var movement_speed: float = 800.0
  4. @export var radius: float = 150.0
  5. var max_velocity: float = movement_speed
  6. @onready var navigation_agent: NavigationAgent2D = $NavigationAgent2D
  7. @onready var plants: Node = $"../../Plants"
  8. var target: Plant = null
  9. var target2: Plant = null
  10. var announce_self: bool = false
  11. var dead = false
  12. @export_range(1.0, 1000.0) var dps: float = 10.0
  13. @export var max_hp = 100.0
  14. var hp: float
  15. var dead_timer: Timer = Timer.new()
  16. @export var wheat_out: int = 3
  17. var Wheat = preload("res://scene/entity/wheat.tscn")
  18. @export var should_rotate: bool = true
  19. @onready var ray = $Plx/Ray
  20. @onready var ray2 = find_child("Ray2")
  21. var attack_range = 1000.0
  22. var second_player = find_child("AudioStreamPlayer2D2")
  23. @onready var power_level: float = $"../../Waves".power_level
  24. @onready var ROOT = get_tree().get_root().get_child(0)
  25. func fuck():
  26. return false
  27. func _ready():
  28. # Workaround bug body->get_space()
  29. call_deferred("actor_setup")
  30. navigation_agent.connect("velocity_computed", move)
  31. max_hp *= power_level
  32. dps *= power_level * pow(power_level, 0.7)
  33. $RerollTimer.connect("timeout", roll_movement_target_2)
  34. $DamageTimer.connect("timeout", damage_target)
  35. navigation_agent.set_path_desired_distance(40.0)
  36. navigation_agent.set_target_desired_distance(attack_range)
  37. navigation_agent.set_path_max_distance(200.0)
  38. navigation_agent.set_avoidance_enabled(true)
  39. if ROOT.disable_ai:
  40. navigation_agent.set_radius(0)
  41. else:
  42. navigation_agent.set_radius(radius)
  43. navigation_agent.set_neighbor_distance(movement_speed * 2)
  44. navigation_agent.set_max_speed(movement_speed)
  45. $AudioStreamPlayer2D.set_bus("Music" + str(randi_range(0, 3)))
  46. $AudioStreamPlayer2D.play($"../../HomeSound".get_playback_position())
  47. if second_player != null:
  48. second_player.set_bus("Music" + str(randi_range(0, 3)))
  49. second_player.play($"../../HomeSound".get_playback_position())
  50. hp = max_hp
  51. $Plx/HP.set_max(max_hp)
  52. $Plx/HP.set_value(max_hp)
  53. func _process(delta):
  54. if dead:
  55. _process_dead(delta)
  56. return
  57. if target == null or target.dead or !is_instance_valid(target):
  58. if target2 != null:
  59. target = target2
  60. target2 = null
  61. else:
  62. target = roll_movement_target()
  63. hide_ray(ray)
  64. if navigation_agent.is_target_reached() and target != null:
  65. enable_ray(ray, target)
  66. if ray2 != null:
  67. if target2 == null or target2.dead:
  68. target2 = roll_aux_target()
  69. if target2 != null:
  70. enable_ray(ray2, target2)
  71. else:
  72. hide_ray(ray2)
  73. if $DamageTimer.is_stopped():
  74. $DamageTimer.start()
  75. else:
  76. hide_ray(ray)
  77. hide_ray(ray2)
  78. $DamageTimer.stop()
  79. func _process_dead(delta: float):
  80. var time_left = dead_timer.get_time_left() * 3.0
  81. var scale = ease(time_left, 5)
  82. set_scale(Vector2(scale, scale))
  83. $AudioStreamPlayer2D.set_volume_db($AudioStreamPlayer2D.get_volume_db() - delta * 30.0)
  84. if second_player != null:
  85. second_player.set_volume_db(second_player.get_volume_db() - delta * 30.0)
  86. if time_left == 0.0:
  87. get_parent().remove_child(self)
  88. queue_free()
  89. func _physics_process(delta):
  90. if fuck():
  91. return
  92. if navigation_agent.is_target_reached():
  93. var new_speed = velocity.length() - delta * movement_speed * 2.0
  94. velocity = velocity.normalized() * new_speed
  95. move_and_slide()
  96. else:
  97. var current_agent_position: Vector2 = global_transform.origin
  98. var next_path_position: Vector2 = navigation_agent.get_next_location()
  99. var new_velocity: Vector2 = next_path_position - current_agent_position
  100. new_velocity = new_velocity.normalized()
  101. new_velocity = new_velocity * movement_speed
  102. navigation_agent.set_velocity(new_velocity)
  103. func actor_setup():
  104. await get_tree().physics_frame
  105. target = roll_movement_target()
  106. func roll_movement_target_2():
  107. target = roll_movement_target()
  108. func roll_movement_target():
  109. var all_plants = plants.get_children()
  110. var count = all_plants.size()
  111. announce_self = true
  112. var res: UFO = null
  113. if count == 0:
  114. $RerollTimer.start()
  115. else:
  116. var i = 0
  117. while (res == null or res.dead or !is_instance_valid(res)) and i < 10:
  118. res = all_plants[randi_range(0, count - 1)]
  119. i += 1
  120. if i == 10:
  121. res = null
  122. $RerollTimer.start()
  123. else:
  124. navigation_agent.set_target_location(res.get_global_position())
  125. return res
  126. func roll_aux_target():
  127. var all_plants = plants.get_children()
  128. for plant in all_plants:
  129. if plant != target and plant.get_global_position().distance_to(get_global_position()) <= attack_range:
  130. return plant
  131. return null
  132. func damage_target():
  133. if target != null:
  134. target.deal_damage(dps / 20.0, self if announce_self else null)
  135. if target2 != null:
  136. target2.deal_damage(dps / 20.0, self if announce_self else null)
  137. func move(velocity_: Vector2):
  138. velocity = velocity_
  139. move_and_slide()
  140. if should_rotate:
  141. var top = find_child("Top")
  142. top.set_rotation(lerp_angle(top.get_rotation(), velocity.angle(), 10.0 * get_physics_process_delta_time()))
  143. func _on_area_2d_body_entered(body):
  144. if body.get_class() == "Bullet":
  145. deal_damage(body.damage)
  146. body.get_parent().remove_child(body)
  147. body.queue_free()
  148. func deal_damage(damage):
  149. hp -= damage
  150. $Plx/HP.set_value(hp)
  151. if hp <= 0 and not dead:
  152. dead = true
  153. $Coin.play()
  154. set_collision_layer_value(1, false)
  155. set_collision_mask_value(1, false)
  156. $Plx/HP.hide()
  157. dead_timer.set_wait_time(0.333)
  158. dead_timer.set_one_shot(true)
  159. add_child(dead_timer)
  160. dead_timer.start()
  161. $DamageTimer.stop()
  162. hide_ray(ray)
  163. hide_ray(ray2)
  164. call_deferred("spawn_wheat")
  165. func spawn_wheat():
  166. if ROOT.disable_wheat:
  167. $"../../Economy".add(ceil(wheat_out * sqrt(power_level)))
  168. else:
  169. for i in range(0, ceil(wheat_out * sqrt(power_level))):
  170. var wheat = Wheat.instantiate()
  171. wheat.rotate(randf_range(-PI, PI))
  172. wheat.set_position(get_global_position())
  173. $"../../Wheat".add_child(wheat)
  174. func enable_ray(ray, target: Node2D):
  175. if ray != null:
  176. var diff = target.get_global_position() - get_global_position()
  177. var poly = ray.get_polygon()
  178. poly[1] = diff + Vector2(-80.0, target.body.texture.get_size().y / 2 + 30.0)
  179. poly[2] = diff + Vector2(80.0, target.body.texture.get_size().y / 2 + 30.0)
  180. ray.set_polygon(poly)
  181. ray.show()
  182. func hide_ray(r):
  183. if r != null:
  184. r.hide()