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).
95 lines
3.2 KiB
GDScript
95 lines
3.2 KiB
GDScript
extends Area3D
|
|
class_name HurtBox
|
|
|
|
## A component that receives damage from HitBoxes
|
|
## Attach to any entity that can be damaged (players, enemies, destructibles)
|
|
## NOTE: This is a passive detection zone - HitBox handles the collision detection
|
|
|
|
## Global debug visibility toggle (press H in-game to toggle)
|
|
static var debug_visible: bool = false
|
|
|
|
## The entity that owns this hurtbox (should be a BaseUnit or similar)
|
|
@export var owner_entity: Node = null
|
|
## Debug mesh for visualization
|
|
var _debug_mesh: MeshInstance3D = null
|
|
var _debug_material: StandardMaterial3D = null
|
|
var _hit_flash_timer: float = 0.0
|
|
const HIT_FLASH_DURATION: float = 0.3 # seconds
|
|
|
|
func _ready():
|
|
# Auto-find owner if not set (traverse up to find BaseUnit)
|
|
if owner_entity == null:
|
|
var parent = get_parent()
|
|
while parent:
|
|
if parent is BaseUnit:
|
|
owner_entity = parent
|
|
break
|
|
parent = parent.get_parent()
|
|
|
|
# Configure collision - hurtboxes are on layer 5, detect nothing (passive)
|
|
collision_layer = 16 # Layer 5 (hurtbox)
|
|
collision_mask = 0 # Don't detect anything - hitboxes detect us
|
|
|
|
# Ensure we can be detected but don't detect others
|
|
monitorable = true
|
|
monitoring = false
|
|
|
|
# Add debug visualization
|
|
_create_debug_visualization()
|
|
|
|
func _process(delta):
|
|
# Update debug visibility
|
|
if _debug_mesh:
|
|
_debug_mesh.visible = HurtBox.debug_visible
|
|
|
|
# Handle hit flash timer
|
|
if _hit_flash_timer > 0.0:
|
|
_hit_flash_timer -= delta
|
|
if _hit_flash_timer <= 0.0:
|
|
# Flash finished, return to green
|
|
if _debug_material:
|
|
_debug_material.albedo_color = Color(0.0, 1.0, 0.0, 0.3) # Green
|
|
|
|
func _create_debug_visualization():
|
|
# Find the collision shape to visualize
|
|
for child in get_children():
|
|
if child is CollisionShape3D and child.shape:
|
|
# Create a semi-transparent green mesh to visualize the hurtbox
|
|
_debug_mesh = MeshInstance3D.new()
|
|
_debug_material = StandardMaterial3D.new()
|
|
_debug_material.albedo_color = Color(0.0, 1.0, 0.0, 0.3) # Green, semi-transparent
|
|
_debug_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
_debug_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
_debug_material.cull_mode = BaseMaterial3D.CULL_DISABLED # Visible from both sides
|
|
|
|
# Create mesh matching the collision shape
|
|
var mesh: Mesh = null
|
|
if child.shape is BoxShape3D:
|
|
var box_mesh = BoxMesh.new()
|
|
box_mesh.size = child.shape.size
|
|
mesh = box_mesh
|
|
elif child.shape is SphereShape3D:
|
|
var sphere_mesh = SphereMesh.new()
|
|
sphere_mesh.radius = child.shape.radius
|
|
sphere_mesh.height = child.shape.radius * 2
|
|
mesh = sphere_mesh
|
|
elif child.shape is CapsuleShape3D:
|
|
var capsule_mesh = CapsuleMesh.new()
|
|
capsule_mesh.radius = child.shape.radius
|
|
capsule_mesh.height = child.shape.height
|
|
mesh = capsule_mesh
|
|
|
|
if mesh:
|
|
_debug_mesh.mesh = mesh
|
|
_debug_mesh.material_override = _debug_material
|
|
_debug_mesh.visible = HurtBox.debug_visible
|
|
# Don't set transform - it inherits from parent CollisionShape3D
|
|
child.add_child(_debug_mesh)
|
|
break
|
|
|
|
## Call this when the hurtbox is hit to flash red
|
|
func flash_hit():
|
|
_hit_flash_timer = HIT_FLASH_DURATION
|
|
if _debug_material:
|
|
_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.5) # Red, semi-transparent
|