demon.gd 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. extends Area2D
  2. class_name Demon
  3. @onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
  4. @onready var vision: ShapeCast2D = $Vision
  5. @onready var vision_polygon: Polygon2D = $Vision/Polygon2D
  6. @onready var gun_shot_sound: AudioStreamPlayer = $GunShotSound
  7. @export var shot_probability: float = 0.5
  8. var sp_modifier: float = 0.0
  9. var ammo: int = 0
  10. enum State {
  11. STATE_IDLE,
  12. STATE_SHOOTING,
  13. STATE_RELOADING,
  14. }
  15. var state: State = State.STATE_IDLE
  16. var is_being_dragged: bool = false
  17. func _ready() -> void:
  18. ammo = Game.max_ammo
  19. func _process(_delta: float) -> void:
  20. vision_polygon.visible = GlobalInput.visible_vision
  21. func _physics_process(_delta: float) -> void:
  22. if state != State.STATE_IDLE:
  23. return
  24. vision.force_shapecast_update()
  25. if not vision.is_colliding():
  26. return
  27. var foe = vision.get_collider(0)
  28. if foe == null || not (foe is Area2D) || not foe.visible:
  29. return
  30. var value = randf()
  31. var actual_probability = shot_probability + sp_modifier
  32. if value <= actual_probability:
  33. sp_modifier = 0.0
  34. await shoot_at(foe as Area2D)
  35. else:
  36. sp_modifier += 0.1
  37. print("%s: not shooting (%.2f > %.2f)" % [name, value, actual_probability])
  38. func _on_mouse_entered():
  39. if not GlobalInput.is_dragging():
  40. # TODO: add custom cursor image
  41. Input.set_custom_mouse_cursor(null, Input.CURSOR_MOVE)
  42. func _on_mouse_exited():
  43. if not GlobalInput.is_dragging():
  44. # TODO: add custom cursor image
  45. Input.set_custom_mouse_cursor(null, Input.CURSOR_ARROW)
  46. func _on_input_event(_viewport: Node, event: InputEvent, _shape_idx: int):
  47. if event is InputEventMouseButton:
  48. handle_click(event)
  49. func handle_click(event: InputEventMouseButton):
  50. if event.button_index != MOUSE_BUTTON_LEFT:
  51. return
  52. if event.pressed && not GlobalInput.is_dragging():
  53. is_being_dragged = true
  54. GlobalInput.start_dragging(self)
  55. elif not event.pressed && is_being_dragged:
  56. is_being_dragged = false
  57. GlobalInput.stop_dragging()
  58. func reload() -> void:
  59. state = State.STATE_RELOADING
  60. print("%s: reloading (%.2f seconds)" % [name, Game.reload_time])
  61. await get_tree().create_timer(Game.reload_time).timeout
  62. print(name + ": done reloading")
  63. ammo = Game.max_ammo
  64. state = State.STATE_IDLE
  65. func shoot_at(foe: Area2D) -> void:
  66. if state != State.STATE_IDLE:
  67. return
  68. print("%s: Ammo = %d/%d" % [name, ammo, Game.max_ammo])
  69. if ammo <= 0:
  70. await reload()
  71. else:
  72. state = State.STATE_SHOOTING
  73. ammo -= 1
  74. gun_shot_sound.play()
  75. sprite.play("shoot")
  76. var distance: float = clampf((foe.global_position - global_position).length(), 140.0, 250.0)
  77. var distance_percent: float = 1.0 - ((distance - 140.0) / 110.0)
  78. #print(distance_percent)
  79. var speed: float = minf(foe.speed, 100.0)
  80. var speed_percent: float = 1.0 - ((speed - 64.0) / 36.0)
  81. #print(speed_percent)
  82. var base = Game.base_accuracy
  83. var rest = (1.0 - base) / 2.0
  84. var hit_chance: float = base + distance_percent * rest + speed_percent * rest
  85. print("%s: Base %.2f Speed %.2f Distance %.2f"% [name, base, speed_percent * rest, distance_percent * rest])
  86. print("%s: Shooting at %s with %.2f hit chance" % [name, foe.name, hit_chance])
  87. var hit: float = randf()
  88. if hit > hit_chance:
  89. print("%s: missed shot (%.2f > %.2f)" % [name, hit, hit_chance])
  90. else:
  91. if foe.has_method("take_damage"):
  92. await foe.take_damage()
  93. else:
  94. foe.queue_free()
  95. await sprite.animation_finished
  96. if ammo <= 0:
  97. await reload()
  98. else:
  99. state = State.STATE_IDLE
  100. func _on_animation_finished():
  101. sprite.animation = "idle"
  102. sprite.frame = 0