This commit is contained in:
team3
2026-09-16 13:21:45 +02:00
parent 8a188c5d5c
commit 49e8f2d95e
27 changed files with 1772 additions and 265 deletions

View File

@@ -20,6 +20,10 @@ var camera: Camera3D = null
var scout_player: PlayerState = null
var selected_slot := {} ## {"kind": "board"/"bench", ...}
var hover_slot := {}
## Reichweite der gewählten Einheit: Mitte (Arena-Feld), Radius, Farbe. Radius 0 = aus.
var range_center := Vector2i(-1, -1)
var range_radius := 0
var range_color := Color(1.0, 0.7, 0.3)
var drag_active := false
var drag_avatar_id := -1
var drag_world := Vector3.ZERO
@@ -37,6 +41,10 @@ var _dying := {} ## key -> Restzeit der Sterbe-Animation
const DEATH_TIME := 0.45
var _shots: Array = [] ## fliegende Geschosse {"node", "from", "to", "t", "dur"}
var shots_total := 0 ## Zähler aller Geschosse, für Tests
var _effects: Array = [] ## Fähigkeitsformen {"node", "t", "dur", "grow"}
var effects_total := 0
var _ring_fx: TorusMesh
var _disc_fx: CylinderMesh
var _shot_mesh: SphereMesh
var _slash_mesh: BoxMesh
var _origin := Vector3.ZERO
@@ -63,6 +71,16 @@ func _ready() -> void:
_shot_mesh.rings = 4
_slash_mesh = BoxMesh.new()
_slash_mesh.size = Vector3(0.12, 0.12, 0.7)
_ring_fx = TorusMesh.new()
_ring_fx.inner_radius = 0.55
_ring_fx.outer_radius = 0.7
_ring_fx.ring_segments = 8
_ring_fx.rings = 28
_disc_fx = CylinderMesh.new()
_disc_fx.top_radius = 0.9
_disc_fx.bottom_radius = 0.9
_disc_fx.height = 0.06
_disc_fx.radial_segments = 6
_build_tiles()
_build_bench()
set_process(true)
@@ -228,6 +246,59 @@ func _process(delta: float) -> void:
_update_tiles()
_update_units(delta)
_update_shots(delta)
_update_effects(delta)
## Sichtbare Fähigkeit: Tank = wachsender Schildring, Schütze = Fläche am Ziel,
## Heiler = aufsteigender Heilring. Farbe nach Element.
func spawn_effect(world: Vector3, target: Vector3, role: int, col: Color) -> void:
var mi := MeshInstance3D.new()
var m := StandardMaterial3D.new()
m.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
m.albedo_color = Color(col, 0.75)
m.emission_enabled = true
m.emission = col
m.emission_energy_multiplier = 1.4
mi.material_override = m
var fx := {"node": mi, "t": 0.0, "dur": 0.45, "rise": 0.0, "grow": 1.0}
match role:
Data.Role.TANK:
mi.mesh = _ring_fx
mi.position = world + Vector3(0.0, 0.3, 0.0)
fx["grow"] = 1.6
Data.Role.DPS:
mi.mesh = _disc_fx
mi.position = target + Vector3(0.0, 0.12, 0.0)
mi.scale = Vector3(0.3, 1.0, 0.3)
fx["grow"] = 1.4
fx["dur"] = 0.35
_:
mi.mesh = _ring_fx
mi.position = world + Vector3(0.0, 0.1, 0.0)
fx["rise"] = 1.2
fx["grow"] = 1.2
fx["dur"] = 0.6
add_child(mi)
effects_total += 1
_effects.append(fx)
func _update_effects(delta: float) -> void:
var keep := []
for fx in _effects:
fx["t"] += delta
var f: float = fx["t"] / fx["dur"]
var node: MeshInstance3D = fx["node"]
if f >= 1.0:
node.queue_free()
continue
var g: float = lerpf(1.0, fx["grow"], f) if fx["grow"] >= 1.0 else 1.0
node.scale = Vector3(g, 1.0, g) if node.mesh == _disc_fx else Vector3(g, g, g)
node.position.y += fx["rise"] * delta
var m: StandardMaterial3D = node.material_override
m.albedo_color.a = 0.75 * (1.0 - f)
keep.append(fx)
_effects = keep
## Sichtbarer Angriff: Fernkampf schickt eine Kugel, Nahkampf einen kurzen Hieb.
@@ -276,6 +347,11 @@ func _update_tiles() -> void:
var slot := {"kind": "board", "hex": to_board(h)}
var col := Color(0.20, 0.23, 0.31) if own else Color(0.28, 0.18, 0.20)
var emit := 0.0
var in_range := range_radius > 0 and range_center.x >= 0 \
and Hex.distance(h, range_center) <= range_radius and h != range_center
if in_range and not drag_active:
col = col.lerp(range_color, 0.45)
emit = 0.22
if own and _is_hover(slot):
col = Color(0.42, 0.60, 0.82)
emit = 0.40
@@ -343,6 +419,7 @@ func _update_units(delta: float) -> void:
"slow": u.slow_timer > 0.0 and u.slow > 0.0,
"stun": u.stun_timer > 0.0,
"shield": clampf(u.shield / maxf(1.0, u.max_hp), 0.0, 1.0),
"face": world_pos(_mirror(u.target.pos, my_team)) if (u.target != null and u.target.alive) else null,
}
else:
for bh in game.human.board:
@@ -410,7 +487,23 @@ func _update_units(delta: float) -> void:
# und sieht nicht angehoben aus.
node.position = target
else:
node.position = node.position.lerp(target, clampf(delta * MOVE_LERP, 0.0, 1.0))
var flat_from := Vector3(node.position.x, 0.0, node.position.z)
var flat_to := Vector3(target.x, 0.0, target.z)
var moving := flat_from.distance_to(flat_to) > 0.05
var next := node.position.lerp(target, clampf(delta * MOVE_LERP, 0.0, 1.0))
if moving:
# Blick in Laufrichtung, kleines Wippen wie ein Schritt.
var dirv := flat_to - flat_from
node.rotation.y = lerp_angle(node.rotation.y, atan2(dirv.x, dirv.z), clampf(delta * 10.0, 0.0, 1.0))
var body: MeshInstance3D = node.get_meta("body")
body.position.y = body.get_meta("base_y", body.position.y) + absf(sin(Time.get_ticks_msec() / 90.0)) * 0.12
elif d.get("face", null) != null:
var fd: Vector3 = d["face"] - flat_to
if fd.length() > 0.05:
node.rotation.y = lerp_angle(node.rotation.y, atan2(fd.x, fd.z), clampf(delta * 6.0, 0.0, 1.0))
var body2: MeshInstance3D = node.get_meta("body")
body2.position.y = body2.get_meta("base_y", body2.position.y)
node.position = next
var body: MeshInstance3D = node.get_meta("body")
var mat: StandardMaterial3D = body.material_override
var base: Color = d["avatar"].color()
@@ -446,8 +539,15 @@ func _update_units(delta: float) -> void:
## Knoten eines Avatars, oder null. Wird von den Tests genutzt.
## Findet auch Kampfeinheiten, die unter einem anderen Schlüssel liegen.
func unit_node(avatar_id: int) -> Node3D:
return _units.get("a%d" % avatar_id)
var direct = _units.get("a%d" % avatar_id)
if direct != null:
return direct
for n in _units.values():
if n.get_meta("avatar_id", -1) == avatar_id:
return n
return null
## Für das Overlay: Arena-Feld so spiegeln, wie das Brett gezeichnet wird.
@@ -555,8 +655,10 @@ func _make_unit(a: Avatar, side: int) -> Node3D:
sph.rings = 10
body.mesh = sph
body.position.y = 0.55
body.set_meta("base_y", body.position.y)
root.add_child(body)
root.set_meta("body", body)
root.set_meta("avatar_id", a.id)
_add_ornament(root, a, body.position.y)