Files
SurvivalOfTheSnippest/level/scripts/base_enemy.gd
T
Scottlg 1562b37563 Gladiator arena: gold economy, outfitting shop, permadeath runs
Turns the sandbox into a survival roguelike loop: outfit your gladiator
with banked gold, enter the arena, earn gold from kills and wave clears,
die, keep 10% of your total value (floored at the 500 fresh-start).

Economy (GameState autoload):
- banked_gold, owned items, and loadout persist to user://save.cfg
- Kill gold (basic 10g, armed 25g) credited server-side to the killer
- Wave-clear bonus (25 + 5/wave) for all players; wave synced to peers
- Death settlement liquidates gear, guarded against double-fire

Character creation / shop:
- Menu reframed: armory shop + live Character Sheet loadout preview
- Character Sheet gains preview mode (renders from loadout, no player)
- Buy/Equip with hand rules; two-handers and off-hands displace each
  other symmetrically (shop and in-game pickups)
- Chosen loadout auto-equips on spawn via existing RPC path

Combat feel:
- Attacks snap to camera facing and lock direction for the swing
- Dash commits to its direction for the full duration
- Weapon slots show name text when no icon is set
- Hitbox/hurtbox debug meshes hidden by default (H toggles)

Death is final: players no longer respawn; a results screen shows waves,
time, kills, and the settlement, then returns to camp. Armed enemies no
longer respawn (were an infinite gold farm and respawned invisible).
2026-07-01 23:38:59 +01:00

111 lines
2.9 KiB
GDScript

extends BaseUnit
class_name BaseEnemy
## Base class for all enemies in the game
## Provides common enemy functionality like AI, pathfinding, and targeting
signal target_changed(new_target: Node)
## Current target (usually a player)
var current_target: Node = null
## Enemy detection/aggro range
@export var detection_range: float = 10.0
## Whether this enemy is aggressive (will attack players)
@export var is_aggressive: bool = true
## Gold credited to the killing player (arena economy)
@export var gold_reward: int = 10
func _ready():
super._ready()
# Enemies should respawn by default
can_respawn = true
# Connect to health signals for AI reactions
health_changed.connect(_on_enemy_health_changed)
died.connect(_on_enemy_died)
respawned.connect(_on_enemy_respawned)
func _physics_process(delta):
# Only server handles enemy AI (check peer is assigned first)
if multiplayer.multiplayer_peer == null or not multiplayer.is_server():
return
if is_dead:
return
# Update target if needed
_update_target()
## Find and update current target
func _update_target():
# Subclasses can override this to implement custom targeting logic
pass
## Get all players in range
func get_players_in_range(range_dist: float) -> Array[Node]:
var players_in_range: Array[Node] = []
# Find the players container
var level = get_tree().get_current_scene()
if not level or not level.has_node("PlayersContainer"):
return players_in_range
var players_container = level.get_node("PlayersContainer")
for player in players_container.get_children():
if player is Character and not player.is_dead:
var distance = global_position.distance_to(player.global_position)
if distance <= range_dist:
players_in_range.append(player)
return players_in_range
## Get nearest player
func get_nearest_player() -> Node:
var players = get_players_in_range(detection_range)
if players.is_empty():
return null
var nearest_player = null
var nearest_distance = INF
for player in players:
var distance = global_position.distance_to(player.global_position)
if distance < nearest_distance:
nearest_distance = distance
nearest_player = player
return nearest_player
## Health changed callback
func _on_enemy_health_changed(old_health: float, new_health: float):
# Subclasses can override to react to damage
pass
## Death callback
func _on_enemy_died(killer_id: int):
print("[Enemy ", name, "] died. Killer ID: ", killer_id)
# Subclasses can override for death effects
pass
## Respawn callback
func _on_enemy_respawned():
print("[Enemy ", name, "] respawned at ", global_position)
# Clear target on respawn
current_target = null
target_changed.emit(null)
# Subclasses can override for respawn effects
pass
## Override hurt animation
@rpc("any_peer", "call_local", "reliable")
func _play_hurt_animation():
# Flash red or play hurt animation
# Subclasses should implement this with their specific animations
pass