This commit is contained in:
bee
2026-07-13 07:38:54 +02:00
parent 90bae5f3e5
commit 9a25448a38
14 changed files with 169 additions and 7 deletions
+33
View File
@@ -0,0 +1,33 @@
[gd_scene format=3 uid="uid://bjsiquu3pu2yi"]
[ext_resource type="Script" uid="uid://b8105tebdqgu7" path="res://scripts/hub.gd" id="1_l10or"]
[node name="Hub" type="Control" unique_id=1025413344]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_l10or")
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=1963957334]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer" unique_id=1923587537]
layout_mode = 2
[node name="Dock" type="Label" parent="CenterContainer/VBoxContainer" unique_id=196076069]
layout_mode = 2
text = "Dock"
[node name="StartMissionButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1251733940]
layout_mode = 2
text = "Start Mission"
[connection signal="pressed" from="CenterContainer/VBoxContainer/StartMissionButton" to="." method="_on_start_mission_button_pressed"]
+3 -1
View File
@@ -1,5 +1,6 @@
[gd_scene format=3 uid="uid://jven27ktracb"]
[ext_resource type="Script" uid="uid://ck3d6hhcoia15" path="res://scripts/mission.gd" id="1_272bh"]
[ext_resource type="PackedScene" uid="uid://c6n3dg530t8oc" path="res://ocean.tscn" id="1_lquwl"]
[ext_resource type="PackedScene" uid="uid://cieiwihia8t2w" path="res://boat.tscn" id="2_7mycd"]
[ext_resource type="Script" uid="uid://46jiwugeccqg" path="res://scripts/camera_rig.gd" id="4_1bvp3"]
@@ -38,7 +39,8 @@ material = SubResource("StandardMaterial3D_j2yfa")
radius = 0.3
height = 0.6
[node name="Main" type="Node3D" unique_id=1242452333]
[node name="Main" type="Node3D" unique_id=1242452333 groups=["mission"]]
script = ExtResource("1_272bh")
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=431656791]
environment = SubResource("Environment_1")
+2 -3
View File
@@ -9,12 +9,11 @@
material_override = ExtResource("1_j38dg")
script = ExtResource("2_nb2vx")
sea_height = 1.0
sea_choppy = 8.0
metadata/follow_step = 1.0
[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=1520290282 groups=["water"]]
[node name="Surface" type="StaticBody3D" parent="." unique_id=1520290282 groups=["water"]]
collision_layer = 32
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=629201170]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Surface" unique_id=629201170]
shape = SubResource("WorldBoundaryShape3D_r1f0u")
+7 -1
View File
@@ -11,12 +11,13 @@ config_version=5
[application]
config/name="boatgame"
run/main_scene="res://main.tscn"
run/main_scene="uid://bjsiquu3pu2yi"
config/features=PackedStringArray("4.7", "Forward Plus")
[autoload]
Game="*uid://dtkqmj8h5mg8y"
GameState="*uid://xpfddtyfru40"
[display]
@@ -70,6 +71,11 @@ debug_possess_next={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194334,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
debug_end_mission={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194335,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
[layer_names]
+65
View File
@@ -0,0 +1,65 @@
class_name AIController
extends Node
@export var standoff_distance := 30.0 # throttle down inside this
@export var fire_range := 120.0 # open fire inside this
@export var full_rudder_angle_deg := 20.0 # bearing error at which rudder saturates
var _boat: Boat
var _target: Boat # node reference — shift-safe; never store a position
func _ready() -> void:
_boat = get_parent() as Boat
func _physics_process(_delta: float) -> void:
if _boat == null:
return
if _player_has_boat():
return # player possessed us — write nothing, resume when they leave
if not is_instance_valid(_target):
_target = _pick_target()
if _target == null:
_boat.desired_throttle = 0.0
_boat.steer_input = 0.0
_boat.aim_point = null # stow turrets, per the aim contract
_boat.fire_intents[0] = false
return
# All geometry computed fresh this tick — origin-shift safe.
var to_target: Vector3 = _target.global_position - _boat.global_position
to_target.y = 0.0
var distance := to_target.length()
var forward := -_boat.global_basis.z
forward.y = 0.0
var bearing_error := forward.signed_angle_to(to_target, Vector3.UP)
_boat.steer_input = clampf(
bearing_error / deg_to_rad(full_rudder_angle_deg), -1.0, 1.0)
_boat.desired_throttle = 1.0 if distance > standoff_distance else 0.2
_boat.aim_point = _target.global_position
_boat.fire_intents[0] = distance < fire_range
func _player_has_boat() -> bool:
var pc := get_tree().get_first_node_in_group(&"player_controllers") as PlayerController
return pc != null and pc.current_controlled == _boat
func _pick_target() -> Boat:
var best: Boat = null
var best_d := INF
for node in get_tree().get_nodes_in_group(&"boats"):
var other := node as Boat
if other == null or other == _boat:
continue
var d := _boat.global_position.distance_squared_to(other.global_position)
if d < best_d:
best_d = d
best = other
return best
+1
View File
@@ -0,0 +1 @@
uid://hvfw24jpikc0
+12 -2
View File
@@ -4,10 +4,12 @@ extends Node
const BOAT_SCENE := preload("res://boat.tscn")
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("restart"):
if event.is_action_pressed(&"restart"):
get_tree().reload_current_scene()
if event.is_action_pressed(&"debug_spawn_boat"):
if event.is_action_pressed(&"debug_spawn_boat") and _is_in_mission():
_debug_spawn_boat()
if event.is_action_pressed(&"debug_end_mission") and _is_in_mission():
GameState.return_to_hub({"outcome": "aborted"})
func _debug_spawn_boat() -> void:
var focus := get_tree().get_first_node_in_group(&"boats") as Node3D
@@ -17,4 +19,12 @@ func _debug_spawn_boat() -> void:
boat.position = focus.global_position + focus.global_basis.x * 10.0 + Vector3.UP * 2.0
else:
boat.position = Vector3(0, 3, 0)
var ai := AIController.new()
ai.name = "AIController"
boat.add_child(ai)
get_tree().current_scene.add_child(boat, true)
func _is_in_mission() -> bool:
var scene := get_tree().current_scene
return scene != null and scene.is_in_group(&"mission")
+22
View File
@@ -0,0 +1,22 @@
# scripts/game_state.gd — autoloaded as "GameState"
extends Node
const HUB_SCENE := "res://hub.tscn"
# Written by the hub before starting a mission, read by the mission in _ready().
# Later this becomes a MissionDef reference; a Dictionary is the v1 stand-in.
var mission_params: Dictionary = {}
# Written by the mission before returning, read by the hub.
var last_mission_result: Dictionary = {}
func start_mission(scene_path: String, params: Dictionary = {}) -> void:
mission_params = params
last_mission_result = {}
get_tree().change_scene_to_file(scene_path)
func return_to_hub(result: Dictionary = {}) -> void:
last_mission_result = result
mission_params = {}
get_tree().change_scene_to_file(HUB_SCENE)
+1
View File
@@ -0,0 +1 @@
uid://xpfddtyfru40
+9
View File
@@ -0,0 +1,9 @@
extends Control
func _ready() -> void:
if not GameState.last_mission_result.is_empty():
print("Mission result: ", GameState.last_mission_result)
func _on_start_mission_button_pressed() -> void:
GameState.start_mission("res://main.tscn", {"difficulty": 1})
+1
View File
@@ -0,0 +1 @@
uid://b8105tebdqgu7
+11
View File
@@ -0,0 +1,11 @@
extends Node3D
@onready var _ocean: Ocean = $Ocean
func _ready() -> void:
var params: Dictionary = GameState.mission_params
_ocean.sea_height = params.get("sea_height", _ocean.sea_height)
_ocean.sea_choppy = params.get("sea_choppy", _ocean.sea_choppy)
_ocean.sea_freq = params.get("sea_freq", _ocean.sea_freq)
_ocean.sea_speed = params.get("sea_speed", _ocean.sea_speed)
print("Mission started with params: ", params)
+1
View File
@@ -0,0 +1 @@
uid://ck3d6hhcoia15
+1
View File
@@ -43,6 +43,7 @@ var desired_throttle := 0.0
func _ready() -> void:
if current_controlled != null: # Only happens on intial loading if boat already moves
desired_throttle = current_controlled.throttle
add_to_group(&"player_controllers")
_possess()
func _physics_process(_delta: float) -> void: