update
This commit is contained in:
@@ -2,56 +2,35 @@ extends CharacterBody3D
|
||||
|
||||
enum State { IDLE, CHASE, ATTACK, RETURN }
|
||||
|
||||
@export var stats: BaseStats
|
||||
|
||||
var state: int = State.IDLE
|
||||
var target: Node3D = null
|
||||
var spawn_position: Vector3
|
||||
var portal: Node3D = null
|
||||
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
||||
@onready var health: Node = $Health
|
||||
|
||||
func _ready() -> void:
|
||||
spawn_position = global_position
|
||||
add_to_group("enemies")
|
||||
Stats.register(self, stats)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
|
||||
func _exit_tree() -> void:
|
||||
Stats.deregister(self)
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
if entity == self:
|
||||
queue_free()
|
||||
elif entity == target:
|
||||
target = null
|
||||
state = State.RETURN
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not is_on_floor():
|
||||
velocity.y -= gravity * delta
|
||||
move_and_slide()
|
||||
|
||||
func _engage(new_target: Node3D) -> void:
|
||||
if state == State.CHASE or state == State.ATTACK:
|
||||
return
|
||||
target = new_target
|
||||
state = State.CHASE
|
||||
var aggro: Node = get_node_or_null("EnemyAggro")
|
||||
if aggro:
|
||||
aggro.add_aggro(new_target, 1.0)
|
||||
_alert_nearby()
|
||||
|
||||
func _alert_nearby() -> void:
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
for enemy in enemies:
|
||||
if enemy != self and is_instance_valid(enemy) and "state" in enemy:
|
||||
if enemy.state == enemy.State.IDLE:
|
||||
var dist: float = global_position.distance_to(enemy.global_position)
|
||||
if dist <= 3.0:
|
||||
enemy._engage(target)
|
||||
|
||||
func _on_detection_area_body_entered(body: Node3D) -> void:
|
||||
if body is CharacterBody3D and body.name == "Player":
|
||||
_engage(body)
|
||||
EventBus.enemy_engaged.emit(self, body)
|
||||
EventBus.enemy_detected.emit(self, body)
|
||||
|
||||
func _on_detection_area_body_exited(body: Node3D) -> void:
|
||||
if body == target and state == State.CHASE:
|
||||
state = State.RETURN
|
||||
target = null
|
||||
func _on_detection_area_body_exited(_body: Node3D) -> void:
|
||||
pass
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
extends Node
|
||||
|
||||
const AGGRO_DECAY := 1.0
|
||||
const PORTAL_RADIUS := 10.0
|
||||
var aggro_table: Dictionary = {}
|
||||
var seconds_outside := 0.0
|
||||
|
||||
@onready var enemy: CharacterBody3D = get_parent()
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.damage_dealt.connect(_on_damage_dealt)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
EventBus.heal_requested.connect(_on_heal_requested)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var outside_portal := false
|
||||
if enemy.portal and is_instance_valid(enemy.portal):
|
||||
var dist_to_portal: float = enemy.global_position.distance_to(enemy.portal.global_position)
|
||||
if dist_to_portal > PORTAL_RADIUS:
|
||||
outside_portal = true
|
||||
seconds_outside += delta
|
||||
else:
|
||||
seconds_outside = 0.0
|
||||
|
||||
for player in aggro_table.keys():
|
||||
var decay: float = AGGRO_DECAY * delta
|
||||
if outside_portal:
|
||||
var bonus_decay: float = aggro_table[player] * 0.01 * pow(2, seconds_outside) * delta
|
||||
decay += bonus_decay
|
||||
aggro_table[player] -= decay
|
||||
# Im Portal-Radius: Aggro bleibt bei mindestens 1
|
||||
if not outside_portal and enemy.portal and is_instance_valid(player):
|
||||
var player_dist: float = player.global_position.distance_to(enemy.portal.global_position)
|
||||
if player_dist <= PORTAL_RADIUS and aggro_table[player] < 1.0:
|
||||
aggro_table[player] = 1.0
|
||||
if aggro_table[player] <= 0:
|
||||
aggro_table.erase(player)
|
||||
|
||||
var top_target: Node = _get_top_target()
|
||||
if top_target and top_target != enemy.target:
|
||||
enemy.target = top_target
|
||||
if enemy.state == enemy.State.IDLE or enemy.state == enemy.State.RETURN:
|
||||
enemy.state = enemy.State.CHASE
|
||||
elif not top_target and enemy.state != enemy.State.IDLE and enemy.state != enemy.State.RETURN:
|
||||
enemy.target = null
|
||||
enemy.state = enemy.State.RETURN
|
||||
|
||||
func _on_damage_dealt(attacker: Node, target: Node, amount: float) -> void:
|
||||
if target != enemy:
|
||||
return
|
||||
var multiplier := 1.0
|
||||
var role: Node = attacker.get_node_or_null("Role")
|
||||
if role and role.current_role == 0:
|
||||
multiplier = 2.0
|
||||
add_aggro(attacker, amount * multiplier)
|
||||
|
||||
func _on_heal_requested(healer: Node, _target: Node, amount: float) -> void:
|
||||
if not healer.is_in_group("player"):
|
||||
return
|
||||
if healer in aggro_table:
|
||||
add_aggro(healer, amount * 0.5)
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
aggro_table.erase(entity)
|
||||
|
||||
func add_aggro(player: Node, amount: float) -> void:
|
||||
if player in aggro_table:
|
||||
aggro_table[player] += amount
|
||||
else:
|
||||
aggro_table[player] = amount
|
||||
|
||||
func _get_top_target() -> Node:
|
||||
var top: Node = null
|
||||
var top_val := 0.0
|
||||
for player in aggro_table:
|
||||
if is_instance_valid(player) and aggro_table[player] > top_val:
|
||||
top_val = aggro_table[player]
|
||||
top = player
|
||||
return top
|
||||
|
||||
func has_aggro_on(player: Node) -> bool:
|
||||
return _get_top_target() == player
|
||||
@@ -1 +0,0 @@
|
||||
uid://bojdohjxr6uef
|
||||
@@ -1,26 +0,0 @@
|
||||
extends Node
|
||||
|
||||
const ATTACK_RANGE := 2.0
|
||||
const ATTACK_COOLDOWN := 1.5
|
||||
const ATTACK_DAMAGE := 5.0
|
||||
|
||||
var attack_timer := 0.0
|
||||
|
||||
@onready var enemy: CharacterBody3D = get_parent()
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
attack_timer -= delta
|
||||
if enemy.state != enemy.State.ATTACK:
|
||||
return
|
||||
if not is_instance_valid(enemy.target):
|
||||
enemy.state = enemy.State.RETURN
|
||||
return
|
||||
var dist := enemy.global_position.distance_to(enemy.target.global_position)
|
||||
if dist > ATTACK_RANGE:
|
||||
enemy.state = enemy.State.CHASE
|
||||
return
|
||||
if attack_timer <= 0:
|
||||
attack_timer = ATTACK_COOLDOWN
|
||||
EventBus.damage_requested.emit(enemy, enemy.target, ATTACK_DAMAGE)
|
||||
enemy.velocity.x = 0
|
||||
enemy.velocity.z = 0
|
||||
@@ -1 +0,0 @@
|
||||
uid://ct4u62xalrjyo
|
||||
@@ -49,10 +49,14 @@ func _return_to_spawn(delta: float) -> void:
|
||||
_regenerate(delta)
|
||||
|
||||
func _regenerate(delta: float) -> void:
|
||||
var health: Node = enemy.get_node("Health")
|
||||
if health.current_health < health.max_health:
|
||||
var rate: float = REGEN_FAST if health.current_health < health.max_health else REGEN_SLOW
|
||||
if health.current_health >= health.max_health * 0.99:
|
||||
var health: float = Stats.get_stat(enemy, "health")
|
||||
var max_health: float = Stats.get_stat(enemy, "max_health")
|
||||
if health == null or max_health == null:
|
||||
return
|
||||
if health < max_health:
|
||||
var rate: float = REGEN_FAST
|
||||
if health >= max_health * 0.99:
|
||||
rate = REGEN_SLOW
|
||||
health.current_health = min(health.current_health + health.max_health * rate * delta, health.max_health)
|
||||
EventBus.health_changed.emit(enemy, health.current_health, health.max_health)
|
||||
health = min(health + max_health * rate * delta, max_health)
|
||||
Stats.set_stat(enemy, "health", health)
|
||||
EventBus.health_changed.emit(enemy, health, max_health)
|
||||
|
||||
Reference in New Issue
Block a user