Populate the first region: 6 placeholder POIs + gated center portal

poi.tscn now uses the checkerboard debug texture instead of the ruins
sprite, and poi.gd gained a marker_color export (multiplied into the
existing visited/undiscovered dimming) so each POI marker can be
tinted a distinct color. Six instances placed inside region k=0's
wedge (angle ~12-48°, radius ~18-38, clear of the border walls),
tagged with the "region1_pois" group, in red/orange/yellow/green/
blue/purple.

Simplified InteractionManager's "Erkunden" content to a plain
placeholder ("Das wurde besucht.") instead of the old random
animal-track flavor text, and removed the now-dead handle_explore()
submenu it drove. Added InteractionManager.open_message() for POIs
that need an info popup without the explore/rest/close menu.

New poi_gate.gd/.tscn: a portal placed near region k=0's table-edge
border. Checks whether every POI in "region1_pois" has been visited;
if not, shows a "not yet" message, otherwise teleports the player to
target_position (the center) — a one-way shortcut past the border
wall rather than an opening in it, matching the "explore everything,
then unlock the way onward" progression the user described (this
pattern will repeat per outer region later).

Verified headless: all 6 POIs register in the group with correct
names/colors, gate refuses the transition at 0/6 and 5/6 visited and
allows it at 6/6, and the teleport lands the player exactly on
target_position.
This commit is contained in:
Jonas Reith 2026-08-26 21:05:38 +02:00
parent 37e0151988
commit 92f220e208
8 changed files with 163 additions and 41 deletions

View File

@ -21,37 +21,20 @@ func _get_main_options() -> Array:
func handle_selection(action: String) -> void: func handle_selection(action: String) -> void:
match action: match action:
"explore": "explore":
InteractionUI.show_menu("Erkunden", _get_explore_options()) # Platzhalter, bis echte Erkunden-Events pro POI existieren
InteractionUI.show_text("Das wurde besucht.")
"rest": "rest":
InteractionUI.show_text("Du rastest eine Weile...\nDu fühlst dich erholt.") InteractionUI.show_text("Du rastest eine Weile...\nDu fühlst dich erholt.")
"close": "close":
close() close()
func handle_explore(action: String) -> void: # Für POIs außerhalb des generischen Menüs (z.B. Portale): Panel mit reinem
match action: # Info-Text öffnen, ohne die Erkunden/Ausruhen/Beenden-Optionen.
"track_follow": func open_message(poi: Node, title: String, text: String) -> void:
var success = randf() > 0.5 active = true
if success: current_poi = poi
InteractionUI.show_text("Du folgst den Spuren...\nEine seltene Tierbeobachtung!") InteractionUI.show_menu(title, [])
else: InteractionUI.show_text(text)
InteractionUI.show_text("Du folgst den Spuren...\nDas Tier ist verschwunden.")
"track_leave":
close()
func _get_explore_options() -> Array:
# Zufälliges Ereignis beim Erkunden
var roll = randf()
if roll < 0.5:
InteractionUI.show_text("Du findest Tierspuren!")
return [
{ "label": "Tier verfolgen", "action": "track_follow" },
{ "label": "In Ruhe lassen", "action": "track_leave" },
]
else:
InteractionUI.show_text("Nichts Besonderes zu finden.")
return [
{ "label": "Zurück", "action": "close" },
]
func close() -> void: func close() -> void:
active = false active = false

View File

@ -51,10 +51,6 @@ func _clear_options() -> void:
child.queue_free() child.queue_free()
func _on_option_pressed(action: String) -> void: func _on_option_pressed(action: String) -> void:
# Unterscheiden ob wir im Erkunden-Submenu sind
if action in ["track_follow", "track_leave"]:
InteractionManager.handle_explore(action)
else:
InteractionManager.handle_selection(action) InteractionManager.handle_selection(action)
func _input(event: InputEvent) -> void: func _input(event: InputEvent) -> void:

View File

