Bear, level selector (to be removed), new matador spawn, new bear level, saving config updates source files

This commit is contained in:
2026-08-23 17:33:15 +03:00
parent d749485126
commit b9e3147519
49 changed files with 2248 additions and 802 deletions
+10
View File
@@ -0,0 +1,10 @@
[gd_scene format=3 uid="uid://map_screen_v1"]
[ext_resource type="Script" path="res://levels/map_screen.gd" id="1_map"]
[node name="MapScreen" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource("1_map")
+431
View File
@@ -0,0 +1,431 @@
[gd_scene format=3 uid="uid://co4tgvdpdte4v"]
[ext_resource type="PackedScene" uid="uid://dio67r8ambe7h" path="res://Assets/arena_placeholder_v01.glb" id="2_nxogm"]
[ext_resource type="PackedScene" uid="uid://cl54dt38yj2x" path="res://Assets/Arena.glb" id="3_fy5k1"]
[ext_resource type="Script" uid="uid://dou5ar6tkmng1" path="res://levels/matador_spawn.gd" id="3_spawn"]
[ext_resource type="PackedScene" uid="uid://cmxqocsdbgufr" path="res://Animations/mees_plaksutab.glb" id="5_m7jmp"]
[ext_resource type="PackedScene" uid="uid://1y07gn65ktsi" path="res://Animations/ANIM_publik_mees.glb" id="7_xlvrw"]
[ext_resource type="Script" uid="uid://wj6tgh8nb0ka" path="res://Animations/anim_publik_mees.gd" id="8_73fnb"]
[ext_resource type="Script" uid="uid://bs682ey3p1ivj" path="res://publikum_controller.gd" id="9_pubctrl"]
[ext_resource type="PackedScene" uid="uid://dxe72ig8gfbcg" path="res://Assets/arena model2026.glb" id="10_5juve"]
[ext_resource type="Script" uid="uid://cj0ytlkuavmf4" path="res://crowd_billboards.gd" id="11_crowd"]
[sub_resource type="BoxShape3D" id="BoxShape3D_ulcgi"]
[sub_resource type="Environment" id="Environment_73fnb"]
[node name="arena_level" type="Node3D" unique_id=701327120]
[node name="Node3D" type="Node3D" parent="." unique_id=1772505910]
[node name="StaticBody3D" type="StaticBody3D" parent="Node3D" unique_id=2083665760]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Node3D/StaticBody3D" unique_id=1028681009]
transform = Transform3D(30, 0, 0, 0, 1, 0, 0, 0, 30, 0, -0.5, 0)
shape = SubResource("BoxShape3D_ulcgi")
[node name="arena_placeholder_v01" parent="Node3D" unique_id=173047470 instance=ExtResource("2_nxogm")]
[node name="Arena_draft" parent="Node3D/arena_placeholder_v01" index="0" unique_id=1422517759]
visible = false
[node name="Wall" parent="Node3D/arena_placeholder_v01" index="1" unique_id=1497936357]
visible = false
[node name="CollisionShape3D" parent="Node3D/arena_placeholder_v01/Wall/StaticBody3D" parent_id_path=PackedInt32Array(173047470, 773519804) index="0" unique_id=580729261]
transform = Transform3D(0.98309773, 0, 0, 0, 0.9558868, 0, 0, 0, 4.061706, 0.13596812, -0.091219574, -5.4371103e-09)
[node name="Arena2" parent="Node3D/arena_placeholder_v01" unique_id=1270130903 instance=ExtResource("3_fy5k1")]
[node name="arena model2026" parent="Node3D/arena_placeholder_v01" unique_id=1744180217 instance=ExtResource("10_5juve")]
transform = Transform3D(0.99999994, 0, 0, 0, 0.99999994, 0, 0, 0, 0.99999994, -0.901, -0.6, 0)
visible = false
[node name="MatadorSpawner" type="Node3D" parent="." unique_id=1524912967]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 14)
script = ExtResource("3_spawn")
[node name="MatadorSpawner2" type="Node3D" parent="." unique_id=2081003535]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 3, 0)
script = ExtResource("3_spawn")
[node name="MatadorSpawner4" type="Node3D" parent="." unique_id=497566616]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 3, 0)
script = ExtResource("3_spawn")
[node name="MatadorSpawner3" type="Node3D" parent="." unique_id=618446747]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, -15)
script = ExtResource("3_spawn")
[node name="Lights" type="Node3D" parent="." unique_id=614769858]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Lights" unique_id=960453715]
transform = Transform3D(0.01535992, 0.56825536, -0.82270896, 0.16280846, 0.8104039, 0.56279576, 0.9865382, -0.14258848, -0.08006904, -4.08134, 9.197985, 0.97087634)
light_energy = 0.4
light_indirect_energy = 0.767
shadow_blur = 0.47
[node name="SpotLight3D2" type="SpotLight3D" parent="Lights" unique_id=1654524086]
transform = Transform3D(0.904133, 0.30962402, -0.2944087, 0.3084627, 0.0037425472, 0.9512291, 0.2956252, -0.95085174, -0.0921237, -3.629381, 10.86347, -2.0124264)
light_energy = 16.0
light_indirect_energy = 26.095
distance_fade_enabled = true
spot_range = 32.90586
spot_angle = 73.93
[node name="SpotLight3D" type="SpotLight3D" parent="Lights" unique_id=1408554387]
transform = Transform3D(0.9763736, -9.445564e-09, 0.21608931, -0.20963383, 0.24260214, 0.9472053, -0.052423734, -0.9701259, 0.23687032, 4.0400305, 13.463301, 3.9410677)
visible = false
light_energy = 13.695
shadow_enabled = true
spot_range = 32.90586
[node name="SUN_DirectionalLight3D2" type="DirectionalLight3D" parent="Lights" unique_id=1840798568]
transform = Transform3D(0.45811793, 0.34843865, -0.81775206, 0.5316611, 0.62986195, 0.56622505, 0.7123655, -0.69416475, 0.10329977, 0, 8.980043, 0)
light_energy = 0.106
shadow_enabled = true
shadow_blur = 10.0
[node name="PublikNode" type="Node3D" parent="." unique_id=537560774]
visible = false
[node name="publikum2" type="Node3D" parent="PublikNode" unique_id=632248377]
transform = Transform3D(-0.91522944, 0, -0.4029331, 0, 1, 0, 0.4029331, 0, -0.91522944, 4.195425, 3.6553268, 13.885913)
visible = false
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum2" unique_id=1523494533 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum2" unique_id=1136452990 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum2" unique_id=1034056333 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum2" unique_id=1301549876 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum2" unique_id=426787640 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum2" unique_id=264608279 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum4" type="Node3D" parent="PublikNode" unique_id=627563732]
transform = Transform3D(-0.629974, 0, -0.77661633, 0, 1, 0, 0.77661633, 0, -0.629974, 9.842437, 3.6553268, 10.401334)
visible = false
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum4" unique_id=1220293959 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum4" unique_id=1450591086 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum4" unique_id=1560510378 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum4" unique_id=37533204 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum4" unique_id=1261306009 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum4" unique_id=1900385491 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum5" type="Node3D" parent="PublikNode" unique_id=1812640124]
transform = Transform3D(-0.39064395, 0, 0.920542, 0, 1, 0, -0.920542, 0, -0.39064395, -14.538336, 3.6553268, 3.9378712)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum5" unique_id=83635667 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum5" unique_id=1143183809 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum5" unique_id=1283965822 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum5" unique_id=1691614127 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum5" unique_id=1236404634 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum5" unique_id=606457826 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum6" type="Node3D" parent="PublikNode" unique_id=629778401]
transform = Transform3D(-0.7681126, 0, 0.64031476, 0, 1, 0, -0.64031476, 0, -0.7681126, -11.103292, 3.6553288, 9.335619)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum6" unique_id=1894579943 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum6" unique_id=1361331032 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum6" unique_id=1108954377 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum6" unique_id=300775025 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum6" unique_id=1733379910 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum6" unique_id=903641089 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum7" type="Node3D" parent="PublikNode" unique_id=1108195583]
transform = Transform3D(0.55061686, 0, -0.8347584, 0, 1, 0, 0.8347584, 0, 0.55061686, 13.163043, 3.4379854, -5.268345)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum7" unique_id=365348429 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum7" unique_id=1699361442 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum7" unique_id=934639622 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum7" unique_id=1585045587 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum7" unique_id=501748248 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum7" unique_id=92474890 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum8" type="Node3D" parent="PublikNode" unique_id=1138908471]
transform = Transform3D(0.85247177, 0, -0.5227735, 0, 1, 0, 0.5227735, 0, 0.85247177, 11.217102, 4.568224, -10.151715)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum8" unique_id=2110447734 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum8" unique_id=1304761336 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum8" unique_id=1725684741 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum8" unique_id=2002512340 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum8" unique_id=1527416664 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum8" unique_id=1248985994 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum3" type="Node3D" parent="PublikNode" unique_id=1978237178]
transform = Transform3D(0.8698311, 0, 0.49334967, 0, 1, 0, -0.49334967, 0, 0.8698311, -4.8910522, 4.627474, -14.224113)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum3" unique_id=878298260 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum3" unique_id=1331430063 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum3" unique_id=263054039 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum3" unique_id=374864807 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum3" unique_id=674678014 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum3" unique_id=2055934328 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum9" type="Node3D" parent="PublikNode" unique_id=1005200820]
transform = Transform3D(0.9760592, 0, 0.21750516, 0, 1, 0, -0.21750516, 0, 0.9760592, -2.1318102, 3.6553268, -13.557504)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum9" unique_id=1436817061 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum9" unique_id=375947457 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum9" unique_id=71021140 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum9" unique_id=2106738663 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum9" unique_id=283876866 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum9" unique_id=1033622841 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum11" type="Node3D" parent="PublikNode" unique_id=407707263]
transform = Transform3D(-0.95107377, 0, -0.30896416, 0, 1, 0, 0.30896416, 0, -0.95107377, 2.4321594, 3.6553268, 14.119543)
visible = false
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum11" unique_id=1140359395 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum11" unique_id=5897282 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum11" unique_id=528539077 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum11" unique_id=693156815 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum11" unique_id=1138785564 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum11" unique_id=576184958 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum13" type="Node3D" parent="PublikNode" unique_id=332757108]
transform = Transform3D(-0.2962035, 0, 0.95512486, 0, 1, 0, -0.95512486, 0, -0.2962035, -14.037817, 3.6553268, 2.0647745)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum13" unique_id=214289805 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum13" unique_id=1147139958 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.1134007, 0, 0.6326933)
[node name="mees_plaksutab3" parent="PublikNode/publikum13" unique_id=662075088 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum13" unique_id=664162740 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum13" unique_id=1967822989 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum13" unique_id=1108301136 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="publikum15" type="Node3D" parent="PublikNode" unique_id=485853358]
transform = Transform3D(0.43092924, 0, -0.90238583, 0, 1, 0, 0.90238583, 0, 0.43092924, 14.766085, 4.568224, -2.378694)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum15" unique_id=525464827 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum15" unique_id=852905531 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum15" unique_id=1785117697 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum15" unique_id=1380611321 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum15" unique_id=322138980 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum15" unique_id=1360012012 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="ANIM_publik_mees" parent="PublikNode" unique_id=941250087 instance=ExtResource("7_xlvrw")]
transform = Transform3D(-0.9976194, 0, 0.0689602, 0, 1, 0, -0.0689602, 0, -0.9976194, -2.9310942, 4.1751385, 14.861324)
script = ExtResource("8_73fnb")
[node name="publikum" type="Node3D" parent="PublikNode" unique_id=1122462483]
transform = Transform3D(0.70069104, 0, 0.7134648, 0, 1, 0, -0.7134648, 0, 0.70069104, -9.04294, 3.6553268, -10.339719)
script = ExtResource("9_pubctrl")
[node name="mees_plaksutab" parent="PublikNode/publikum" unique_id=1481053606 instance=ExtResource("5_m7jmp")]
[node name="mees_plaksutab2" parent="PublikNode/publikum" unique_id=984780880 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.8139224, 0, -0.33299255)
[node name="mees_plaksutab3" parent="PublikNode/publikum" unique_id=1137984224 instance=ExtResource("5_m7jmp")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.5662975, 0, 0.3316717)
[node name="mees_plaksutab4" parent="PublikNode/publikum" unique_id=1462909335 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -8.003071, 0, 1.5986013)
[node name="mees_plaksutab5" parent="PublikNode/publikum" unique_id=227073959 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.89589787, 0, 0.44426048, 0, 1, 0, -0.44426048, 0, 0.89589787, -7.1850557, 0.8316374, 0.36604595)
[node name="mees_plaksutab6" parent="PublikNode/publikum" unique_id=1680469468 instance=ExtResource("5_m7jmp")]
transform = Transform3D(0.97025216, 0, 0.24209678, 0, 1, 0, -0.24209678, 0, 0.97025216, -4.039103, 0.8316374, -0.8892679)
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1750284504]
environment = SubResource("Environment_73fnb")
[node name="CrowdBillboards" type="MultiMeshInstance3D" parent="." unique_id=106730614]
visible = false
script = ExtResource("11_crowd")
[editable path="Node3D/arena_placeholder_v01"]
[editable path="PublikNode/publikum2/mees_plaksutab"]
[editable path="PublikNode/publikum2/mees_plaksutab2"]
[editable path="PublikNode/publikum2/mees_plaksutab3"]
[editable path="PublikNode/publikum2/mees_plaksutab4"]
[editable path="PublikNode/publikum2/mees_plaksutab5"]
[editable path="PublikNode/publikum2/mees_plaksutab6"]
[editable path="PublikNode/publikum4/mees_plaksutab"]
[editable path="PublikNode/publikum4/mees_plaksutab2"]
[editable path="PublikNode/publikum4/mees_plaksutab3"]
[editable path="PublikNode/publikum4/mees_plaksutab4"]
[editable path="PublikNode/publikum4/mees_plaksutab5"]
[editable path="PublikNode/publikum4/mees_plaksutab6"]
[editable path="PublikNode/publikum5/mees_plaksutab"]
[editable path="PublikNode/publikum5/mees_plaksutab2"]
[editable path="PublikNode/publikum5/mees_plaksutab3"]
[editable path="PublikNode/publikum5/mees_plaksutab4"]
[editable path="PublikNode/publikum5/mees_plaksutab5"]
[editable path="PublikNode/publikum5/mees_plaksutab6"]
[editable path="PublikNode/publikum6/mees_plaksutab"]
[editable path="PublikNode/publikum6/mees_plaksutab2"]
[editable path="PublikNode/publikum6/mees_plaksutab3"]
[editable path="PublikNode/publikum6/mees_plaksutab4"]
[editable path="PublikNode/publikum6/mees_plaksutab5"]
[editable path="PublikNode/publikum6/mees_plaksutab6"]
[editable path="PublikNode/publikum7/mees_plaksutab"]
[editable path="PublikNode/publikum7/mees_plaksutab2"]
[editable path="PublikNode/publikum7/mees_plaksutab3"]
[editable path="PublikNode/publikum7/mees_plaksutab4"]
[editable path="PublikNode/publikum7/mees_plaksutab5"]
[editable path="PublikNode/publikum7/mees_plaksutab6"]
[editable path="PublikNode/publikum8/mees_plaksutab"]
[editable path="PublikNode/publikum8/mees_plaksutab2"]
[editable path="PublikNode/publikum8/mees_plaksutab3"]
[editable path="PublikNode/publikum8/mees_plaksutab4"]
[editable path="PublikNode/publikum8/mees_plaksutab5"]
[editable path="PublikNode/publikum8/mees_plaksutab6"]
[editable path="PublikNode/publikum3/mees_plaksutab"]
[editable path="PublikNode/publikum3/mees_plaksutab2"]
[editable path="PublikNode/publikum3/mees_plaksutab3"]
[editable path="PublikNode/publikum3/mees_plaksutab4"]
[editable path="PublikNode/publikum3/mees_plaksutab5"]
[editable path="PublikNode/publikum3/mees_plaksutab6"]
[editable path="PublikNode/publikum9/mees_plaksutab"]
[editable path="PublikNode/publikum9/mees_plaksutab2"]
[editable path="PublikNode/publikum9/mees_plaksutab3"]
[editable path="PublikNode/publikum9/mees_plaksutab4"]
[editable path="PublikNode/publikum9/mees_plaksutab5"]
[editable path="PublikNode/publikum9/mees_plaksutab6"]
[editable path="PublikNode/publikum11/mees_plaksutab"]
[editable path="PublikNode/publikum11/mees_plaksutab2"]
[editable path="PublikNode/publikum11/mees_plaksutab3"]
[editable path="PublikNode/publikum11/mees_plaksutab4"]
[editable path="PublikNode/publikum11/mees_plaksutab5"]
[editable path="PublikNode/publikum11/mees_plaksutab6"]
[editable path="PublikNode/publikum13/mees_plaksutab"]
[editable path="PublikNode/publikum13/mees_plaksutab2"]
[editable path="PublikNode/publikum13/mees_plaksutab3"]
[editable path="PublikNode/publikum13/mees_plaksutab4"]
[editable path="PublikNode/publikum13/mees_plaksutab5"]
[editable path="PublikNode/publikum13/mees_plaksutab6"]
[editable path="PublikNode/publikum15/mees_plaksutab"]
[editable path="PublikNode/publikum15/mees_plaksutab2"]
[editable path="PublikNode/publikum15/mees_plaksutab3"]
[editable path="PublikNode/publikum15/mees_plaksutab4"]
[editable path="PublikNode/publikum15/mees_plaksutab5"]
[editable path="PublikNode/publikum15/mees_plaksutab6"]
[editable path="PublikNode/ANIM_publik_mees"]
[editable path="PublikNode/publikum/mees_plaksutab"]
[editable path="PublikNode/publikum/mees_plaksutab2"]
[editable path="PublikNode/publikum/mees_plaksutab3"]
[editable path="PublikNode/publikum/mees_plaksutab4"]
[editable path="PublikNode/publikum/mees_plaksutab5"]
[editable path="PublikNode/publikum/mees_plaksutab6"]
+104
View File
@@ -0,0 +1,104 @@
extends CanvasLayer
const _GOLD := Color(1.00, 0.78, 0.22)
const _CREAM := Color(0.93, 0.88, 0.72)
const _BAR_W: float = 260.0
const _BAR_H: float = 18.0
const _BAR_BG: Color = Color(0.12, 0.04, 0.04, 0.88)
const _BAR_FILL: Color = Color(0.65, 0.08, 0.06)
const _BAR_ENRAGE: Color = Color(0.90, 0.32, 0.06)
var _bear: Node = null
var _fill: ColorRect = null
var _label: Label = null
var _max_hp: int = 1
var _current_hp: int = 1
func _ready() -> void:
layer = 10
_build_ui()
_find_bear.call_deferred()
func _find_bear() -> void:
var bears := get_tree().get_nodes_in_group(&"bear")
if bears.is_empty():
return
_bear = bears[0]
_bear.health_changed.connect(_on_health_changed)
_bear.killed.connect(_on_bear_killed)
var max_hp := int(DP.f("bear_max_hp"))
_on_health_changed(_bear.get_hp(), max_hp)
func _on_health_changed(current: int, max_hp: int) -> void:
_current_hp = current
_max_hp = max_hp
_update_bar()
func _on_bear_killed() -> void:
_current_hp = 0
_update_bar()
func _update_bar() -> void:
if _fill == null:
return
var frac := 0.0 if _max_hp <= 0 else clampf(float(_current_hp) / float(_max_hp), 0.0, 1.0)
_fill.size.x = _BAR_W * frac
var enrage_frac: float = DP.f("bear_enrage_frac")
_fill.color = _BAR_FILL.lerp(_BAR_ENRAGE, 1.0 - frac / enrage_frac if frac < enrage_frac else 0.0)
if _label != null:
_label.text = "BEAR %d / %d" % [maxi(_current_hp, 0), _max_hp]
func _build_ui() -> void:
var panel := PanelContainer.new()
panel.add_theme_stylebox_override(
"panel", UiTheme.plaque(UiTheme.LEATHER, UiTheme.BORDER, 4, 14.0, 8.0))
panel.anchor_left = 0.5
panel.anchor_right = 0.5
panel.anchor_top = 0.0
panel.anchor_bottom = 0.0
panel.grow_horizontal = Control.GROW_DIRECTION_BOTH
panel.offset_top = 16.0
panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(panel)
var col := VBoxContainer.new()
col.add_theme_constant_override("separation", 5)
col.mouse_filter = Control.MOUSE_FILTER_IGNORE
panel.add_child(col)
_label = Label.new()
_label.text = "BEAR"
_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_label.add_theme_font_override("font", UiFonts.body())
_label.add_theme_color_override("font_color", _GOLD)
_label.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.85))
_label.add_theme_constant_override("outline_size", 3)
_label.add_theme_font_size_override("font_size", 13)
_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
col.add_child(_label)
var bar_bg := PanelContainer.new()
bar_bg.custom_minimum_size = Vector2(_BAR_W, _BAR_H)
bar_bg.clip_contents = true
var bg_style := StyleBoxFlat.new()
bg_style.bg_color = _BAR_BG
bg_style.set_corner_radius_all(4)
bg_style.corner_detail = 4
bg_style.border_width_bottom = 2
bg_style.border_color = Color(UiTheme.GOLD.r, UiTheme.GOLD.g, UiTheme.GOLD.b, 0.35)
bar_bg.add_theme_stylebox_override("panel", bg_style)
bar_bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
col.add_child(bar_bg)
_fill = ColorRect.new()
_fill.color = _BAR_FILL
_fill.size = Vector2(_BAR_W, _BAR_H)
_fill.mouse_filter = Control.MOUSE_FILTER_IGNORE
bar_bg.add_child(_fill)
+1
View File
@@ -0,0 +1 @@
uid://dfk7dcvki7rqu
+49
View File
@@ -0,0 +1,49 @@
[gd_scene format=3 uid="uid://dfxigg3cr6nl5"]
[ext_resource type="PackedScene" uid="uid://bm6rmt3xprhjs" path="res://Assets/bear_level_arena.glb" id="1_beararena"]
[ext_resource type="Script" path="res://levels/bear_spawn.gd" id="2_spawn"]
[ext_resource type="PackedScene" path="res://Bear.tscn" id="3_bear"]
[ext_resource type="Script" path="res://levels/bear_hud.gd" id="4_bearhud"]
[sub_resource type="BoxShape3D" id="BoxShape3D_floor"]
[node name="bear_level" type="Node3D" unique_id=261730746]
[node name="Node3D" type="Node3D" parent="." unique_id=333447532]
[node name="StaticBody3D" type="StaticBody3D" parent="Node3D" unique_id=1466540248]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Node3D/StaticBody3D" unique_id=575454169]
transform = Transform3D(30, 0, 0, 0, 1, 0, 0, 0, 30, 0, -0.5, 0)
shape = SubResource("BoxShape3D_floor")
[node name="bear_level_arena" parent="." unique_id=1529242945 instance=ExtResource("1_beararena")]
transform = Transform3D(2.875, 0, 0, 0, 2.875, 0, 0, 0, 2.875, 0, 0, 0)
[node name="floor" parent="bear_level_arena/floor" parent_id_path=PackedInt32Array(1529242945, 41795043) index="0" unique_id=1653720920]
visible = false
[node name="wall" parent="bear_level_arena/floor" parent_id_path=PackedInt32Array(1529242945, 41795043) index="1" unique_id=2041884474]
visible = false
[node name="BearSpawner" type="Node3D" parent="." unique_id=47931969]
script = ExtResource("2_spawn")
enemy_scene = ExtResource("3_bear")
[node name="BearHud" type="CanvasLayer" parent="." unique_id=102938471]
script = ExtResource("4_bearhud")
[node name="Lights" type="Node3D" parent="." unique_id=937122836]
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Lights" unique_id=1786336124]
transform = Transform3D(1, 0, 0, 0, 0.7071067, 0.70710677, 0, -0.70710677, 0.7071067, 0, 36.945946, 70)
light_color = Color(1, 0.75782466, 0.7332154, 1)
light_energy = 1.796
shadow_enabled = true
[node name="DirectionalLight3D2" type="DirectionalLight3D" parent="Lights" unique_id=1308313006]
transform = Transform3D(1, 0, 0, 0, -0.8660254, 0.49999994, 0, -0.49999994, -0.8660254, 0, 112.56815, 93.37274)
light_color = Color(0, 0.33367383, 0.57971, 1)
light_energy = 0.616
[editable path="bear_level_arena"]
+33
View File
@@ -0,0 +1,33 @@
extends Node3D
signal matador_killed
signal all_defeated
@export var enemy_scene: PackedScene = null
var _alive: int = 0
var _resolved: bool = false
func _ready() -> void:
add_to_group(&"matador_spawn")
_spawn.call_deferred()
func _spawn() -> void:
if enemy_scene == null:
return
var bear: Node3D = enemy_scene.instantiate()
bear.position = Vector3.ZERO
get_parent().add_child(bear)
if bear.has_signal(&"killed"):
bear.killed.connect(_on_bear_killed)
_alive += 1
func _on_bear_killed() -> void:
matador_killed.emit()
_alive -= 1
if not _resolved:
_resolved = true
all_defeated.emit()
+1
View File
@@ -0,0 +1 @@
uid://bbtlwihtlc1wp
+26
View File
@@ -0,0 +1,26 @@
extends Node3D
## The reusable game shell: player, camera, HUD and the PS1 filter — everything that
## outlives a single level. On load it instances the active level's module (geometry,
## spawners, lighting, colliders) into LevelRoot. Switching levels frees the whole
## previous module first, so no old mesh or collider can survive into the next fight.
@onready var _level_root: Node3D = $LevelRoot
func _ready() -> void:
Run.ensure_run()
load_active_level()
## Tear down the current level module and build the run's active one. Freeing the whole
## subtree (not hiding it) is the point: a level's colliders and spawners cease to exist
## before the next level's are created.
func load_active_level() -> void:
for child: Node in _level_root.get_children():
_level_root.remove_child(child)
child.queue_free()
var level := Run.active_level()
if level == null or level.level_scene == null:
push_warning("game_shell: active level has no level_scene")
return
_level_root.add_child(level.level_scene.instantiate())
+1
View File
@@ -0,0 +1 @@
uid://ptsfs3wq5bje
+26
View File
@@ -0,0 +1,26 @@
class_name LevelDef
extends Resource
## Definition of one playable level on the run map — the arena today, later a cave with
## a bear, a china shop to smash, and so on. Every map node references a LevelDef, and
## the map/flow code never hard-codes a scene: adding a level is a new LevelDef (plus its
## scene) handed out by the map generator in run_state.gd.
@export var id: StringName = &""
@export var display_name: String = "Level"
## The shell scene loaded when the player enters a node with this level — the reusable
## frame (player, camera, HUD, PS1 filter). Shared by every level; the level's own
## geometry rides in `level_scene`, which the shell instances at runtime.
@export_file("*.tscn") var scene_path: String = ""
## The level module instanced into the shell's LevelRoot: spawners, lighting, meshes,
## colliders and any level-specific subscripts. Swapped wholesale between levels so no
## previous level's geometry (or its colliders) can leak into the next.
@export var level_scene: PackedScene = null
## Placeholder pip tint on the map until per-level icon art exists.
@export var color: Color = Color(0.80, 0.62, 0.24)
## A boss level caps a run — its node is drawn larger and clearing it ends the map.
@export var is_boss: bool = false
## The enemy this level pits the bull against. Null → the usual matador wave (the
## spawner's default). Set → a single boss enemy of this scene is spawned instead (e.g.
## the bear), and the fight is won when it falls. The arena scene is shared either way;
## only the opponent changes.
@export var enemy_scene: PackedScene = null
+1
View File
@@ -0,0 +1 @@
uid://c6sef0tfl26sr
+13
View File
@@ -0,0 +1,13 @@
class_name MapNode
extends RefCounted
## One node on the run map. Regenerated every run and never persisted, so RefCounted
## rather than Resource. `pos` is normalized (0-1) so the map screen can lay the node out
## at any resolution; `links` are column indices in the *next* row this node connects to
## (the Slay-the-Spire branching path).
var row: int = 0
var col: int = 0
var pos: Vector2 = Vector2.ZERO
var level: LevelDef = null
var links: PackedInt32Array = PackedInt32Array()
var cleared: bool = false
+1
View File
@@ -0,0 +1 @@
uid://emq8i2bmkekn
+280
View File
@@ -0,0 +1,280 @@
extends Control
## Slay-the-Spire-style run map shown between fights. Placeholder art — coloured pips
## joined by paths — but fully interactive: the player picks one of the reachable nodes
## and is sent into that level. Every node is the arena for now (see run_state.gd); the
## screen is level-agnostic, so richer levels just need a LevelDef with its own colour.
const _GOLD := Color(1.00, 0.78, 0.22)
const _CREAM := Color(0.93, 0.88, 0.72)
const _EDGE_DIM := Color(0.45, 0.36, 0.18, 0.55)
const _EDGE_ACTIVE := Color(1.00, 0.82, 0.32, 0.95)
const _NODE_SIZE := 58.0
const _BOSS_SIZE := 88.0
const _MENU_SCENE := "res://MainMenu.tscn"
var _map_area: Control
var _completion: PanelContainer
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
get_tree().paused = false
Run.ensure_run()
_build_ui()
_relayout.call_deferred() # defer so _map_area has its real size first
func _build_ui() -> void:
set_anchors_preset(Control.PRESET_FULL_RECT)
var bg := ColorRect.new()
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.color = Color(0.09, 0.06, 0.03) # opaque leather, no scene showing through
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(bg)
var title := Label.new()
title.text = "Choose Your Path"
title.set_anchors_preset(Control.PRESET_TOP_WIDE)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title.offset_top = 22.0
title.add_theme_font_override("font", UiFonts.title())
title.add_theme_font_size_override("font_size", 46)
title.add_theme_color_override("font_color", _GOLD)
title.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9))
title.add_theme_constant_override("outline_size", 6)
title.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(title)
_map_area = Control.new()
_map_area.set_anchors_preset(Control.PRESET_FULL_RECT)
_map_area.offset_top = 108.0
_map_area.offset_bottom = -92.0
_map_area.offset_left = 48.0
_map_area.offset_right = -48.0
_map_area.clip_contents = false
add_child(_map_area)
_map_area.resized.connect(_relayout)
var menu_btn := _make_button("Main Menu")
menu_btn.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
menu_btn.grow_vertical = Control.GROW_DIRECTION_BEGIN
menu_btn.offset_left = 32.0
menu_btn.offset_bottom = -26.0
menu_btn.pressed.connect(_to_menu)
add_child(menu_btn)
_build_completion()
# ── Layout ──────────────────────────────────────────────────────────────────
# Rebuilt from the run state on first show and whenever the map area resizes. Edges are
# added first so the node pips draw on top of the paths.
func _relayout() -> void:
if _map_area == null:
return
for child in _map_area.get_children():
child.queue_free()
var size := _map_area.size
if size.x <= 0.0 or size.y <= 0.0:
return
var reachable := Run.reachable()
var reach_set := {}
for n: MapNode in reachable:
reach_set[n] = true
var here := Run.current_node()
if Run.player_row < 0:
var start_pos := Vector2(size.x * 0.5, size.y - 8.0)
_add_start_pip(start_pos)
for n: MapNode in reachable:
_add_edge(start_pos, _local(n, size), true)
for row: Array in Run.map:
for from_node: MapNode in row:
for to_col: int in from_node.links:
var to_node := Run.node_at(from_node.row + 1, to_col)
if to_node == null:
continue
var active := from_node == here and reach_set.has(to_node)
_add_edge(_local(from_node, size), _local(to_node, size), active)
for row: Array in Run.map:
for node: MapNode in row:
_add_node(node, size, reach_set.has(node))
_completion.visible = Run.run_complete() or reachable.is_empty()
func _local(node: MapNode, size: Vector2) -> Vector2:
return Vector2(node.pos.x * size.x, node.pos.y * size.y)
func _add_edge(a: Vector2, b: Vector2, active: bool) -> void:
var line := Line2D.new()
line.add_point(a)
line.add_point(b)
line.width = 4.0 if active else 2.5
line.default_color = _EDGE_ACTIVE if active else _EDGE_DIM
line.antialiased = true
_map_area.add_child(line)
func _add_start_pip(center: Vector2) -> void:
var pip := PanelContainer.new()
var sz := 22.0
pip.custom_minimum_size = Vector2(sz, sz)
pip.size = Vector2(sz, sz)
pip.position = center - Vector2(sz, sz) * 0.5
pip.mouse_filter = Control.MOUSE_FILTER_IGNORE
pip.add_theme_stylebox_override("panel", UiTheme.disc(_GOLD, _CREAM, 2))
_map_area.add_child(pip)
func _add_node(node: MapNode, size: Vector2, is_reachable: bool) -> void:
var is_current := node.row == Run.player_row and node.col == Run.player_col
var sz := _BOSS_SIZE if node.level.is_boss else _NODE_SIZE
var center := _local(node, size)
var btn := Button.new()
btn.custom_minimum_size = Vector2(sz, sz)
btn.size = Vector2(sz, sz)
btn.position = center - Vector2(sz, sz) * 0.5
btn.focus_mode = Control.FOCUS_NONE
btn.disabled = not is_reachable
btn.mouse_filter = Control.MOUSE_FILTER_STOP if is_reachable else Control.MOUSE_FILTER_IGNORE
var fill := node.level.color
var border := UiTheme.BORDER
if is_current:
border = _CREAM
elif is_reachable:
border = _GOLD
elif node.cleared:
fill = fill.darkened(0.35)
border = Color(_GOLD.r, _GOLD.g, _GOLD.b, 0.5)
else: # locked — a future row not yet reachable
fill = fill.darkened(0.6)
border = Color(0.40, 0.34, 0.20, 0.6)
var border_w := 4 if (is_reachable or is_current) else 2
var normal := UiTheme.disc(fill, border, border_w)
var hover := UiTheme.disc(fill.lightened(0.18), _CREAM, border_w)
btn.add_theme_stylebox_override("normal", normal)
btn.add_theme_stylebox_override("hover", hover)
btn.add_theme_stylebox_override("pressed", hover)
btn.add_theme_stylebox_override("disabled", normal)
_map_area.add_child(btn)
if is_reachable:
btn.pressed.connect(_enter_node.bind(node))
_pulse(btn)
if is_reachable or is_current or node.level.is_boss:
_add_caption(node.level.display_name, center + Vector2(0.0, sz * 0.5 + 6.0), is_reachable)
if is_current:
_add_caption("You are here", center - Vector2(0.0, sz * 0.5 + 18.0), false)
func _add_caption(text: String, center_top: Vector2, bright: bool) -> void:
var lbl := Label.new()
lbl.text = text
lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lbl.size = Vector2(200.0, 0.0)
lbl.position = center_top - Vector2(100.0, 0.0)
lbl.add_theme_font_override("font", UiFonts.body())
lbl.add_theme_font_size_override("font_size", 15)
lbl.add_theme_color_override("font_color", _CREAM if bright else UiTheme.MUTED)
lbl.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9))
lbl.add_theme_constant_override("outline_size", 4)
lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
_map_area.add_child(lbl)
# Slow glow on the reachable pips so the eye lands on where the player can go next.
func _pulse(node: CanvasItem) -> void:
var tw := create_tween().set_loops()
tw.tween_property(node, "modulate:a", 0.6, 0.75).set_trans(Tween.TRANS_SINE)
tw.tween_property(node, "modulate:a", 1.0, 0.75).set_trans(Tween.TRANS_SINE)
# ── Actions ───────────────────────────────────────────────────────────────────
func _enter_node(node: MapNode) -> void:
Run.select_node(node)
get_tree().paused = false
get_tree().change_scene_to_file(node.level.scene_path)
func _to_menu() -> void:
get_tree().paused = false
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
get_tree().change_scene_to_file(_MENU_SCENE)
# ── Run-complete overlay ──────────────────────────────────────────────────────
func _build_completion() -> void:
_completion = PanelContainer.new()
_completion.set_anchors_preset(Control.PRESET_CENTER)
_completion.grow_horizontal = Control.GROW_DIRECTION_BOTH
_completion.grow_vertical = Control.GROW_DIRECTION_BOTH
_completion.add_theme_stylebox_override(
"panel", UiTheme.plaque(UiTheme.LEATHER, _GOLD, 4, 32.0, 24.0)
)
_completion.visible = false
add_child(_completion)
var col := VBoxContainer.new()
col.add_theme_constant_override("separation", 16)
col.alignment = BoxContainer.ALIGNMENT_CENTER
_completion.add_child(col)
var heading := Label.new()
heading.text = "Run Complete!"
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
heading.add_theme_font_override("font", UiFonts.title())
heading.add_theme_font_size_override("font_size", 40)
heading.add_theme_color_override("font_color", _GOLD)
heading.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.9))
heading.add_theme_constant_override("outline_size", 5)
col.add_child(heading)
var row := HBoxContainer.new()
row.alignment = BoxContainer.ALIGNMENT_CENTER
row.add_theme_constant_override("separation", 16)
col.add_child(row)
var again := _make_button("New Run")
again.pressed.connect(_new_run)
row.add_child(again)
var menu := _make_button("Main Menu")
menu.pressed.connect(_to_menu)
row.add_child(menu)
func _new_run() -> void:
Run.start_new_run()
_relayout()
# Shared thematic button, matching the HUD / menu plaques.
func _make_button(text: String) -> Button:
var btn := Button.new()
btn.text = text
btn.focus_mode = Control.FOCUS_NONE
btn.add_theme_stylebox_override(
"normal", UiTheme.plaque(Color(0.10, 0.07, 0.03, 0.90), _GOLD, 3, 18.0, 9.0)
)
var hover := UiTheme.plaque(Color(0.16, 0.10, 0.04, 0.96), _CREAM, 3, 18.0, 9.0)
btn.add_theme_stylebox_override("hover", hover)
btn.add_theme_stylebox_override("pressed", hover)
btn.add_theme_font_override("font", UiFonts.body())
btn.add_theme_color_override("font_color", _GOLD)
btn.add_theme_color_override("font_hover_color", _CREAM)
btn.add_theme_font_size_override("font_size", 18)
return btn
+1
View File
@@ -0,0 +1 @@
uid://dt3ffakbwlkmc
+76
View File
@@ -0,0 +1,76 @@
extends Node3D
signal matador_killed
signal all_defeated
const MATADOR := preload("res://Matador.tscn")
var _alive: int = 0
var _wave: int = 0
var _resolved: bool = false
var _spawn_points: Array[Node3D] = []
func _ready() -> void:
add_to_group(&"matador_spawn")
_spawn.call_deferred()
# Each spawner node is a spawn-point marker. The first in tree order (the one the HUD
# connects its all_defeated to) runs the wave logic and uses every marker — itself and
# its siblings — as an entry point; the rest stay passive so waves aren't run four times.
func _spawn() -> void:
var markers := get_tree().get_nodes_in_group(&"matador_spawn")
if markers.is_empty() or markers[0] != self:
return
for node: Node in markers:
if node is Node3D:
_spawn_points.append(node as Node3D)
_place_bull(randf() * TAU, DP.f("arena_spawn_radius"))
_start_next_wave()
# Wave 1 sends 1 matador, wave 2 sends 2, … up to the number of placed spawn points.
# Matadors are assigned to a random subset of spawn points each wave and enter staggered.
func _start_next_wave() -> void:
_wave += 1
var count := mini(_wave, _spawn_points.size())
var points: Array = _spawn_points.duplicate()
points.shuffle()
for i: int in count:
var sp := points[i] as Node3D
var m := _spawn_at(sp.global_position)
m.call_deferred(&"enter_spawn_roll", Vector3.ZERO)
if i < count - 1:
await get_tree().create_timer(DP.f("mat_spawn_stagger")).timeout
func _place_bull(angle: float, arena_r: float) -> void:
var bull := get_tree().get_first_node_in_group(&"player") as Node3D
if not bull:
return
bull.global_position = Vector3(
cos(angle) * arena_r, bull.global_position.y, sin(angle) * arena_r
)
func _spawn_at(pos: Vector3) -> Node3D:
var m: Node3D = MATADOR.instantiate()
m.position = pos
get_parent().add_child(m)
m.killed.connect(_on_matador_killed)
_alive += 1
return m
func _on_matador_killed() -> void:
matador_killed.emit()
_alive -= 1
if _alive > 0 or _resolved:
return
if _wave >= _spawn_points.size():
_resolved = true
all_defeated.emit()
else:
await get_tree().create_timer(DP.f("mat_wave_pause")).timeout
_start_next_wave()
+1
View File
@@ -0,0 +1 @@
uid://dou5ar6tkmng1
+246
View File
@@ -0,0 +1,246 @@
extends Node
## Persistent run progression across scene changes — the Slay-the-Spire-style map and
## where the player sits on it. The arena scene is reloaded for every fight, so this
## state must outlive scene changes: genuinely global run state earns an autoload (`Run`).
##
## Flow: menu Play → start_new_run() → arena (intro fight) → win → MapScreen shows the
## first frontier → pick a node → its level scene → win → back to MapScreen advanced one
## row, and on until a boss node ends the map. Every node is the arena for now; adding a
## level later is a new LevelDef in the catalog and pointing the generator at it.
signal run_started
# Every level loads the same reusable shell (player, camera, HUD, PS1 filter); the shell
# then instances the level's own `level_scene` module into its LevelRoot.
const _SHELL_SCENE := "res://scene.tscn"
const _ARENA_LEVEL := preload("res://levels/arena_level.tscn")
const _BEAR_LEVEL := preload("res://levels/bear_level.tscn")
# Map shape. Small + placeholder for now; the final row is a single boss node.
const _ROWS := 5
const _MIN_PER_ROW := 2
const _MAX_PER_ROW := 4
## map[row] is an Array[MapNode]; empty until a run starts.
var map: Array = []
## Node the player currently occupies. (-1, -1) = the start gate below the first row —
## i.e. straight after the intro fight, before any map node has been chosen.
var player_row: int = -1
var player_col: int = -1
## Node picked on the map whose level is being played now; committed to on the next win.
var pending_row: int = -1
var pending_col: int = -1
## Debug override — when set, active_level() returns this regardless of map state.
## Set via the console `level` command; cleared on start_new_run().
var debug_level_override: LevelDef = null
# Level catalog. Every level shares the shell scene; what changes is the level module
# (its geometry + spawners) and the opponent. The arena (matador wave, normal + boss
# flavours) and the bear (a lone boss enemy that can turn up as a normal node) live here.
# Future levels — china shop — slot in the same way: a new module + a LevelDef entry.
const _BEAR_SCENE := preload("res://Bear.tscn")
var _arena: LevelDef
var _arena_boss: LevelDef
var _bear: LevelDef
func _ready() -> void:
_arena = _make_level(&"arena", "The Arena", _ARENA_LEVEL, Color(0.80, 0.62, 0.24), false)
_arena_boss = _make_level(
&"arena_boss", "Grand Arena", _ARENA_LEVEL, Color(0.88, 0.30, 0.20), true
)
_bear = _make_level(&"bear", "Bear's Den", _BEAR_LEVEL, Color(0.55, 0.38, 0.22), false)
_bear.enemy_scene = _BEAR_SCENE
func _make_level(
id: StringName, level_name: String, level_scene: PackedScene, color: Color, boss: bool
) -> LevelDef:
var l := LevelDef.new()
l.id = id
l.display_name = level_name
l.scene_path = _SHELL_SCENE
l.level_scene = level_scene
l.color = color
l.is_boss = boss
return l
## The level being fought right now: debug override wins, then the pending/current map
## node, falling back to the plain arena for the intro fight or a direct scene launch.
func active_level() -> LevelDef:
if debug_level_override != null:
return debug_level_override
var n := node_at(pending_row, pending_col)
if n == null:
n = current_node()
return n.level if n != null and n.level != null else _arena
## Return a level by id, or null if unknown. Used by the debug console `level` command.
func get_level(id: StringName) -> LevelDef:
match id:
&"arena": return _arena
&"arena_boss": return _arena_boss
&"bear": return _bear
return null
## All known level ids — shown by `level` in the console.
func level_ids() -> Array[StringName]:
return [&"arena", &"arena_boss", &"bear"]
## Begin a fresh run: build a new map and place the player at the start gate. Called from
## the main-menu Play button, not from the arena scene (which reloads for every fight).
func start_new_run() -> void:
_generate_map()
player_row = -1
player_col = -1
pending_row = -1
pending_col = -1
debug_level_override = null
run_started.emit()
## Build a run if none exists — a safety net for entering the map or arena directly (e.g.
## running scene.tscn straight from the editor) without coming through the menu.
func ensure_run() -> void:
if map.is_empty():
start_new_run()
func has_run() -> bool:
return not map.is_empty()
func node_at(row: int, col: int) -> MapNode:
if row < 0 or row >= map.size():
return null
var r: Array = map[row]
if col < 0 or col >= r.size():
return null
return r[col]
func current_node() -> MapNode:
return node_at(player_row, player_col)
## Nodes the player may pick next: the whole first row from the start gate, otherwise the
## nodes the current node links forward to.
func reachable() -> Array:
if map.is_empty():
return []
if player_row < 0:
return (map[0] as Array).duplicate()
var here := current_node()
if here == null or player_row + 1 >= map.size():
return []
var out: Array = []
for c: int in here.links:
var n := node_at(player_row + 1, c)
if n != null:
out.append(n)
return out
## True once the player has cleared a boss node — the map is finished.
func run_complete() -> bool:
var here := current_node()
return here != null and here.level != null and here.level.is_boss
## Mark a node as the one being played (its level scene is about to load).
func select_node(node: MapNode) -> void:
if node == null:
return
pending_row = node.row
pending_col = node.col
## Commit the level just won: move the player onto the pending node (or leave them at the
## start gate after the intro fight, which has no pending node).
func complete_current_level() -> void:
if pending_row >= 0:
player_row = pending_row
player_col = pending_col
var n := current_node()
if n != null:
n.cleared = true
pending_row = -1
pending_col = -1
# ── Map generation ────────────────────────────────────────────────────────────
# A small branching lattice: each row gets a handful of nodes, each node links to the
# nearest node(s) in the next row, and every next-row node is guaranteed a parent so the
# path is always connected (no orphan the player can never reach). The final row is a
# single boss node the whole map funnels toward.
func _generate_map() -> void:
map.clear()
for r: int in _ROWS:
var count := 1 if r == _ROWS - 1 else randi_range(_MIN_PER_ROW, _MAX_PER_ROW)
var y := 1.0 - float(r + 1) / float(_ROWS + 1) # row 0 near the bottom, boss on top
var row_nodes: Array = []
for c: int in count:
var node := MapNode.new()
node.row = r
node.col = c
node.pos = Vector2(_column_x(c, count), y)
# Boss row is always the Grand Arena; normal nodes are a mix of matador
# arenas and the occasional Bear's Den for variety.
if r == _ROWS - 1:
node.level = _arena_boss
else:
node.level = _bear if randf() < 0.35 else _arena
row_nodes.append(node)
map.append(row_nodes)
_link_rows()
func _column_x(col: int, count: int) -> float:
if count <= 1:
return 0.5
return lerpf(0.18, 0.82, float(col) / float(count - 1))
func _link_rows() -> void:
for r: int in _ROWS - 1:
var cur: Array = map[r]
var nxt: Array = map[r + 1]
var incoming := PackedInt32Array()
incoming.resize(nxt.size()) # zero-filled
for from_node: MapNode in cur:
var nearest := _nearest_index(from_node, nxt)
_add_link(from_node, nearest, incoming)
# Occasionally fan out to an adjacent next-row node so paths branch.
if nxt.size() > 1 and randf() < 0.45:
var step := 1 if randf() < 0.5 else -1
_add_link(from_node, clampi(nearest + step, 0, nxt.size() - 1), incoming)
# Guarantee every next-row node has at least one parent (no orphans).
for c: int in nxt.size():
if incoming[c] == 0:
_add_link(cur[_nearest_index(nxt[c], cur)], c, incoming)
func _add_link(from_node: MapNode, to_col: int, incoming: PackedInt32Array) -> void:
if to_col in from_node.links:
return
from_node.links.append(to_col)
incoming[to_col] += 1
# Index of the node in `others` whose column position is closest to `node` on the x axis —
# used both to link a node forward and to adopt an orphan back to its nearest parent.
func _nearest_index(node: MapNode, others: Array) -> int:
var best := 0
var best_d := INF
for i: int in others.size():
var d: float = absf((others[i] as MapNode).pos.x - node.pos.x)
if d < best_d:
best_d = d
best = i
return best
+1
View File
@@ -0,0 +1 @@
uid://u4fartm52bk3