This commit is contained in:
Team3
2026-09-16 16:15:32 +02:00
parent e813b5f7aa
commit 7c4edaa439
19 changed files with 1244 additions and 636 deletions

View File

@@ -11,7 +11,6 @@ const GRAVITY := 24.0
const JUMP := 9.0
const FOLGE_ABSTAND := 2.5 # so weit bleibt der Geist auf der Spur zurück
const LEINE := 14.0 # weiter weg reißt die Leine: er wird nachgezogen
const FOLGE_NAH := 5.0 # näher dran braucht es keinen Anlauf
const SCHWEBE := 0.25 # eine Handbreit über dem Boden
const PITCH := -0.96 # 55° von oben, fest wie in Diablo
const CAM_YAW := PI * 0.25 # Blick diagonal über den Bezirk
@@ -29,9 +28,12 @@ var arm: Node3D
var cam: Camera3D
var _dreh_rest := 0.0 # noch zu drehender Winkel (Q)
var _zoom_stufe := 0
var _anlauf: Array = [] # Weg zu dem Menschen, an den man sich gehängt hat
var _stockt := 0.0 # Sekunden ohne Vorankommen
var spruenge := 0 # wie oft die Leine nachziehen musste
var _leine_pfad: Array = [] # Pfad zum Verfolgten, alle 0,4 s neu
var _leine_uhr := 0.0
var _verloren := 0.0 # Sekunden zu weit weg vom Verfolgten
var _stockt := 0.0 # Sekunden ohne Vorankommen (eingeklemmt)
var _vorher := Vector3.ZERO # Position im letzten Physikbild
var spruenge := 0 # wie oft Leine oder Stadt nachziehen mussten
var body: Node3D
var _schleier: MeshInstance3D
var _kern: MeshInstance3D
@@ -111,8 +113,7 @@ func _ready() -> void:
func set_mode(m: int) -> void:
mode = m
folgt = null
collision_layer = 1
collision_mask = 1
collision_layer = 1 # die Maske stellt main je Welt: jede Welt hat ihre Ebene
if m == Mode.GEIST:
body.scale = Vector3.ONE * 0.6
_schleier_mat.albedo_color.a = 0.3
@@ -177,7 +178,11 @@ func _physics_process(delta: float) -> void:
## Wer mit gedrückter Taste zwei Sekunden nicht vorankommt, steckt fest --
## dann setzt ihn die Stadt auf die nächste Straße.
func _eingeklemmt(delta: float, will: bool) -> void:
if mode != Mode.GEIST or not will or Vector2(velocity.x, velocity.z).length() > 0.3:
# gemessen am Weg, nicht an der Geschwindigkeit: in einer Ecke schiebt man mit
# voller Geschwindigkeit gegen die Wand und kommt doch nicht vom Fleck
var weg := Vector2(global_position.x - _vorher.x, global_position.z - _vorher.z).length()
_vorher = global_position
if mode != Mode.GEIST or not will or weg > 0.3 * delta:
_stockt = 0.0
return
_stockt += delta
@@ -210,37 +215,39 @@ func _fallen(delta: float) -> void:
velocity.y -= GRAVITY * delta
## An einen Menschen hängen. Steht er weit weg, wird erst der Weg zu ihm
## geplant: aus dem Haus, über die Straßen, in seine Wohnung.
## An einen Menschen hängen. Den Weg zu ihm findet das Navigationsnetz.
func anhaengen(m: Bewohner) -> void:
folgt = m
_anlauf = []
_stockt = 0.0
if global_position.distance_to(m.global_position) > FOLGE_NAH:
var ort: String = m.place if m._path.is_empty() else ""
_anlauf = Orte.route_zu(m.places, m.netz, global_position, m.global_position,
ort, m.etage(ort) if ort != "" else 0)
_leine_pfad = []
_leine_uhr = 0.0
_verloren = 0.0
## Folgen heißt: seiner Spur nachgehen, durch dieselben Türen und Treppen.
## Er läuft so schnell wie nötig, um dranzubleiben -- auch bei hohem Tempo.
## Folgen heißt: ihm nachgehen, durch dieselben Türen und Treppen. Der Pfad
## kommt vom Navigationsnetz und wird laufend neu geplant. Er läuft so schnell
## wie nötig, um dranzubleiben -- auch bei hohem Tempo.
func _an_der_leine(delta: float) -> void:
var mensch := folgt as Bewohner
var ziel: Vector3
if not _anlauf.is_empty():
ziel = _anlauf[0]
var bis := ziel - global_position
bis.y = 0.0
if bis.length() < 0.6 and absf(ziel.y - global_position.y) < 1.6:
_anlauf.pop_front()
if _anlauf.is_empty():
ziel = mensch.spur_punkt(FOLGE_ABSTAND)
else:
ziel = _anlauf[0]
_leine_uhr -= delta
if _leine_uhr <= 0.0 or _leine_pfad.is_empty():
_leine_uhr = 0.4
_leine_pfad = []
if mensch.nav_map.is_valid():
var pts := NavigationServer3D.map_get_path(mensch.nav_map, global_position,
mensch.global_position, true)
for i in range(1, pts.size()):
_leine_pfad.append(pts[i])
if pts.size() < 2:
print(" [kein Weg] Geist von %s zu %s bei %s" % [str(global_position),
mensch.data["name"], str(mensch.global_position)])
var abstand := global_position.distance_to(mensch.global_position)
var ziel: Vector3 = _leine_pfad[0] if not _leine_pfad.is_empty() else mensch.global_position
var d := ziel - global_position
d.y = 0.0
if not _leine_pfad.is_empty() and d.length() < 0.5 and absf(ziel.y - global_position.y) < 1.6:
_leine_pfad.pop_front()
var tempo := maxf(SPEED_GEIST, Bewohner.GEHTEMPO * Game.zeit_faktor() * 1.4)
if d.length() > 0.3:
if abstand > FOLGE_ABSTAND and d.length() > 0.05:
var v := d.normalized() * minf(tempo, d.length() / delta)
velocity.x = v.x
velocity.z = v.z
@@ -250,16 +257,13 @@ func _an_der_leine(delta: float) -> void:
velocity.z = 0.0
_fallen(delta)
move_and_slide()
if d.length() > 0.6 and Vector2(velocity.x, velocity.z).length() < 0.3:
_stockt += delta
else:
_stockt = 0.0
var zu_weit: bool = _anlauf.is_empty() \
and global_position.distance_to(mensch.global_position) > LEINE
if _stockt > 2.0 or zu_weit:
global_position = ziel # hängen geblieben: die Leine zieht ihn nach
# Verloren? Nur wenn das Netz keinen Weg weiß und er zu weit weg ist,
# wird er nachgezogen -- ein Notnagel, kein Spielzug.
_verloren = _verloren + delta if abstand > LEINE and _leine_pfad.is_empty() else 0.0
if _verloren > 3.0:
global_position = mensch.global_position + Vector3(0, 0.3, 0)
velocity = Vector3.ZERO
_stockt = 0.0
_verloren = 0.0
spruenge += 1