@ -5,6 +5,7 @@
[ext_resource type="Texture2D" uid="uid://epcqli428yem" path="res://assets/sprites/player/debug_player.png" id="2_h2yge"] [ext_resource type="Texture2D" uid="uid://epcqli428yem" path="res://assets/sprites/player/debug_player.png" id="2_h2yge"]
[ext_resource type="Script" uid="uid://b0y06y7c0bihj" path="res://camera_rig.gd" id="3_h2yge"] [ext_resource type="Script" uid="uid://b0y06y7c0bihj" path="res://camera_rig.gd" id="3_h2yge"]
[ext_resource type="PackedScene" uid="uid://be6ufrc3dsjge" path="res://poi.tscn" id="5_lquwl"] [ext_resource type="PackedScene" uid="uid://be6ufrc3dsjge" path="res://poi.tscn" id="5_lquwl"]
[ext_resource type="PackedScene" path="res://poi_gate.tscn" id="5_gate"]
[ext_resource type="PackedScene" uid="uid://dmhs1oy1kh6hi" path="res://assets/models/mountain.glb" id="6_7mycd"] [ext_resource type="PackedScene" uid="uid://dmhs1oy1kh6hi" path="res://assets/models/mountain.glb" id="6_7mycd"]
[ext_resource type="PackedScene" uid="uid://gl4qpk4wto1e" path="res://assets/models/mountain2v5.glb" id="7_272bh"] [ext_resource type="PackedScene" uid="uid://gl4qpk4wto1e" path="res://assets/models/mountain2v5.glb" id="7_272bh"]
[ext_resource type="PackedScene" uid="uid://be2uq7ixlbv6b" path="res://assets/models/forest.glb" id="8_5vw27"] [ext_resource type="PackedScene" uid="uid://be2uq7ixlbv6b" path="res://assets/models/forest.glb" id="8_5vw27"]
@ -205,6 +206,41 @@ far = 500.0
[node name="POI" parent="." unique_id=1770000468 instance=ExtResource("5_lquwl")] [node name="POI" parent="." unique_id=1770000468 instance=ExtResource("5_lquwl")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0.5, 2) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0.5, 2)
[node name="Ort1" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24.15, -1.214, 6.47)
poi_name = "Ort 1"
marker_color = Color(1, 0.2, 0.2, 1)
[node name="Ort2" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.68, -1.214, 17.68)
poi_name = "Ort 2"
marker_color = Color(1, 0.6, 0.1, 1)
[node name="Ort3" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 37.17, -3.443, 7.9)
poi_name = "Ort 3"
marker_color = Color(1, 0.95, 0.2, 1)
[node name="Ort4" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 25.43, -3.443, 28.24)
poi_name = "Ort 4"
marker_color = Color(0.2, 0.9, 0.3, 1)
[node name="Ort5" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 19.05, -0.7, 11.0)
poi_name = "Ort 5"
marker_color = Color(0.2, 0.5, 1, 1)
[node name="Ort6" parent="." instance=ExtResource("5_lquwl") groups=["region1_pois"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 38.1, -4.471, 22.0)
poi_name = "Ort 6"
marker_color = Color(0.7, 0.3, 0.9, 1)
[node name="GateToCenter" parent="." instance=ExtResource("5_gate")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14.72, 0.157, 8.5)
poi_name = "Tor zur Mitte"
target_position = Vector3(0, 0.5, 0)
[node name="Node3D" type="Node3D" parent="." unique_id=597556846] [node name="Node3D" type="Node3D" parent="." unique_id=597556846]
transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 3.6214647, -0.23645921, 0.47627413) transform = Transform3D(0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1, 3.6214647, -0.23645921, 0.47627413)

8
poi.gd
View File

@ -1,6 +1,7 @@
extends Area3D extends Area3D
@export var poi_name: String = "Ruinen" @export var poi_name: String = "Ruinen"
@export var marker_color: Color = Color.WHITE
@onready var sprite: AnimatedSprite3D = $AnimatedSprite3D @onready var sprite: AnimatedSprite3D = $AnimatedSprite3D
@onready var name_label: Label3D = $Billboard/NameLabel @onready var name_label: Label3D = $Billboard/NameLabel
@ -19,13 +20,12 @@ func _ready() -> void:
func _update_visuals() -> void: func _update_visuals() -> void:
if visited: if visited:
name_label.text = poi_name name_label.text = poi_name
# Normale Farbe sprite.modulate = marker_color
sprite.modulate = Color(1.0, 1.0, 1.0, 1.0)
name_label.modulate = Color(1.0, 1.0, 1.0, 1.0) name_label.modulate = Color(1.0, 1.0, 1.0, 1.0)
else: else:
name_label.text = "???" name_label.text = "???"
# Ausgegraut # Ausgegraut, behält aber die Kennfarbe
sprite.modulate = Color(0.4, 0.4, 0.4, 1.0) sprite.modulate = marker_color * Color(0.4, 0.4, 0.4, 1.0)
name_label.modulate = Color(0.6, 0.6, 0.6, 1.0) name_label.modulate = Color(0.6, 0.6, 0.6, 1.0)
func _on_body_entered(body: Node3D) -> void: func _on_body_entered(body: Node3D) -> void:

View File

