update
This commit is contained in:
@@ -2,19 +2,24 @@ extends Sprite3D
|
||||
|
||||
@onready var viewport: SubViewport = $SubViewport
|
||||
@onready var health_bar: ProgressBar = $SubViewport/HealthBar
|
||||
@onready var shield_bar: ProgressBar = $SubViewport/ShieldBar
|
||||
@onready var border: ColorRect = $SubViewport/Border
|
||||
@onready var health: Node = get_parent().get_node("Health")
|
||||
@onready var shield: Node = get_parent().get_node("Shield")
|
||||
@onready var enemy: CharacterBody3D = get_parent()
|
||||
@onready var parent_node: Node = get_parent()
|
||||
|
||||
var shield: Node = null
|
||||
var shield_bar: ProgressBar = null
|
||||
var style_normal: StyleBoxFlat
|
||||
var style_aggro: StyleBoxFlat
|
||||
|
||||
func _ready() -> void:
|
||||
texture = viewport.get_texture()
|
||||
health_bar.max_value = health.max_health
|
||||
shield_bar.max_value = shield.max_shield
|
||||
shield = get_parent().get_node_or_null("Shield")
|
||||
shield_bar = $SubViewport.get_node_or_null("ShieldBar")
|
||||
if shield and shield_bar:
|
||||
shield_bar.max_value = shield.max_shield
|
||||
elif shield_bar:
|
||||
shield_bar.visible = false
|
||||
border.visible = false
|
||||
style_normal = health_bar.get_theme_stylebox("fill").duplicate()
|
||||
style_aggro = style_normal.duplicate()
|
||||
@@ -23,9 +28,10 @@ func _ready() -> void:
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
health_bar.value = health.current_health
|
||||
shield_bar.value = shield.current_shield
|
||||
if shield and shield_bar:
|
||||
shield_bar.value = shield.current_shield
|
||||
var player: Node = get_tree().get_first_node_in_group("player")
|
||||
if player and enemy.target == player:
|
||||
if player and "target" in parent_node and parent_node.target == player:
|
||||
health_bar.add_theme_stylebox_override("fill", style_aggro)
|
||||
else:
|
||||
health_bar.add_theme_stylebox_override("fill", style_normal)
|
||||
1
scripts/components/healthbar.gd.uid
Normal file
1
scripts/components/healthbar.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1w7vm7t3k3ts
|
||||
@@ -5,6 +5,7 @@ enum State { IDLE, CHASE, ATTACK, RETURN }
|
||||
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
|
||||
@@ -12,6 +13,7 @@ var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
func _ready() -> void:
|
||||
spawn_position = global_position
|
||||
add_to_group("enemies")
|
||||
add_to_group("targetable")
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
@@ -39,7 +41,7 @@ func _engage(new_target: Node3D) -> void:
|
||||
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):
|
||||
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:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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()
|
||||
|
||||
@@ -10,15 +12,32 @@ func _ready() -> void:
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
|
||||
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():
|
||||
aggro_table[player] -= AGGRO_DECAY * delta
|
||||
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
|
||||
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:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
uid://dwqx03nfypa7u
|
||||
@@ -1,13 +1,14 @@
|
||||
extends Node
|
||||
|
||||
const SPEED := 3.0
|
||||
const LEASH_RANGE := 15.0
|
||||
const ATTACK_RANGE := 2.0
|
||||
const REGEN_FAST := 0.10
|
||||
const REGEN_SLOW := 0.01
|
||||
|
||||
@onready var enemy: CharacterBody3D = get_parent()
|
||||
@onready var nav_agent: NavigationAgent3D = get_parent().get_node("NavigationAgent3D")
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
func _physics_process(delta: float) -> void:
|
||||
match enemy.state:
|
||||
enemy.State.IDLE:
|
||||
enemy.velocity.x = 0
|
||||
@@ -15,17 +16,12 @@ func _physics_process(_delta: float) -> void:
|
||||
enemy.State.CHASE:
|
||||
_chase()
|
||||
enemy.State.RETURN:
|
||||
_return_to_spawn()
|
||||
_return_to_spawn(delta)
|
||||
|
||||
func _chase() -> void:
|
||||
if not is_instance_valid(enemy.target):
|
||||
enemy.state = enemy.State.RETURN
|
||||
return
|
||||
var dist_to_spawn := enemy.global_position.distance_to(enemy.spawn_position)
|
||||
if dist_to_spawn > LEASH_RANGE:
|
||||
enemy.state = enemy.State.RETURN
|
||||
enemy.target = null
|
||||
return
|
||||
var dist_to_target := enemy.global_position.distance_to(enemy.target.global_position)
|
||||
if dist_to_target <= ATTACK_RANGE:
|
||||
enemy.state = enemy.State.ATTACK
|
||||
@@ -37,7 +33,7 @@ func _chase() -> void:
|
||||
enemy.velocity.x = direction.x * SPEED
|
||||
enemy.velocity.z = direction.z * SPEED
|
||||
|
||||
func _return_to_spawn() -> void:
|
||||
func _return_to_spawn(delta: float) -> void:
|
||||
var dist := enemy.global_position.distance_to(enemy.spawn_position)
|
||||
if dist < 1.0:
|
||||
enemy.state = enemy.State.IDLE
|
||||
@@ -50,3 +46,13 @@ func _return_to_spawn() -> void:
|
||||
direction.y = 0
|
||||
enemy.velocity.x = direction.x * SPEED
|
||||
enemy.velocity.z = direction.z * SPEED
|
||||
_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:
|
||||
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)
|
||||
|
||||
@@ -14,3 +14,4 @@ signal shield_changed(entity, current, max_val)
|
||||
signal respawn_tick(timer)
|
||||
signal enemy_engaged(enemy, target)
|
||||
signal cooldown_tick(cooldowns, max_cooldowns, gcd_timer)
|
||||
signal portal_spawn(portal, enemies)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends Node
|
||||
|
||||
const GCD_TIME := 1.0
|
||||
const GCD_TIME := 0.5
|
||||
|
||||
@onready var player: CharacterBody3D = get_parent()
|
||||
@onready var targeting: Node = get_parent().get_node("Targeting")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends CanvasLayer
|
||||
|
||||
const GCD_TIME := 1.0
|
||||
const GCD_TIME := 0.5
|
||||
|
||||
@onready var health_bar: ProgressBar = $HealthBar
|
||||
@onready var shield_bar: ProgressBar = $ShieldBar
|
||||
|
||||
@@ -49,16 +49,16 @@ func _try_target_under_mouse(mouse_pos: Vector2) -> void:
|
||||
set_target(null)
|
||||
|
||||
func _cycle_target() -> void:
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
if enemies.is_empty():
|
||||
var targets := get_tree().get_nodes_in_group("targetable")
|
||||
if targets.is_empty():
|
||||
set_target(null)
|
||||
return
|
||||
if current_target == null or current_target not in enemies:
|
||||
set_target(enemies[0])
|
||||
if current_target == null or current_target not in targets:
|
||||
set_target(targets[0])
|
||||
return
|
||||
var idx := enemies.find(current_target)
|
||||
var next_idx := (idx + 1) % enemies.size()
|
||||
set_target(enemies[next_idx])
|
||||
var idx := targets.find(current_target)
|
||||
var next_idx := (idx + 1) % targets.size()
|
||||
set_target(targets[next_idx])
|
||||
|
||||
func set_target(target: Node3D) -> void:
|
||||
current_target = target
|
||||
|
||||
4
scripts/portal/portal.gd
Normal file
4
scripts/portal/portal.gd
Normal file
@@ -0,0 +1,4 @@
|
||||
extends StaticBody3D
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("targetable")
|
||||
1
scripts/portal/portal.gd.uid
Normal file
1
scripts/portal/portal.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://byjxj4mq84gki
|
||||
30
scripts/portal/portal_spawner.gd
Normal file
30
scripts/portal/portal_spawner.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
extends Node
|
||||
|
||||
const SPAWN_COUNT := 3
|
||||
var thresholds := [0.85, 0.70, 0.55, 0.40, 0.25, 0.10]
|
||||
var triggered: Array[bool] = [false, false, false, false, false, false]
|
||||
var enemy_scene: PackedScene = preload("res://scenes/enemy/enemy.tscn")
|
||||
|
||||
@onready var portal: StaticBody3D = get_parent()
|
||||
@onready var health: Node = get_parent().get_node("Health")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if health.current_health <= 0:
|
||||
return
|
||||
var ratio: float = health.current_health / health.max_health
|
||||
for i in range(thresholds.size()):
|
||||
if not triggered[i] and ratio <= thresholds[i]:
|
||||
triggered[i] = true
|
||||
_spawn_enemies()
|
||||
|
||||
func _spawn_enemies() -> void:
|
||||
var spawned: Array = []
|
||||
for j in range(SPAWN_COUNT):
|
||||
var enemy: CharacterBody3D = enemy_scene.instantiate()
|
||||
var offset := Vector3(randf_range(-2, 2), 0, randf_range(-2, 2))
|
||||
portal.get_parent().add_child(enemy)
|
||||
enemy.global_position = portal.global_position + offset
|
||||
enemy.spawn_position = portal.global_position
|
||||
enemy.portal = portal
|
||||
spawned.append(enemy)
|
||||
EventBus.portal_spawn.emit(portal, spawned)
|
||||
1
scripts/portal/portal_spawner.gd.uid
Normal file
1
scripts/portal/portal_spawner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://begrg74oh76pu
|
||||
Reference in New Issue
Block a user