@ -1,20 +1,16 @@
[gd_scene format=3 uid="uid://be6ufrc3dsjge"] [gd_scene format=3 uid="uid://be6ufrc3dsjge"]
[ext_resource type="Script" uid="uid://c12tpdqhr2l5" path="res://poi.gd" id="1_gwpk6"] [ext_resource type="Script" uid="uid://c12tpdqhr2l5" path="res://poi.gd" id="1_gwpk6"]
[ext_resource type="Texture2D" uid="uid://c0u8xjymrfw2x" path="res://assets/sprites/enviroment/Brown-gray_ruins2.png" id="2_yof6k"] [ext_resource type="Texture2D" uid="uid://c3ajv7vb8c4fb" path="res://assets/debug/checkerboard.png" id="2_yof6k"]
[sub_resource type="SphereShape3D" id="SphereShape3D_gwpk6"] [sub_resource type="SphereShape3D" id="SphereShape3D_gwpk6"]
radius = 0.8699188 radius = 0.8699188
[sub_resource type="AtlasTexture" id="AtlasTexture_mekjb"]
atlas = ExtResource("2_yof6k")
region = Rect2(0, 0, 80, 80)
[sub_resource type="SpriteFrames" id="SpriteFrames_0surh"] [sub_resource type="SpriteFrames" id="SpriteFrames_0surh"]
animations = [{ animations = [{
"frames": [{ "frames": [{
"duration": 1.0, "duration": 1.0,
"texture": SubResource("AtlasTexture_mekjb") "texture": ExtResource("2_yof6k")
}], }],
"loop": true, "loop": true,
"name": &"default", "name": &"default",

59
poi_gate.gd Normal file
View File

@ -0,0 +1,59 @@
extends Area3D
@export var poi_name: String = "Tor zur Mitte"
@export var marker_color: Color = Color(1.0, 0.9, 0.4)
@export var required_group: String = "region1_pois"
@export var target_position: Vector3 = Vector3.ZERO
@onready var sprite: AnimatedSprite3D = $AnimatedSprite3D
@onready var name_label: Label3D = $Billboard/NameLabel
@onready var prompt: Label3D = $Billboard/InteractionPrompt
var player_nearby: bool = false
func _ready() -> void:
prompt.visible = false
prompt.text = "[F] Interact"
name_label.text = poi_name
_update_visuals()
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
func _all_required_visited() -> bool:
var pois := get_tree().get_nodes_in_group(required_group)
if pois.is_empty():
return false
for poi in pois:
if not poi.visited:
return false
return true
func _update_visuals() -> void:
if _all_required_visited():
sprite.modulate = marker_color
else:
sprite.modulate = marker_color * Color(0.4, 0.4, 0.4, 1.0)
func _on_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
player_nearby = true
prompt.visible = true
_update_visuals()
func _on_body_exited(body: Node3D) -> void:
if body.is_in_group("player"):
player_nearby = false
prompt.visible = false
func _input(event: InputEvent) -> void:
if player_nearby and Input.is_action_just_pressed("interact"):
_try_transition()
func _try_transition() -> void:
if _all_required_visited():
InteractionManager.open_message(self, poi_name, "Du betrittst die Mitte der Insel.")
var player := get_tree().get_first_node_in_group("player")
if player:
player.global_position = target_position
else:
InteractionManager.open_message(self, poi_name, "Hier geht es noch nicht weiter.\nErst müssen alle sechs Orte dieser Region erkundet sein.")

1
poi_gate.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://d34t6agm5pxgt

51
poi_gate.tscn Normal file
View File

@ -0,0 +1,51 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://poi_gate.gd" id="1_gate"]
[ext_resource type="Texture2D" uid="uid://c3ajv7vb8c4fb" path="res://assets/debug/checkerboard.png" id="2_gate"]
[sub_resource type="SphereShape3D" id="SphereShape3D_gate"]
radius = 0.8699188
[sub_resource type="SpriteFrames" id="SpriteFrames_gate"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("2_gate")
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[sub_resource type="BoxShape3D" id="BoxShape3D_gate"]
size = Vector3(0.8, 2.132, 2.333828)
[node name="POIGate" type="Area3D"]
script = ExtResource("1_gate")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.6096671)
shape = SubResource("SphereShape3D_gate")
[node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="."]
sprite_frames = SubResource("SpriteFrames_gate")
[node name="StaticBody3D" type="StaticBody3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"]
transform = Transform3D(0.6, 0, 0, 0, 0.6, 0, 0, 0, 0.6, 0, 0, -0.69380605)
shape = SubResource("BoxShape3D_gate")
[node name="Billboard" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
[node name="NameLabel" type="Label3D" parent="Billboard"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0)
billboard = 1
text = "Tor zur Mitte"
font_size = 16
[node name="InteractionPrompt" type="Label3D" parent="Billboard"]
visible = false
billboard = 1
font_size = 12