29 Commits

Author SHA1 Message Date
4ddcbd8000 Connectable but buggy exported exe 2025-11-19 22:44:07 +01:00
8968586b0f Merge branch 'feature/menu-system' 2025-11-19 22:42:52 +01:00
8f641ab36e Fix error overflowing 2025-11-19 18:58:23 +01:00
9ae32ca6a9 Menu system WIP 2025-11-19 18:45:42 +01:00
ae18d1456a Merge branch 'feature/networked-star-system' 2025-11-19 17:01:39 +01:00
ab17242804 Jittering but synchronized physics state 2025-11-19 17:01:15 +01:00
a5dec9c2fd Fix networked input 2025-11-19 11:24:49 +01:00
e271c59837 Server generation of star system and authority for gravity 2025-11-18 21:58:14 +01:00
f8d140a9b0 Merge branch 'tech-test/3d-system-refactor' 2025-11-18 16:42:09 +01:00
67a4b7038a Cleanup character move 2025-11-18 16:29:38 +01:00
18f9a4fec7 Move character 2025-11-18 11:22:12 +01:00
4796a2d5ca Rename bad file 2025-11-18 11:06:46 +01:00
f8578bc3f2 Merge branch 'refactor/folder-structures' into tech-test/3d-system-refactor 2025-11-18 11:02:04 +01:00
a7583637e9 Remove tmp file 2025-11-18 11:00:20 +01:00
86762d0d50 Finished move 2025-11-18 11:00:14 +01:00
e2da700bcd WIP Renaming 2025-11-18 10:55:10 +01:00
466dff11d0 Added readme 2025-11-17 20:09:17 +01:00
636123344b Gd 4.6 compiled for large world vectors 2025-11-17 19:43:42 +01:00
25d9d55044 Welded ship wip physics architecture redesign 2025-11-17 19:43:11 +01:00
aafb939cbf Working cached gravity calculations 2025-11-17 08:32:48 +01:00
3d01edb2d9 WIP Gravitational refactor 2025-11-17 08:08:01 +01:00
398ec829ae Force based EVA movement 2025-11-16 19:07:24 +01:00
ec69ed2ee5 Force based zero g movement component 2025-11-15 22:38:24 +01:00
3647aa599d WIP OrbitalBody3D rework 2025-11-15 18:12:00 +01:00
1342ca2610 New visible celestial bodies 2025-11-14 10:58:45 +01:00
27ce796898 Physics mode on new ship 2025-11-07 16:27:06 +01:00
cff5ec27f8 Spawnable test ship with multiplayer and orbit 2025-11-07 12:04:56 +01:00
245be4a4f5 WIP 3d refactor COMPILING 2025-11-07 09:52:39 +01:00
6b9efda0d2 Merge branch 'tech-test/3d-controller' 2025-11-06 17:57:43 +01:00
232 changed files with 2960 additions and 2240 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
/android/
*.tmp
/export/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "godot_engine"]
path = godot_engine
url = https://codeberg.org/seedlingattempt/godot.git

View File

@ -2,7 +2,9 @@
## 1. Game Vision & Concept
Project Millimeters of Aluminum is a top-down 2D spaceship simulation game that emphasizes realistic orbital mechanics, deep ship management, and cooperative crew gameplay. Players take on roles as members of a multi-species crew aboard a modular, physically simulated spaceship.
Project Millimeters of Aluminum is a third-person 3D spaceship simulation game that emphasizes realistic physics, deep ship management, and cooperative crew gameplay. Players take on roles as members of a multi-species crew aboard a modular, physically simulated spaceship.
The game's aesthetic is inspired by the functional, industrial look of real-world space hardware and sci-fi like The Expanse, focusing on diegetic interfaces and detailed, functional components. The core experience is about planning and executing complex maneuvers in a hazardous, procedurally generated star system, where understanding the ship's systems is as important as piloting skill.
The game's aesthetic is inspired by the technical, gritty, and high-contrast 2D style of games like Barotrauma, focusing on diegetic interfaces and detailed, functional components. The core experience is about planning and executing complex maneuvers in a hazardous, procedurally generated star system, where understanding the ship's systems is as important as piloting skill.
@ -12,26 +14,80 @@ The gameplay is centered around a Plan -> Execute -> Manage loop:
1. Plan: The crew uses the Navigation Computer to analyze their orbit and plan complex maneuvers, such as a Hohmann transfer to another planet. They must account for launch windows, fuel costs, and travel time.
2. Execute: The crew engages the autopilot or manually pilots the ship. The Thruster Controller executes the planned burns, performing precise, fuel-optimal rotations and main engine thrusts to alter the ship's trajectory.
2. Execute: The crew engages the autopilot or manually pilots the ship. The Helm executes the planned burns, performing precise, fuel-optimal rotations and main engine thrusts to alter the ship's trajectory.
3. Manage: While underway, the crew manages the ship's modular systems, monitors resources like fuel and power, and responds to emergent events like hull breaches or system failures.
3. Manage: While underway, the crew moves about the ship's 3D interior, manages modular systems, monitors resources, and responds to emergent events like hull breaches or system failures.
## 3. Key Features
### 3. Key Features
### 1. Procedural Star System
The game world is a procedurally generated star system created by the StarSystemGenerator. Each system features a central star, a variable number of planets, moons, and asteroid belts, creating a unique environment for each playthrough.
### 2. N-Body Physics Simulation
Major bodies in orbit (CelestialBody class) are goveerened by a simplified n-body gravity simulation. Physical objects with player interaction (ships, crew characters, detached components, and eventually stations) are governed by a realistic N-body gravitational simulation, managed by the OrbitalMechanics library.
- Objects inherit from a base OrbitalBody2D class, ensuring consistent physics.
- This allows for complex and emergent orbital behaviors, such as tidal forces and stable elliptical orbits.
Major bodies in orbit (CelestialBody class) are governed by a 3D n-body gravity simulation, managed by the OrbitalMechanics library. Objects inherit from a base OrbitalBody3D class, ensuring consistent physics. The simulation allows for complex and emergent orbital behaviors.
### 3. Modular Spaceship
The player's ship is not a monolithic entity but a collection of distinct, physically simulated components attached to a root Module node.
The Module class extends OrbitalBody3D and aggregates mass and inertia from all child Component and StructuralPiece nodes.
Ship logic is decentralized into data-driven "databanks," such as the HelmLogicShard and AutopilotShard.
Hardware, like a Thruster, is a 3D Component that applies force to the root Module.
### 4. Advanced Navigation Computer
This is the primary crew interface for long-range travel, presented as a diegetic 2D screen (SensorPanel) within the 3D world.
Maneuver Planning: The computer can calculate various orbital transfers, each with strategic trade-offs:
Hohmann Transfer
Brachistochrone (Torchship) Trajectory
Tactical Map: A fully interactive UI map featuring:
Zoom-to-cursor and click-and-drag panning.
Predictive orbital path drawing.
Icon culling and detailed tooltips.
### 5. Physics-Based 3D Character Control
Character control is built on a robust, physics-based 3D system designed for complex zero-G environments.
Pawn/Controller Architecture: Player control is split between a PlayerController3D (which gathers hardware input and sends it via RPC) and a CharacterPawn3D (a CharacterBody3D that acts as the physics integrator).
Modular Movement: The pawn's movement logic is handled by component "brains." The ZeroGMovementComponent manages all zero-G interaction, while the EVAMovementComponent acts as a "dumb tool" providing thruster forces.
Physics-Based Gripping: Players can grab onto designated GripArea3D nodes. This is not an animation lock; a PD controller applies forces to the player's body to move them to the grip point and align them with its orientation.
Zero-G Traversal: The ZeroGMovementComponent features a state machine for IDLE (coasting), CLIMBING (moving between grips), REACHING (pending implementation), and CHARGING_LAUNCH (pushing off surfaces).
### 6. Runtime Component Design & Engineering
(This future-facing concept remains valid from the original design)
To move beyond pre-defined ship parts, the game will feature an in-game system for players to design, prototype, and manufacture their own components. This is achieved through a "Component Blueprint" architecture that separates a component's data definition from its physical form.
Component Blueprints: A ComponentBlueprint is a Resource file (.tres) that acts as a schematic.
Generic Template Scenes: The game will use a small number of generic, unconfigured "template" scenes (e.g., generic_thruster.tscn).
The Design Lab: Players will use a dedicated SystemStation to create and modify blueprints.
Networked Construction: A global ComponentFactory on the server will instantiate and configure components based on player-chosen blueprints, which are then replicated by the MultiplayerSpawner.
### 3. Modular Spaceship
The player's ship is not a monolithic entity but a collection of distinct, physically simulated components attached by joints. Key modules include:
- Spaceship: The main RigidBody2D hull that tracks overall mass, inertia, and health.
- Thruster: Self-contained RigidBody2D components that apply their own force. Their role (main engine, RCS, etc.) is an emergent property of their placement on the hull.
- Spaceship: The main RigidBody3D hull that tracks overall mass, inertia, and health.
- Thruster: Self-contained RigidBody3D components that apply their own force. Their role (main engine, RCS, etc.) is an emergent property of their placement on the hull.
- ThrusterController: The "brains" of the ship's movement, featuring a sophisticated autopilot that can execute fuel-optimal "bang-coast-bang" rotational maneuvers and a PD controller for stable attitude hold.
- FuelSystem & LifeSupport: Centralized managers for resources and internal ship environment. Hull breaches can create thrust vectors from escaping atmosphere.
- On-board Sensors: Diegetic components like Accelerometers allow the crew to calibrate ship performance by test-firing thrusters and measuring the true physical output.
@ -78,19 +134,17 @@ To move beyond pre-defined ship parts, the game will feature an in-game system f
3. A global `ComponentFactory` singleton on the server takes the blueprint, instantiates the correct generic template scene, and applies the blueprint's property overrides to the new instance.
4. This fully-configured node is then passed to the `MultiplayerSpawner`, which replicates the object across the network, ensuring all clients see the correctly customized component.
## 4. Technical Overview
- Architecture: The project uses a decoupled, modular architecture heavily reliant on a global SignalBus for inter-scene communication and a GameManager for global state. Ships feature their own local ShipSignalBus for internal component communication.
- Architecture: The project uses a decoupled, modular architecture. A GameManager handles global state, while ship systems are managed by ControlPanel and Databank resources loaded by a SystemStation.
- Key Scripts:
- OrbitalBody2D.gd: The base class for all physical objects.
- Spaceship.gd: The central hub for a player ship.
- Thruster.gd: A self-contained, physically simulated thruster component.
- ThrusterController.gd: Contains advanced autopilot and manual control logic (PD controller, bang-coast-bang maneuvers).
- NavigationComputer.gd: Manages the UI and high-level maneuver planning.
- MapDrawer.gd: A Control node that manages the interactive map UI.
- MapIcon.gd: The reusable UI component for map objects.
-OrbitalBody3D.gd: The base class for all physical objects.
- Module.gd: The central hub for a player ship, aggregating mass, inertia, and components.
- HelmLogicShard.gd / AutopilotShard.gd: Databanks that contain the advanced autopilot and manual control logic.
- SensorPanel.gd: A Control node that manages the interactive map UI.
- CharacterPawn3D.gd / ZeroGMovementComponent.gd: Manages all third-person 3D physics-based character movement.
- Art Style: Aims for a Barotraumainspired aesthetic using 2D ragdolls (Skeleton2D, PinJoint2D), detailed sprites with normal maps, and high-contrast dynamic lighting (PointLight2D, LightOccluder2D).
- Art Style: Aims for a functional, industrial 3D aesthetic. Character movement is physics-based using CharacterBody3D and Area3D grip detection. Ship interiors will be built from 3D modules and viewed from an over-the-shoulder camera.
## 5. Game Progression & Economy
This is the biggest area for potential expansion. A new section could detail how the player engages with the world and improves their situation over time.
@ -128,9 +182,10 @@ You mention "emergent events" in the gameplay loop. It would be beneficial to de
Since co-op and crew management are central, detailing this aspect is crucial.
### 1. Ship Interior Management:
- Diegetic Interfaces: You mention this in the vision. It's worth specifying how the crew will interact with systems. Will they need to be at a specific console (like the Navigation Computer) to use it? Do repairs require a character to physically be at the damaged module?
- Atmospherics & Life Support: How is the ship's interior environment simulated? Will fires or toxic gas leaks be a possibility? This ties directly into your LifeSupport system.
- Diegetic Interfaces: The crew will interact with systems from a third-person, over-the-shoulder perspective. They must be at a specific SystemStation to use its panels. Repairs will require a character to physically be at the damaged module.
- Atmospherics & Life Support: How is the ship's interior environment simulated? This will tie into the LifeSupport system.
### 2. Character States:
- Health & Injury: How are characters affected by hazards? Can they be injured in high-G maneuvers or from system failures?
- EVA (Extra-Vehicular Activity): Detail the mechanics for EVAs. What equipment is needed? How is movement handled in zero-G? This would be a perfect role for the "Hard Vacuum Monster" species.
- EVA (Extra-Vehicular Activity): This is a core feature. The EVAMovementComponent provides force-based thruster control for linear movement and roll torque. The ZeroGMovementComponent manages gripping, climbing, and launching from the ship's exterior and interior surfaces.
- Movement for the "Hard Vacuum Monster" species can be refined from a version of the reaching component where it can grab any nearby surface and can generate enough suction strength to remain attached to a moving object.

96
README.md Normal file
View File

@ -0,0 +1,96 @@
# Project Millimeters of Aluminum
A space simulation game built on a custom fork of the Godot Engine (4.x) with 64-bit double-precision physics enabled.
## 📋 Prerequisites
Before compiling the engine, ensure you have the following installed on your system.
### 1. Python & SCons
The Godot build system relies on SCons, which is written in Python.
* **Install Python (3.6+):** [Download Here](https://www.python.org/downloads/)
* *Windows Users:* Ensure "Add Python to PATH" is checked during installation. [Check this stackoverflow answer](https://stackoverflow.com/questions/57421669/question-about-pip-using-python-from-windows-store).
* **Install SCons:** Open your terminal/command prompt and run:
```bash
pip install scons
```
### 2. C++ Compiler
Godot requires a C++ compiler to build from source.
* **Windows:**
* Install **Visual Studio Community** (2019 or later).
* During installation, select the **"Desktop development with C++"** workload.
* **Linux:**
* Install GCC or Clang.
* *Debian/Ubuntu:* `sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev`
* **macOS:**
* Install **Xcode** from the App Store.
* Run `xcode-select --install` in the terminal.
---
## 🛠️ Setup & Compilation
This project uses a custom engine build to support solar-system scale coordinates (Double Precision). You **cannot** use the standard Steam or website version of Godot to open this project.
### 1. Clone the Repository
Clone the repository with the `--recursive` flag to automatically pull the engine source code submodule.
```bash
git clone --recursive [https://codeberg.org/YOUR_USERNAME/ProjectMillimetersOfAluminum.git](https://codeberg.org/YOUR_USERNAME/ProjectMillimetersOfAluminum.git)
cd ProjectMillimetersOfAluminum
```
_If you have already cloned without recursive, run:_
```
git submodule update --init --recursive
```
### 2. Configure Engine Ignore (Optional but Recommended)
To prevent your Editor from trying to import the thousands of raw assets inside the engine source folder:
1. Navigate to `godot_engine/`.
2. Create a new empty file named `.gdignore`.
3. Compile the Engine
Run the build command for your platform from the godot_engine/ directory.
**Windows:**
```Bash
cd godot_engine
scons platform=windows target=editor precision=double arch=x86_64
```
**Linux:**
```Bash
cd godot_engine
scons platform=linuxbsd target=editor precision=double arch=x86_64
```
**macOS:**
```Bash
cd godot_engine
scons platform=macos target=editor precision=double arch=x86_64
```
_Note: (-j6 flag tells the compiler to use 6 CPU cores. Adjust this number based on your hardware to speed up compilation. Not using the flag will use all available cores)_
---
## 🚀 Running the Project
Once compilation is complete (usually 10-30 minutes), the executable will be located in godot_engine/bin/.
Do not open the project with standard Godot.
Navigate to `godot_engine/bin/`.
Run the binary ending in `.double.x86_64` (e.g., `godot.windows.editor.double.x86_64.exe`).
Import and open the `project.godot` file located in the root `ProjectMillimetersOfAluminum` folder.
### Troubleshooting
"No valid compilers found" (Windows): Ensure you installed the C++ Desktop Development workload in the Visual Studio Installer. Just the editor is not enough.
Jittering Objects: If objects jitter at large distances, ensure you are running the double precision binary and not a standard build.

View File

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://bm1rbv4tuppbc"]
[ext_resource type="Script" uid="uid://d4jka2etva22s" path="res://scenes/tests/3d/eva_movement_component.gd" id="1_mb22m"]
[node name="EVASuitController" type="Node3D"]
script = ExtResource("1_mb22m")

View File

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://dogqi2c58qdc0"]
[ext_resource type="Script" uid="uid://bkcouefvi7iup" path="res://scripts/star_system.gd" id="1_ig7tw"]
[node name="StarSystem" type="Node2D"]
script = ExtResource("1_ig7tw")

View File

@ -1,27 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://dogqi2c58qdc0"]
[ext_resource type="Script" uid="uid://j3j483itissq" path="res://scripts/star_system_generator.gd" id="1_h2yge"]
[ext_resource type="PackedScene" uid="uid://5uqp4amjj7ww" path="res://scenes/star.tscn" id="2_7mycd"]
[ext_resource type="PackedScene" uid="uid://clt4qlsjcfgln" path="res://scenes/planet.tscn" id="3_272bh"]
[ext_resource type="PackedScene" uid="uid://74ppvxcw8an4" path="res://scenes/moon.tscn" id="4_5vw27"]
[ext_resource type="PackedScene" uid="uid://dm3s33o4xhqfv" path="res://scenes/station.tscn" id="5_kek77"]
[ext_resource type="PackedScene" uid="uid://bawsujtlpmh5r" path="res://scenes/asteroid.tscn" id="6_4c57u"]
[ext_resource type="PackedScene" uid="uid://cm5qsuunboxm3" path="res://scenes/developer_pawn.tscn" id="7_272bh"]
[ext_resource type="PackedScene" uid="uid://ctlw5diis8h1x" path="res://scenes/map_canvas.tscn" id="8_5vw27"]
[node name="Node2D" type="Node2D"]
script = ExtResource("1_h2yge")
min_asteroid_belts = 0
star_scene = ExtResource("2_7mycd")
planet_scene = ExtResource("3_272bh")
moon_scene = ExtResource("4_5vw27")
station_scene = ExtResource("5_kek77")
asteroid_scene = ExtResource("6_4c57u")
sim_scale = 1e+09
[node name="DeveloperPawn" parent="." node_paths=PackedStringArray("map_canvas") instance=ExtResource("7_272bh")]
input_pickable = true
map_canvas = NodePath("../MapCanvas")
[node name="MapCanvas" parent="." node_paths=PackedStringArray("star_system_generator") instance=ExtResource("8_5vw27")]
star_system_generator = NodePath("..")

View File

@ -1,31 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://b1kpyek60vyof"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_1abiy"]
[ext_resource type="PackedScene" uid="uid://bho8x10x4oab7" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_risxe"]
[node name="Module" type="RigidBody2D"]
position = Vector2(-50, 50)
mass = null
center_of_mass_mode = 1
center_of_mass = Vector2(-50, 0)
inertia = null
linear_velocity = null
angular_velocity = null
script = ExtResource("1_1abiy")
base_mass = null
inertia = null
[node name="StructuralContainer" type="Node2D" parent="."]
[node name="Hullplate" parent="StructuralContainer" instance=ExtResource("2_risxe")]
base_mass = null
inertia = null
[node name="@StaticBody2D@23989" parent="StructuralContainer" instance=ExtResource("2_risxe")]
position = Vector2(-100, 0)
base_mass = null
inertia = null
[node name="HullVolumeContainer" type="Node2D" parent="."]
[node name="AtmosphereVisualizer" type="Node2D" parent="."]

View File

@ -1,31 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://baeikwxkh26fh"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_1rae4"]
[ext_resource type="PackedScene" uid="uid://bho8x10x4oab7" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_fbnt1"]
[node name="Module" type="RigidBody2D"]
position = Vector2(-50, 50)
mass = null
center_of_mass_mode = 1
center_of_mass = Vector2(-50, 0)
inertia = null
linear_velocity = null
angular_velocity = null
script = ExtResource("1_1rae4")
base_mass = null
inertia = null
[node name="StructuralContainer" type="Node2D" parent="."]
[node name="Hullplate" parent="StructuralContainer" instance=ExtResource("2_fbnt1")]
base_mass = null
inertia = null
[node name="@StaticBody2D@23989" parent="StructuralContainer" instance=ExtResource("2_fbnt1")]
position = Vector2(-100, 0)
base_mass = null
inertia = null
[node name="HullVolumeContainer" type="Node2D" parent="."]
[node name="AtmosphereVisualizer" type="Node2D" parent="."]

View File

@ -1,145 +0,0 @@
[gd_scene load_steps=20 format=3 uid="uid://didt2nsdtbmra"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_nqe0s"]
[ext_resource type="PackedScene" uid="uid://bho8x10x4oab7" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_foqop"]
[ext_resource type="PackedScene" uid="uid://d3hitk62fice4" path="res://scenes/ship/builder/pieces/bulkhead.tscn" id="4_dmrms"]
[ext_resource type="PackedScene" uid="uid://2n42nstcj1n0" path="res://scenes/ship/components/hardware/system_station.tscn" id="5_nqe0s"]
[ext_resource type="Script" uid="uid://diu2tgusi3vmt" path="res://scenes/ship/computer/shards/sensor_databank.gd" id="9_ixntg"]
[ext_resource type="PackedScene" uid="uid://dt1t2n7dewucw" path="res://scenes/ship/computer/UI/button_panel.tscn" id="10_px2ne"]
[ext_resource type="Script" uid="uid://cfbyqvnvf3hna" path="res://scenes/ship/computer/shards/helm_logic_databank.gd" id="10_wkxbw"]
[ext_resource type="PackedScene" uid="uid://cdbqjkgsj02or" path="res://scenes/ship/computer/UI/readout_screen_panel.tscn" id="11_erhv3"]
[ext_resource type="Script" uid="uid://t12etsdx2h38" path="res://scenes/ship/computer/shards/nav_selection_databank.gd" id="11_xwy4s"]
[ext_resource type="Script" uid="uid://ceqdi6jobefnc" path="res://scenes/ship/computer/shards/helm_autopilot_databank.gd" id="12_4epkn"]
[ext_resource type="PackedScene" uid="uid://rd1c22nsru8y" path="res://scenes/ship/computer/UI/sensor_panel.tscn" id="12_q1rtr"]
[ext_resource type="PackedScene" uid="uid://c0bb77rmyatr0" path="res://scenes/ship/components/hardware/thruster.tscn" id="12_vmx8o"]
[ext_resource type="PackedScene" uid="uid://dvpy3urgtm62n" path="res://scenes/ship/components/hardware/spawner.tscn" id="13_83bu1"]
[ext_resource type="PackedScene" uid="uid://pq55j75t3fda" path="res://scenes/ship/computer/UI/throttle_lever_panel.tscn" id="13_rsa1x"]
[ext_resource type="Script" uid="uid://ctgl5kxyagw0f" path="res://scenes/ship/computer/shards/helm_ship_status.gd" id="13_wkxbw"]
[ext_resource type="Script" uid="uid://ghluwjd5c5ul" path="res://scenes/ship/computer/shards/nav_brachistochrone_planner.gd" id="14_xwy4s"]
[ext_resource type="Script" uid="uid://bghu5lhcbcfmh" path="res://scenes/ship/computer/shards/nav_hohman_planner.gd" id="15_fll2s"]
[ext_resource type="Script" uid="uid://dsbn7ushwqrko" path="res://scenes/ship/computer/shards/nav_intercept_solver.gd" id="16_vufgi"]
[ext_resource type="Script" uid="uid://0f6v6iu3o5qo" path="res://scenes/ship/computer/shards/nav_projection_shard.gd" id="17_34v0b"]
[node name="Module" type="Node2D"]
physics_interpolation_mode = 2
script = ExtResource("1_nqe0s")
physics_mode = 1
mass = 1.0
inertia = 0.0
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="Hullplate" parent="." instance=ExtResource("2_foqop")]
physics_interpolation_mode = 2
is_pressurized = false
base_mass = 0.0
[node name="@StaticBody2D@30634" parent="." instance=ExtResource("2_foqop")]
physics_interpolation_mode = 2
position = Vector2(0, 100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30635" parent="." instance=ExtResource("2_foqop")]
physics_interpolation_mode = 2
position = Vector2(0, -100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="Bulkhead" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(-50, 100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30636" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(-50, 0)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30637" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(-50, -100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30638" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(50, -100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30639" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(0, -150)
rotation = 1.5708
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30640" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(0, 150)
rotation = 4.71239
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30641" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(50, 100)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="@StaticBody2D@30642" parent="." instance=ExtResource("4_dmrms")]
physics_interpolation_mode = 2
position = Vector2(50, 0)
is_pressurized = false
health = 0.0
base_mass = 0.0
[node name="Station" parent="." instance=ExtResource("5_nqe0s")]
position = Vector2(0, -10)
panel_scenes = Array[PackedScene]([ExtResource("11_erhv3"), ExtResource("11_erhv3"), ExtResource("12_q1rtr"), ExtResource("10_px2ne"), ExtResource("13_rsa1x")])
databank_installations = Array[Script]([ExtResource("10_wkxbw"), ExtResource("12_4epkn"), ExtResource("13_wkxbw"), ExtResource("9_ixntg"), ExtResource("11_xwy4s"), ExtResource("14_xwy4s"), ExtResource("15_fll2s"), ExtResource("16_vufgi"), ExtResource("17_34v0b")])
physics_mode = 2
[node name="Thruster" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(-95, -130)
rotation = 1.5708
main_thruster = false
physics_mode = 2
[node name="Thruster2" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(-95, 130)
rotation = 1.5708
main_thruster = false
physics_mode = 2
[node name="Thruster3" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(95, 130)
rotation = -1.5708
main_thruster = false
physics_mode = 2
[node name="Thruster4" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(95, -130)
rotation = -1.5708
main_thruster = false
physics_mode = 2
[node name="MainEngine" parent="." instance=ExtResource("12_vmx8o")]
position = Vector2(0, 195)
max_thrust = 10.0
physics_mode = 2
[node name="Spawner" parent="." instance=ExtResource("13_83bu1")]
position = Vector2(0, 27)
physics_mode = 2

View File

@ -1,19 +0,0 @@
class_name Asteroid
extends OrbitalBody2D
# The orbital radius for this asteroid.
var orbital_radius: float
func get_class_name() -> String:
return "Asteroid"
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# An Asteroid has negligible mass for physics calculations.
#mass = 0.001
#radius = 5.0
# You can set a default texture here.
# texture = preload("res://assets/asteroid_texture.png")
super._ready()

View File

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

View File

@ -1,8 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://bawsujtlpmh5r"]
[ext_resource type="Script" uid="uid://0isnsk356que" path="res://scripts/orbital_body_2d.gd" id="1_4q05e"]
[node name="Asteroid" type="Node2D"]
script = ExtResource("1_4q05e")
base_mass = 50000.0
metadata/_custom_type_script = "uid://0isnsk356que"

View File

@ -1,17 +0,0 @@
class_name Moon
extends OrbitalBody2D
# The orbital radius for this moon.
var orbital_radius: float
func get_class_name() -> String:
return "Moon"
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# A Moon has a smaller mass than a planet.
# You can set a default texture here.
# texture = preload("res://assets/moon_texture.png")
super._ready()

View File

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

View File

@ -1,8 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://74ppvxcw8an4"]
[ext_resource type="Script" uid="uid://b1xsx7er22nxn" path="res://scenes/celestial_bodies/moon.gd" id="1_530pw"]
[node name="Moon" type="Node2D"]
script = ExtResource("1_530pw")
base_mass = 1e+06
metadata/_custom_type_script = "uid://0isnsk356que"

View File

@ -1,15 +0,0 @@
class_name Planet
extends OrbitalBody2D
# The orbital radius for this planet.
var orbital_radius: float
func get_class_name() -> String:
return "Planet"
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# You can set a default texture here.
# texture = preload("res://assets/planet_texture.png")
super._ready()

View File

@ -1 +0,0 @@
uid://5f6ipgu65urb

View File

@ -1,10 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://clt4qlsjcfgln"]
[ext_resource type="Script" uid="uid://5f6ipgu65urb" path="res://scenes/celestial_bodies/planet.gd" id="1_cktii"]
[node name="Planet" type="Node2D"]
script = ExtResource("1_cktii")
base_mass = 2.5e+07
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]

View File

@ -1,14 +0,0 @@
class_name Star
extends OrbitalBody2D
func get_class_name() -> String:
return "Star"
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# A Star has no primary and a very large mass.
# You can set a default texture here, or assign it in the Inspector.
# texture = preload("res://assets/star_texture.png")
super._ready()

View File

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

View File

@ -1,15 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://5uqp4amjj7ww"]
[ext_resource type="Script" uid="uid://um2sfghmii42" path="res://scenes/celestial_bodies/star.gd" id="1_mcqwg"]
[sub_resource type="CircleShape2D" id="CircleShape2D_508pf"]
radius = 200.0
[node name="Star" type="Node2D"]
script = ExtResource("1_mcqwg")
base_mass = 5e+08
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_508pf")
debug_color = Color(0.863865, 0.471779, 0.162305, 1)

View File

@ -1,17 +0,0 @@
class_name Station
extends OrbitalBody2D
# The orbital radius for this station.
var orbital_radius: float
func get_class_name() -> String:
return "Station"
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# A Station has negligible mass for physics calculations.
# You can set a default texture here.
# texture = preload("res://assets/station_texture.png")
super._ready()

View File

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

View File

@ -1,8 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://dm3s33o4xhqfv"]
[ext_resource type="Script" uid="uid://ulw61oxppwdu" path="res://scenes/celestial_bodies/station.gd" id="1_rod8h"]
[node name="Station" type="Node2D"]
script = ExtResource("1_rod8h")
base_mass = 5000.0
metadata/_custom_type_script = "uid://0isnsk356que"

View File

@ -1,205 +0,0 @@
extends CharacterBody2D
class_name PilotBall
# --- Movement Constants (Friction Simulation) ---
# When in open space (no module overlap), movement is zeroed out quickly.
const EXTERIOR_DRAG_FACTOR: float = 0.05
# When pushing off hullplates (low friction, slow acceleration)
const INTERIOR_SLUGGISH_SPEED: float = 100.0
const INTERIOR_SLUGGISH_ACCEL: float = 5 # Low acceleration, simulating mass and small push
# When gripping a ladder (high friction, direct control)
const LADDER_SPEED: float = 100.0
const LADDER_ACCEL: float = 20 # High acceleration, simulating direct grip
@onready var camera: Camera2D = $Camera2D
@onready var overlap_area: Area2D = $OverlapDetector
@onready var ui_container: Control = $CanvasLayer/UIContainer
var nearby_station: SystemStation = null
var current_station: SystemStation = null
# --- State Variables ---
enum MovementState {
NO_CONTROL,
ZERO_G_INTERIOR,
LADDER_GRIP,
IN_STATION
}
var current_state: MovementState = MovementState.NO_CONTROL
var ladder_area: Area2D = null # Area of the ladder currently overlapped
var is_grabbing_ladder: bool = false # True if 'Space' is held while on ladder
# --- Overlap Detection (Assuming you use Area2D for detection) ---
var overlapping_modules: int = 0
# --- Ladder Constants ---
const LAUNCH_VELOCITY: float = 300.0
var _movement_input: Vector2 = Vector2.ZERO
var _interact_just_pressed: bool = false
var _interact_held: bool = false
# --- PUBLIC INPUT METHODS (Called by the PlayerController) ---
func set_movement_input(input_dir: Vector2):
_movement_input = input_dir
func set_interaction_input(just_pressed: bool, is_held: bool):
_interact_just_pressed = just_pressed
_interact_held = is_held
# --- New: Physics Initialization (Assuming CharacterBody2D is parented to the scene root or Ship) ---
# NOTE: CharacterBody2D cannot inherit OrbitalBody2D, so we manage its velocity manually.
func _ready():
# Set up overlap signals if they aren't already connected in the scene file
# You must have an Area2D child on PilotBall to detect overlaps.
overlap_area.body_entered.connect(on_body_entered)
overlap_area.body_exited.connect(on_body_exited)
overlap_area.area_entered.connect(_on_station_area_entered)
overlap_area.area_exited.connect(_on_station_area_exited)
camera.make_current()
func on_body_entered(body: Node2D):
# Detect Modules (which all inherit OrbitalBody2D via StructuralPiece)
if body is StructuralPiece:
overlapping_modules += 1
# Detect Ladders
if body is Ladder:
ladder_area = body.find_child("ClimbArea") # Assuming the Ladder has a specific Area2D for climbing
func on_body_exited(body: Node2D):
if body is StructuralPiece:
overlapping_modules -= 1
if body is Ladder:
if body.find_child("ClimbArea") == ladder_area:
ladder_area = null
is_grabbing_ladder = false # Force detach if the ladder moves away
# --- NEW: Functions to be called by the Station ---
func enter_station_state():
current_state = MovementState.IN_STATION
velocity = Vector2.ZERO # FIX: Stop all movement when entering a station
func exit_station_state():
# When leaving, transition to a sensible default state.
current_state = MovementState.ZERO_G_INTERIOR
func _physics_process(delta):
# This script now runs on the server and its state is synced to clients.
# It no longer checks for local input authority.
if current_state == MovementState.IN_STATION:
move_and_slide()
return
_update_movement_state() # This function now uses the new variables
process_interaction() # Process any interaction presses
# Reset input flags for the next frame
_interact_just_pressed = false
_interact_held = false
# The 'input_dir' now comes from our variable, not the Input singleton.
var input_dir = _movement_input
match current_state:
MovementState.ZERO_G_INTERIOR:
_sluggish_movement(input_dir, delta)
MovementState.LADDER_GRIP:
_ladder_movement(input_dir, delta)
# Reset input for the next frame
_movement_input = Vector2.ZERO
move_and_slide()
# This function is called every physics frame by _physics_process().
func process_interaction():
# If the interact button was not pressed this frame, do nothing.
if not _interact_just_pressed:
return
# Priority 1: Disengage from a station if we are in one.
if current_station:
current_station.disengage(self)
current_station = null
return
# Priority 2: Occupy a nearby station if we are not in one.
elif is_instance_valid(nearby_station):
current_station = nearby_station
current_station.occupy(self)
return
# Priority 3: Handle ladder launch logic.
# This part of the old logic was in _handle_interaction_input,
# but it's cleaner to check for the release of the button here.
if current_state == MovementState.LADDER_GRIP and not _interact_held:
# Launch the player away from the ladder when the interact button is released.
var launch_direction = - _movement_input.normalized()
if launch_direction == Vector2.ZERO:
# Default launch: use the character's forward direction
launch_direction = Vector2.UP.rotated(rotation)
velocity = launch_direction * LAUNCH_VELOCITY
# Immediately switch to zero-G interior state
is_grabbing_ladder = false
current_state = MovementState.ZERO_G_INTERIOR
# --- State Machine Update ---
func _update_movement_state():
# Priority 1: Ladder Grip
# This now checks the variable instead of the Input singleton.
if ladder_area and _interact_held:
is_grabbing_ladder = true
current_state = MovementState.LADDER_GRIP
return
# Priority 2: Interior Zero-G (must overlap a module/piece AND not be grabbing)
if overlapping_modules > 0:
if is_grabbing_ladder:
# If we were grabbing a ladder but released 'interact', we transition to zero-G interior
is_grabbing_ladder = false
current_state = MovementState.ZERO_G_INTERIOR
return
current_state = MovementState.ZERO_G_INTERIOR
return
# Priority 3: No Control (floating free)
is_grabbing_ladder = false
current_state = MovementState.NO_CONTROL
# --- Movement Implementations ---
func _sluggish_movement(input_dir: Vector2, delta: float):
# Simulates pushing off the wall: slow acceleration, but minimal drag
var target_velocity = input_dir * INTERIOR_SLUGGISH_ACCEL
velocity = velocity + target_velocity * delta
#velocity.lerp(velocity + interi, INTERIOR_SLUGGISH_ACCEL)
func _ladder_movement(input_dir: Vector2, delta: float):
# Simulates direct grip: fast acceleration, perfect control
var target_velocity = input_dir * LADDER_SPEED
velocity = velocity.lerp(target_velocity, LADDER_ACCEL * delta)
# --- New Functions for Station Interaction ---
func _on_station_area_entered(area: Area2D):
if area.get_parent() is SystemStation:
nearby_station = area.get_parent()
print("Near station: ", nearby_station.name)
func _on_station_area_exited(area: Area2D):
if area.get_parent() == nearby_station:
nearby_station = null
# Stations will call this to get the node where they should place their UIs.
func get_ui_container() -> Control:
return ui_container

View File

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

View File

@ -1,36 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://chgycmkkaf7jv"]
[ext_resource type="Script" uid="uid://dxngvoommn5f1" path="res://scenes/characters/pilot_ball.gd" id="1_rhbna"]
[sub_resource type="CircleShape2D" id="CircleShape2D_6jclb"]
[sub_resource type="CircleShape2D" id="CircleShape2D_rhbna"]
[node name="PilotBall" type="CharacterBody2D"]
collision_layer = 32
collision_mask = 16
script = ExtResource("1_rhbna")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_6jclb")
debug_color = Color(0.61528, 0.358023, 1, 1)
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(4, 4)
[node name="OverlapDetector" type="Area2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="OverlapDetector"]
shape = SubResource("CircleShape2D_rhbna")
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="UIContainer" type="Control" parent="CanvasLayer"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

View File

@ -1,37 +0,0 @@
extends Node
class_name PlayerController
# TODO: Change this to custom pawn type
var possessed_pawn: Node # The character this controller is currently driving
func _ready():
# --- FIX: Manually enable input processing for this node ---
set_process_input(true)
func _physics_process (delta):
if not is_multiplayer_authority():
return
# 1. Gather all input states for this frame.
var input_dir = Input.get_vector("move_left", "move_right", "move_up", "move_down")
var is_interact_just_pressed = Input.is_action_just_pressed("interact")
var is_interact_held = Input.is_action_pressed("interact")
#print(is_interact_just_pressed)
#print(input_dir)
# 2. Send the collected input state to the server via RPC.
server_process_input.rpc_id(1, input_dir, is_interact_just_pressed, is_interact_held)
@rpc("any_peer", "call_local")
func server_process_input(input_dir: Vector2, is_interact_just_pressed: bool, is_interact_held: bool):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_movement_input(input_dir)
# Pass both interact states to the pawn
possessed_pawn.set_interaction_input(is_interact_just_pressed, is_interact_held)
func possess(pawn_to_control: Node):
possessed_pawn = pawn_to_control
reparent(pawn_to_control, false)
self.owner = pawn_to_control
print("PlayerController possessed: ", possessed_pawn.name)

View File

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

View File

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://dnre6svquwdtb"]
[ext_resource type="Script" uid="uid://dmhwqmbwk0t8k" path="res://scenes/characters/player_controller.gd" id="1_b8jga"]
[node name="PlayerController" type="Node"]
script = ExtResource("1_b8jga")

View File

@ -1,82 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://c77wxeb7gpplw"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_mtskc"]
[ext_resource type="PackedScene" uid="uid://bho8x10x4oab7" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_aovrk"]
[ext_resource type="PackedScene" uid="uid://d3hitk62fice4" path="res://scenes/ship/builder/pieces/bulkhead.tscn" id="4_dwgsg"]
[node name="Module" type="Node2D"]
position = Vector2(-50, 50)
script = ExtResource("1_mtskc")
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="StructuralContainer" type="Node2D" parent="."]
[node name="Hullplate" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
[node name="@StaticBody2D@31031" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(0, 100)
[node name="@StaticBody2D@31033" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(100, 100)
[node name="@StaticBody2D@31035" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(100, 0)
[node name="@StaticBody2D@31037" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(100, -100)
[node name="@StaticBody2D@31039" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(100, -200)
[node name="@StaticBody2D@31041" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(0, -200)
[node name="@StaticBody2D@31043" parent="StructuralContainer" instance=ExtResource("2_aovrk")]
position = Vector2(0, -100)
[node name="Bulkhead" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(-50, 100)
[node name="@StaticBody2D@31046" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(-50, 0)
[node name="@StaticBody2D@31048" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(-50, -100)
[node name="@StaticBody2D@31050" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(-50, -200)
[node name="@StaticBody2D@31052" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(150, -200)
[node name="@StaticBody2D@31054" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(150, -100)
[node name="@StaticBody2D@31056" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(150, 0)
[node name="@StaticBody2D@31058" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(150, 100)
[node name="@StaticBody2D@31060" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(0, 150)
rotation = 1.5708
[node name="@StaticBody2D@31062" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(100, 150)
rotation = 1.5708
[node name="@StaticBody2D@31064" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(0, -250)
rotation = 1.5708
[node name="@StaticBody2D@31066" parent="StructuralContainer" instance=ExtResource("4_dwgsg")]
position = Vector2(100, -250)
rotation = 1.5708
[node name="HullVolumeContainer" type="Node2D" parent="."]
[node name="AtmosphereVisualizer" type="Node2D" parent="."]
[node name="Camera2D" type="Camera2D" parent="."]
position = Vector2(50, -50)

View File

@ -1,13 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://cm0rohkr6khd1"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_b1h2b"]
[node name="Module" type="Node2D"]
script = ExtResource("1_b1h2b")
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="StructuralContainer" type="Node2D" parent="."]
[node name="HullVolumeContainer" type="Node2D" parent="."]
[node name="AtmosphereVisualizer" type="Node2D" parent="."]

View File

@ -1,6 +0,0 @@
@tool
class_name Bulkhead
extends StructuralPiece
# This piece represents a wall or edge.
# No additional logic is needed right now, we just need the class_name.

View File

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

View File

@ -1,29 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://d3hitk62fice4"]
[ext_resource type="Script" uid="uid://b4g288mje38nj" path="res://scenes/ship/builder/pieces/bulkhead.gd" id="1_1wp2n"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_1wp2n"]
size = Vector2(10, 100)
[node name="Bulkhead" type="StaticBody2D"]
collision_layer = 16
collision_mask = 60
script = ExtResource("1_1wp2n")
metadata/_custom_type_script = "uid://b7f8x2qimvn37"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_1wp2n")
[node name="ColorRect" type="ColorRect" parent="."]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -5.0
offset_top = -50.0
offset_right = 5.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.6, 0.6, 0.6, 1)

View File

@ -1,6 +0,0 @@
@tool
class_name Hullplate
extends StructuralPiece
# This piece represents an interior surface.
# No additional logic is needed right now, we just need the class_name.

View File

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

View File

@ -1,28 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://bho8x10x4oab7"]
[ext_resource type="Script" uid="uid://crmwm623rh1ps" path="res://scenes/ship/builder/pieces/hullplate.gd" id="1_ecow4"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_1wp2n"]
size = Vector2(100, 100)
[node name="Hullplate" type="StaticBody2D"]
collision_mask = 0
script = ExtResource("1_ecow4")
metadata/_custom_type_script = "uid://b7f8x2qimvn37"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_1wp2n")
[node name="ColorRect" type="ColorRect" parent="."]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -50.0
offset_top = -50.0
offset_right = 50.0
offset_bottom = 50.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.4, 0.4, 0.4, 1)

View File

@ -1,20 +0,0 @@
@tool
class_name StructuralPiece
extends OrbitalBody2D
# Does this piece block atmosphere? (e.g., a hull plate would, a girder would not).
@export var is_pressurized: bool = true
# The health of this specific piece.
@export var health: float = 100.0
# This setter is triggered by the editor.
var is_preview: bool = false:
set(value):
is_preview = value
if is_preview:
# Make the piece translucent if it's a preview.
modulate = Color(1, 1, 1, 0.5)
else:
# Make it opaque if it's a permanent piece.
modulate = Color(1, 1, 1, 1)

View File

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

View File

@ -1,16 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://ds4eilbvihjy7"]
[ext_resource type="Script" uid="uid://b7f8x2qimvn37" path="res://scenes/ship/builder/pieces/structural_piece.gd" id="1_6jsoj"]
[sub_resource type="CircleShape2D" id="CircleShape2D_jsbwo"]
[node name="StructuralPiece" type="Node2D"]
script = ExtResource("1_6jsoj")
metadata/_custom_type_script = "uid://0isnsk356que"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_jsbwo")
[node name="ColorRect" type="ColorRect" parent="."]
offset_right = 40.0
offset_bottom = 40.0

View File

@ -1,37 +0,0 @@
extends Databank
class_name ShipStatusShard
## This shard emits a signal with the formatted ship status text.
signal status_updated(text: String)
# Called by the Station when it's created.
func initialize(ship_root: Module):
self.root_module = ship_root
## Describes the functions this shard needs as input.
func get_input_sockets() -> Array[String]:
return []
## Describes the signals this shard can output.
func get_output_sockets() -> Array[String]:
return ["status_updated"]
func _physics_process(delta):
if not is_instance_valid(root_module):
return
# 1. Gather all the data from the root module.
var rotation_deg = rad_to_deg(root_module.rotation)
var angular_vel_dps = rad_to_deg(root_module.angular_velocity)
var linear_vel_mps = root_module.linear_velocity.length()
# 2. Build the string that will be displayed.
var status_text = """
[font_size=24]Ship Status[/font_size]
[font_size=18]Rotation: %.1f deg[/font_size]
[font_size=18]Ang. Vel.: %.2f deg/s[/font_size]
[font_size=18]Velocity: %.2f m/s[/font_size]
""" % [rotation_deg, angular_vel_dps, linear_vel_mps]
# 3. Emit the signal with the formatted text.
status_updated.emit(status_text)

View File

@ -1,66 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://7yc6a07xoccy"]
[ext_resource type="Script" uid="uid://cdmmiixa75f3x" path="res://scenes/tests/3d/character_pawn_3d.gd" id="1_4frsu"]
[ext_resource type="PackedScene" uid="uid://bm1rbv4tuppbc" path="res://eva_suit_controller.tscn" id="3_gnddn"]
[ext_resource type="Script" uid="uid://y3vo40i16ek3" path="res://scenes/tests/3d/zero_g_movement_component.gd" id="4_8jhjh"]
[ext_resource type="PackedScene" uid="uid://ba3ijdstp2bvt" path="res://scenes/tests/3d/player_controller_3d.tscn" id="4_bcy3l"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6vm80"]
[sub_resource type="CapsuleMesh" id="CapsuleMesh_6vm80"]
[sub_resource type="SphereShape3D" id="SphereShape3D_gnddn"]
radius = 1.0
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_gnddn"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:rotation")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath("CameraPivot:rotation")
properties/2/spawn = true
properties/2/replication_mode = 2
[node name="CharacterPawn3D" type="CharacterBody3D"]
script = ExtResource("1_4frsu")
metadata/_custom_type_script = "uid://cdmmiixa75f3x"
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_6vm80")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("CapsuleMesh_6vm80")
[node name="CameraAnchor" type="Marker3D" parent="."]
[node name="CameraPivot" type="Node3D" parent="."]
physics_interpolation_mode = 1
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
top_level = true
[node name="SpringArm" type="SpringArm3D" parent="CameraPivot"]
spring_length = 3.0
[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm"]
[node name="GripDetector" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1)
collision_layer = 0
collision_mask = 32768
monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="GripDetector"]
shape = SubResource("SphereShape3D_gnddn")
[node name="ZeroGMovementComponent" type="Node3D" parent="."]
script = ExtResource("4_8jhjh")
metadata/_custom_type_script = "uid://y3vo40i16ek3"
[node name="EVAMovementComponent" parent="." instance=ExtResource("3_gnddn")]
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_gnddn")
[node name="PlayerController3d" parent="." instance=ExtResource("4_bcy3l")]

View File

@ -1,50 +0,0 @@
extends Node
var port = 42069
func create_server() -> void:
print(multiplayer.multiplayer_peer)
var peer = ENetMultiplayerPeer.new()
setup_connections()
var error = peer.create_server(port)
if error:
push_error(error)
return
multiplayer.multiplayer_peer = peer
print("Server Unique ID: ", multiplayer.get_unique_id())
func create_client() -> void:
setup_connections()
var peer = ENetMultiplayerPeer.new()
var error = peer.create_client("127.0.0.1", port)
if error:
push_error(error)
return
multiplayer.multiplayer_peer = peer
print("Client Unique ID: ", multiplayer.get_unique_id())
func setup_connections():
multiplayer.peer_connected.connect(on_peer_connected)
multiplayer.peer_disconnected.connect(on_peer_disconnected)
multiplayer.connected_to_server.connect(on_connected_to_server)
func on_peer_connected(peer_id: int) -> void:
print("Peer %s recieved connection: %s" % [multiplayer.get_unique_id(), peer_id])
# For each peer that connects, we put them in the queue to spawn
if multiplayer.is_server():
GameManager.queue_spawn_player(peer_id)
func on_peer_disconnected(peer_id: int) -> void:
print("Peer %s lost connection to: %s" % [multiplayer.get_unique_id(), peer_id])
print(multiplayer.get_peers())
func on_connected_to_server() -> void:
print("%s connected to server!" % multiplayer.get_unique_id())

View File

@ -1,182 +0,0 @@
extends Node2D
class_name OrbitalBody2D
# Defines the physical behavior of this body.
enum PhysicsMode {
INDEPENDENT, # An independent body with its own physics simulation (planets, characters in EVA).
COMPOSITE, # A body that aggregates mass and forces from ANCHORED children (ships, modules).
ANCHORED # A component that is "bolted on" and defers physics to a COMPOSITE parent.
}
@export var physics_mode: PhysicsMode = PhysicsMode.INDEPENDENT
var current_grid_authority: OrbitalBody2D = null
# Mass of this individual component
@export var base_mass: float = 1.0
@export var mass: float = 0.0 # Aggregated mass of this body and all its OrbitalBody2D children
@export var linear_velocity: Vector2 = Vector2.ZERO
@export var angular_velocity: float = 0.0
# Variables to accumulate forces applied during the current physics frame
var accumulated_force: Vector2 = Vector2.ZERO
var accumulated_torque: float = 0.0
# Placeholder for Moment of Inertia.
@export var inertia: float = 1.0
func _ready():
# Ensure mass update runs immediately before the first _physics_process.
recalculate_physical_properties()
set_physics_process(not Engine.is_editor_hint())
physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF
# --- PUBLIC FORCE APPLICATION METHODS ---
# This method is called by a component (like Thruster) at its global position.
func apply_force(force: Vector2, pos: Vector2 = self.global_position):
# This is the force routing logic.
match physics_mode:
PhysicsMode.INDEPENDENT:
_add_forces(force, pos)
PhysicsMode.COMPOSITE:
_add_forces(force, pos)
## If we are the root, accumulate the force and calculate torque on the total body.
#accumulated_force += force
#
## Calculate torque (2D cross product: T = r x F = r.x * F.y - r.y * F.x)
## 'r' is the vector from the center of mass (global_position) to the point of force application (position).
#var r = position - global_position
#var torque = r.x * force.y - r.y * force.x
#accumulated_torque += torque
PhysicsMode.ANCHORED:
# If we are not the root, we must route the force to the next OrbitalBody2D parent.
var p = get_parent()
while p:
if p is OrbitalBody2D:
# Recursively call the parent's apply_force method.
# This sends the force (and its original global position) up the chain.
p.apply_force(force, pos)
return # Stop at the first OrbitalBody2D parent
p = p.get_parent()
push_error("Anchored OrbitalBody2D has become dislodged and is now Composite.")
physics_mode = PhysicsMode.COMPOSITE
apply_force(force, position)
func _add_forces(force: Vector2, pos: Vector2 = Vector2.ZERO):
# If we are the root, accumulate the force and calculate torque on the total body.
accumulated_force += force
# Calculate torque (2D cross product: T = r x F = r.x * F.y - r.y * F.x)
# 'r' is the vector from the center of mass (global_position) to the point of force application (position).
var r = pos - global_position
var torque = r.x * force.y - r.y * force.x
accumulated_torque += torque
func _update_mass_and_inertia():
mass = base_mass
for child in get_children():
if child is OrbitalBody2D:
child._update_mass_and_inertia() # Recurse into children
mass += child.mass
print("Node: %s, Mass: %f" % [self, mass])
func _physics_process(delta):
if not Engine.is_editor_hint():
# Note: We're not integrating forces for anchored bodies
# anchored bodies add forces to their parents and
match physics_mode:
PhysicsMode.INDEPENDENT:
_integrate_forces(delta)
PhysicsMode.COMPOSITE:
_integrate_forces(delta)
func _integrate_forces(delta):
# Safety Check for Division by Zero
var sim_mass = mass
if sim_mass <= 0.0:
# If mass is zero, stop all physics to prevent NaN explosion.
accumulated_force = Vector2.ZERO
accumulated_torque = 0.0
return
## 1. Calculate and accumulate gravitational force (F_g)
#var total_gravity_force = OrbitalMechanics.calculate_n_body_gravity_forces(self)
#
## 2. Total all forces: F_total = F_g + F_accumulated_from_thrusters
#var total_force = total_gravity_force +
# 3. Apply Linear Physics (F = ma)
var linear_acceleration = accumulated_force / sim_mass # Division is now safe
linear_velocity += linear_acceleration * delta
global_position += linear_velocity * delta
# 4. Apply Rotational Physics (T = I * angular_acceleration)
var angular_acceleration = accumulated_torque / inertia
angular_velocity += angular_acceleration * delta
rotation += angular_velocity * delta
# 5. Reset accumulated forces for the next frame
accumulated_force = Vector2.ZERO
accumulated_torque = 0.0
# This is the new, corrected function.
func recalculate_physical_properties():
# For non-composite bodies, the calculation is simple.
if physics_mode != PhysicsMode.COMPOSITE:
mass = base_mass
# --- THE FIX ---
# An independent body doesn't calculate inertia from parts.
# We ensure it has a non-zero default value to prevent division by zero.
if inertia <= 0.0:
inertia = 1.0
return
var all_parts: Array[OrbitalBody2D] = []
_collect_anchored_parts(all_parts)
if all_parts.is_empty():
mass = base_mass
inertia = 1.0
return
# --- Step 1: Calculate Total Mass and LOCAL Center of Mass ---
var total_mass = 0.0
var weighted_local_pos_sum = Vector2.ZERO
for part in all_parts:
total_mass += part.base_mass
# We get the part's position *relative to the root module*
var local_pos = part.global_position - self.global_position
weighted_local_pos_sum += local_pos * part.base_mass
var local_center_of_mass = Vector2.ZERO
if total_mass > 0:
local_center_of_mass = weighted_local_pos_sum / total_mass
# --- Step 2: Calculate Total Moment of Inertia around the LOCAL CoM ---
var total_inertia = 0.0
for part in all_parts:
# Get the part's position relative to the root module again
var local_pos = part.global_position - self.global_position
# The radius is the distance from the part's local position to the ship's local center of mass
var r_squared = (local_pos - local_center_of_mass).length_squared()
total_inertia += part.base_mass * r_squared
# --- Step 3: Assign the final values ---
self.mass = total_mass
# We apply a scaling factor here because our "units" are pixels.
# This brings the final value into a range that feels good for gameplay.
# You can tune this factor to make ships feel heavier or lighter.
self.inertia = total_inertia * 0.01
#print("Physics Recalculated: Mass=%.2f kg, Inertia=%.2f" % [mass, inertia])
# A recursive helper function to get an array of all OrbitalBody2D children
func _collect_anchored_parts(parts_array: Array):
parts_array.append(self)
for child in get_children():
# TODO: this assumes that all OrbitalBody2D that are attached are done in a clean chain without breaks, which may not be the case
if child is OrbitalBody2D and child.physics_mode == PhysicsMode.ANCHORED:
child._collect_anchored_parts(parts_array)

View File

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

View File

@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="GameConfig" load_steps=5 format=3 uid="uid://cv15sck8rl2b7"]
[ext_resource type="PackedScene" uid="uid://chgycmkkaf7jv" path="res://scenes/characters/pilot_ball.tscn" id="1_s0mxw"]
[ext_resource type="PackedScene" uid="uid://didt2nsdtbmra" path="res://modules/Tube.tscn" id="2_75b4c"]
[ext_resource type="PackedScene" uid="uid://dnre6svquwdtb" path="res://scenes/characters/player_controller.tscn" id="2_sk8k5"]
[ext_resource type="Script" uid="uid://bfc6u1f8sigxj" path="res://scripts/singletons/game_config.gd" id="3_75b4c"]
[resource]
script = ExtResource("3_75b4c")
player_controller_scene = ExtResource("2_sk8k5")
default_pawn_scene = ExtResource("1_s0mxw")
default_ship_scene = ExtResource("2_75b4c")
metadata/_custom_type_script = "uid://bfc6u1f8sigxj"

View File

@ -1,241 +0,0 @@
# OrbitalMechanics.gd
extends Node
# This singleton holds all universal physics constants and functions.
# The scaled gravitational constant for the entire simulation.
const G = 1.0 # Adjust this to control the "speed" of your simulation
const MIN_INFLUENCE_THRESHOLD = 0.00001
const ROCHE_LIMIT_MASS_MULTIPLIER = 0.5
class BodyTuple:
var body_a: OrbitalBody2D
var body_b: OrbitalBody2D
var cached_forces: Dictionary[BodyTuple, Vector2] = {
}
# --- Centralized Physics Process ---
func _physics_process(_delta: float) -> void:
var star_system: StarSystem = GameManager.current_star_system
if not star_system:
return
var star = star_system.get_star()
var planetary_systems = star_system.get_planetary_systems()
# TODO: Would this be true in case we are working with a system that is just a rouge planet or a brown dwarf?
if not star:
return
# 1: Calculate star system pull
# a: Get the star and top level Barycenters
var top_level_bodies: Array[OrbitalBody2D] = [star]
top_level_bodies.append_array(planetary_systems)
# b: calculate and apply pull between these
apply_n_body_forces(top_level_bodies)
# 2: Calculate Barycenters local pull
for system in planetary_systems:
# a: Get each Planetary Barycenters OrbitalBody2Ds (including Ships, Satelites, and Stations fully within the Barycenter)
var system_attractors = system.get_internal_attractors()
# b: Calculate and apply pull within each Barycenter
apply_n_body_forces(system_attractors)
# 3: Calculate top level Ships, Satelites, and Stations pull
# a: Get top level OrbitalBody2Ds of non-celestial classes
for star_orbiter in star_system.get_orbital_bodies():
# b: Split into Star Orbiting and On-Approach using mass/distance ratios to Barycenters
# TODO: Check for distance to Barycenter
# c: For Star Orbiting objects -> Calculate and apply pull to star and Barycenter
star_orbiter.apply_force(calculate_n_body_force(star_orbiter, top_level_bodies))
# d: For On Approach -> Calculate and apply pull to star and distant Barycenters
# as well as individual bodies within approaching Barycenter
func calculate_gravitational_force(orbiter: OrbitalBody2D, primary: OrbitalBody2D) -> Vector2:
if not is_instance_valid(orbiter) or not is_instance_valid(primary):
return Vector2.ZERO
var distance_sq = orbiter.global_position.distance_squared_to(primary.global_position)
if distance_sq < 1.0:
return Vector2.ZERO
# --- Influence Pruning (Culling) ---
# We check both directions of influence
var influence_a = primary.mass / distance_sq
var influence_b = orbiter.mass / distance_sq
if influence_a < MIN_INFLUENCE_THRESHOLD and influence_b < MIN_INFLUENCE_THRESHOLD:
return Vector2.ZERO
var force_magnitude = (G * primary.mass * orbiter.mass) / distance_sq
var direction = orbiter.global_position.direction_to(primary.global_position)
return direction * force_magnitude
# Calculates the pull between a set number of bodies
# Use carefully to simulate each level of the simulation
# Iterate through every unique pair of bodies (i, j) where j > i
func apply_n_body_forces(attractors: Array[OrbitalBody2D]):
# Iterate through every unique pair of bodies (i, j) where j > i
for i in range(attractors.size()):
var body_a: OrbitalBody2D = attractors[i]
if not is_instance_valid(body_a): continue
for j in range(i + 1, attractors.size()):
var body_b: OrbitalBody2D = attractors[j]
if not is_instance_valid(body_b): continue
# Calculate the force vector ONCE
var force_vector = calculate_gravitational_force(body_a, body_b)
# Apply the force symmetrically
if force_vector != Vector2.ZERO:
body_a.apply_force(force_vector)
body_b.apply_force(-force_vector)
func calculate_n_body_force(body: OrbitalBody2D, attractors: Array[OrbitalBody2D]) -> Vector2:
var total_pull: Vector2 = Vector2.ZERO
for attractor in attractors:
total_pull += calculate_gravitational_force(body, attractor)
return total_pull
func calculate_n_body_gravity_forces(body_to_affect: Node2D) -> Vector2:
var total_force = Vector2.ZERO
if not is_instance_valid(body_to_affect):
return total_force
# Get the list of all major gravitational bodies from the GameManager.
var system_data = GameManager.get_system_data()
if not system_data:
return total_force
# We only consider planets and the star as major attractors for performance.
var attractors = system_data.all_bodies()
for attractor in attractors:
if is_instance_valid(attractor) and attractor != body_to_affect:
total_force += calculate_gravitational_force(body_to_affect, attractor)
return total_force
# Calculates the perfect initial velocity for a stable circular orbit.
func calculate_circular_orbit_velocity(orbiter: OrbitalBody2D, primary: OrbitalBody2D) -> Vector2:
if not is_instance_valid(primary):
return Vector2.ZERO
var distance = orbiter.global_position.distance_to(primary.global_position)
if distance == 0:
return Vector2.ZERO
# v = sqrt(G * M / r)
var speed_magnitude = sqrt(G * primary.mass / distance)
var direction_to_orbiter = primary.global_position.direction_to(orbiter.global_position)
var perpendicular_direction = Vector2(direction_to_orbiter.y, -direction_to_orbiter.x)
return perpendicular_direction * speed_magnitude
func _calculate_n_body_orbital_path(body_to_trace: OrbitalBody2D) -> PackedVector2Array:
var num_steps = 10
var time_step = 60
var ghost_position = body_to_trace.global_position
var ghost_velocity = body_to_trace.linear_velocity
var path_points = PackedVector2Array()
for i in range(num_steps):
# Create a temporary "ghost" body to calculate forces on.
var ghost_body = OrbitalBody2D.new()
ghost_body.global_position = ghost_position
ghost_body.mass = body_to_trace.mass
# Use our library to get the total gravitational force at the ghost's position.
var total_force = calculate_n_body_gravity_forces(ghost_body)
var acceleration = total_force / ghost_body.mass
ghost_velocity += acceleration * time_step
ghost_position += ghost_velocity * time_step
path_points.append(ghost_position)
ghost_body.free() # Clean up the temporary node
return path_points
# Calculates an array of points for the orbit RELATIVE to the primary body.
func _calculate_relative_orbital_path(body_to_trace: OrbitalBody2D) -> PackedVector2Array:
if not is_instance_valid(body_to_trace) or not body_to_trace.has_method("get_primary") or not is_instance_valid(body_to_trace.get_primary()):
return PackedVector2Array()
var primary = body_to_trace.get_primary()
var primary_mass = primary.mass
var body_mass = body_to_trace.mass
var ghost_relative_pos = body_to_trace.global_position - primary.global_position
var ghost_relative_vel = body_to_trace.linear_velocity - primary.linear_velocity
var r_magnitude = ghost_relative_pos.length()
if r_magnitude == 0:
return PackedVector2Array()
var v_sq = ghost_relative_vel.length_squared()
var mu = G * primary_mass
var specific_energy = v_sq / 2.0 - mu / r_magnitude
var num_steps = 200
var time_step: float
if specific_energy >= 0:
time_step = 0.1
else:
var semi_major_axis = -mu / (2.0 * specific_energy)
var orbital_period = 2.0 * PI * sqrt(pow(semi_major_axis, 3) / mu)
time_step = orbital_period / float(num_steps)
var path_points = PackedVector2Array()
for i in range(num_steps):
var distance_sq = ghost_relative_pos.length_squared()
if distance_sq < 1.0:
break
var direction = -ghost_relative_pos.normalized()
var force_magnitude = (G * primary_mass * body_mass) / distance_sq
var force_vector = direction * force_magnitude
var acceleration = force_vector / body_mass
ghost_relative_vel += acceleration * time_step
ghost_relative_pos += ghost_relative_vel * time_step
path_points.append(ghost_relative_pos)
return path_points
# Calculates the Hill Sphere radius for a satellite.
# This is the region where the satellite's gravity is dominant over its primary's.
func calculate_hill_sphere(orbiter: OrbitalBody2D, primary: OrbitalBody2D) -> float:
if not is_instance_valid(orbiter) or not is_instance_valid(primary) or primary.mass <= 0:
return 0.0
var distance = orbiter.global_position.distance_to(primary.global_position)
# The formula is: a * (m / 3M)^(1/3)
var mass_ratio = orbiter.mass / (3.0 * primary.mass)
if mass_ratio < 0: return 0.0
return distance * pow(mass_ratio, 1.0/3.0)
# Calculates a simplified Roche Limit, or minimum safe orbital distance.
func calculate_simplified_roche_limit(primary: OrbitalBody2D) -> float:
if not is_instance_valid(primary) or primary.mass <= 0:
return 100.0 # Return a small default if primary is invalid
# We approximate a "radius" from the square root of the mass, then apply a multiplier.
# This ensures more massive stars and planets have larger "keep-out" zones.
return sqrt(primary.mass) * ROCHE_LIMIT_MASS_MULTIPLIER
func get_orbital_time_in_seconds(orbiter: OrbitalBody2D, primary: OrbitalBody2D) -> float:
var mu = OrbitalMechanics.G * primary.mass
var r = orbiter.global_position.distance_to(primary.global_position)
return TAU * sqrt(pow(r, 3) / mu)

View File

@ -1,53 +0,0 @@
# scripts/star_system.gd
class_name StarSystem
extends Node2D
@export_group("System Metadata")
@export var system_name: String = "Kepler-186"
@export var galactic_coordinates: Vector2i = Vector2i.ZERO
var system_data: SystemData
func _ready():
# 1. Create the generator tool.
var generator = StarSystemGenerator.new()
# 2. Tell the generator to build the system within this StarSystem node.
system_data = generator.generate(self)
# 3. Register the completed system with the GameManager.
GameManager.register_star_system(self)
GameManager.start_game()
# --- Public API for accessing system data ---
func get_star() -> OrbitalBody2D:
if is_instance_valid(system_data):
return system_data.star
return null
func get_planetary_systems() -> Array[Barycenter]:
var bodies: Array[Barycenter] = []
for child in get_children():
if child is Barycenter:
bodies.append(child)
return bodies
func get_orbital_bodies() -> Array[OrbitalBody2D]:
var bodies: Array[OrbitalBody2D] = []
for child in get_children():
if child is Star or child is Barycenter:
continue
if child is OrbitalBody2D:
bodies.append(child)
return bodies
func get_system_data() -> SystemData:
return system_data
class AsteroidBelt:
var width : float
var mass : float
var centered_radius : float = 0.0
var asteroids : Array[OrbitalBody2D]

View File

@ -1,115 +0,0 @@
# scripts/star_system_generator.gd
class_name StarSystemGenerator
extends RefCounted
# --- Stable Mass Ratios & Generation Rules ---
const STAR_MASS = 50000000.0
const PLANET_MASS = STAR_MASS / 10000.0 # Planet is 10,000x less massive than the star.
const MOON_MASS = PLANET_MASS / 1000.0 # Moon is 1,000x less massive than its planet.
const MIN_PLANETS = 3
const MAX_PLANETS = 8
const MAX_MOONS_PER_PLANET = 5
const ORBIT_SAFETY_FACTOR = 5 # Increase space between orbits
# --- The main public method ---
func generate(star_system: StarSystem) -> SystemData:
# 1. Create the root Barycenter for the entire system.
var system_data = SystemData.new()
# 2. Create the star itself inside the root Barycenter.
var star = Star.new()
system_data.star = star
star.name = "Star"
star.base_mass = STAR_MASS
star_system.add_child(star)
# 3. Procedurally generate and place the planetary systems.
var num_planets = randi_range(MIN_PLANETS, MAX_PLANETS)
var current_orbit_radius = 15000.0 # OrbitalMechanics.calculate_simplified_roche_limit(star) # Start with the first orbit
for i in range(num_planets):
# A. Create the Barycenter for the new planetary system.
var planet_barycenter = Barycenter.new()
planet_barycenter.name = "PlanetSystem_%d" % (i + 1)
star_system.add_child(planet_barycenter)
# B. Create the planet itself inside its Barycenter.
var planet = Planet.new()
system_data.planets.append(planet)
planet.name = "Planet_%d" % (i + 1)
planet.base_mass = randf_range(PLANET_MASS * 0.2, PLANET_MASS * 5.0)
planet_barycenter.add_child(planet)
planet.owner = planet_barycenter
planet.position = Vector2.ZERO
planet_barycenter.recalculate_total_mass()
# C. Create moons for this planet.
_generate_moons(planet, planet_barycenter, system_data)
# D. Place the entire planetary system in a stable orbit.
planet_barycenter.global_position = Vector2(current_orbit_radius, 0).rotated(randf_range(0, TAU))
planet_barycenter.linear_velocity = OrbitalMechanics.calculate_circular_orbit_velocity(planet_barycenter, star)
# Update the new edge of the star's influence
# 1. Calculate the Hill Sphere (gravitational influence) for the planet we just placed.
var hill_sphere = OrbitalMechanics.calculate_hill_sphere(planet_barycenter, star)
# 2. Add this zone of influence (plus a safety margin) to the current radius
# to determine the starting point for the NEXT planet. This ensures orbits never overlap.
current_orbit_radius += hill_sphere * ORBIT_SAFETY_FACTOR
# --- Spawn the ship at the last planet's L4 point ---
if i == num_planets - 1:
_spawn_player_ship(star_system, star, planet_barycenter)
return system_data
func _generate_moons(planet: OrbitalBody2D, planet_barycenter: Barycenter, system_data: SystemData):
var num_moons = randi_range(0, int(planet.mass / MOON_MASS / 2.0)) # Heavier planets get more moons
num_moons = min(num_moons, MAX_MOONS_PER_PLANET)
var current_orbit_radius = 200.0 # OrbitalMechanics.calculate_simplified_roche_limit(planet) # Start with the first orbit
for i in range(num_moons):
var moon = Moon.new()
system_data.moons.append(moon)
planet_barycenter.add_child(moon)
planet_barycenter.recalculate_total_mass()
moon.owner = planet_barycenter
moon.name = "Moon_%d" % (i + 1)
moon.base_mass = randf_range(MOON_MASS * 0.1, MOON_MASS * 2.0)
moon.position = Vector2(current_orbit_radius, 0).rotated(randf_range(0, TAU))
# Velocity is calculated relative to the parent (the planet)
moon.linear_velocity = OrbitalMechanics.calculate_circular_orbit_velocity(moon, planet_barycenter)
# Update the new edge of the planets's influence
# 1. Calculate the Hill Sphere (gravitational influence) for the moon we just placed.
var hill_sphere = OrbitalMechanics.calculate_hill_sphere(moon, planet_barycenter)
# 2. Add this zone of influence (plus a safety margin) to the current radius
# to determine the starting point for the NEXT planet. This ensures orbits never overlap.
current_orbit_radius += hill_sphere * ORBIT_SAFETY_FACTOR
# --- NEW FUNCTION: Spawns the player ship at a Lagrange point ---
func _spawn_player_ship(star_system: StarSystem, star: OrbitalBody2D, planet_system: Barycenter):
# L4 and L5 Lagrange points form an equilateral triangle with the star and planet.
# We'll calculate L4 by rotating the star-planet vector by +60 degrees.
var star_to_planet_vec = planet_system.global_position - star.global_position
var l4_position = star.global_position + star_to_planet_vec.rotated(PI / 3.0)
# The ship's velocity at L4 must match the orbital characteristics of that point.
# This is an approximation where we rotate the planet's velocity vector by 60 degrees.
var l4_velocity = planet_system.linear_velocity.rotated(PI / 3.0)
# Instantiate, position, and configure the ship.
var ship_instance = GameManager.config.default_ship_scene.instantiate()
GameManager.register_ship(ship_instance)
ship_instance.name = "PlayerShip"
star_system.add_child(ship_instance) # Add ship to the root StarSystem node
ship_instance.global_position = l4_position
ship_instance.linear_velocity = l4_velocity
ship_instance.rotation = l4_velocity.angle() + (PI / 2.0) # Point prograde
# Make sure the new ship is included in the physics simulation
#_system_data_dict.all_bodies.append(ship_instance)
print("Player ship spawned at L4 point of %s" % planet_system.name)

67
src/export_presets.cfg Normal file
View File

@ -0,0 +1,67 @@
[preset.0]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../export/moa.exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.0.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"

View File

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 994 B

7
src/main.tscn Normal file
View File

@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://dogqi2c58qdc0"]
[ext_resource type="PackedScene" uid="uid://ojcho3pi3u7n" path="res://scenes/UI/main_menu/main_menu.tscn" id="1_ig7tw"]
[node name="StartMenu" type="Node3D" unique_id=1392183658]
[node name="MainMenu" parent="." unique_id=2099645465 instance=ExtResource("1_ig7tw")]

View File

@ -0,0 +1,573 @@
[gd_scene load_steps=5 format=3 uid="uid://bkwogkfqk2uxo"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_ktv2t"]
[ext_resource type="PackedScene" uid="uid://bsyufiv0m1018" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_shb7f"]
[ext_resource type="PackedScene" uid="uid://dvpy3urgtm62n" path="res://scenes/ship/components/hardware/spawner.tscn" id="3_ism2t"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_ism2t"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:linear_velocity")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath(".:rotation")
properties/2/spawn = true
properties/2/replication_mode = 1
[node name="3dTestShip" type="RigidBody3D" unique_id=246037729]
physics_interpolation_mode = 1
script = ExtResource("1_ktv2t")
physics_mode = 1
base_mass = 10000.0
metadata/_custom_type_script = "uid://6co67nfy8ngb"
[node name="Hullplate7" parent="." unique_id=1182121679 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, 0)
physics_mode = 2
[node name="Hullplate8" parent="." unique_id=294855274 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, -1)
physics_mode = 2
[node name="Hullplate9" parent="." unique_id=130054924 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, 1)
physics_mode = 2
[node name="Hullplate4" parent="." unique_id=2133064539 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, 0)
physics_mode = 2
[node name="Hullplate5" parent="." unique_id=1436331513 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, -1)
physics_mode = 2
[node name="Hullplate6" parent="." unique_id=1249365999 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, 1)
physics_mode = 2
[node name="Hullplate11" parent="." unique_id=1656979163 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 0, -4)
physics_mode = 2
[node name="Hullplate13" parent="." unique_id=1426276711 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, -3)
physics_mode = 2
[node name="Hullplate14" parent="." unique_id=1212526811 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, -4)
physics_mode = 2
[node name="Hullplate15" parent="." unique_id=403515873 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, -2)
physics_mode = 2
[node name="Hullplate16" parent="." unique_id=145935239 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, -3)
physics_mode = 2
[node name="Hullplate17" parent="." unique_id=1662804653 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, -4)
physics_mode = 2
[node name="Hullplate18" parent="." unique_id=741829932 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, -2)
physics_mode = 2
[node name="Hullplate21" parent="." unique_id=31417961 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 0, 4)
physics_mode = 2
[node name="Hullplate22" parent="." unique_id=1845702661 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, 3)
physics_mode = 2
[node name="Hullplate23" parent="." unique_id=1747432968 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, 2)
physics_mode = 2
[node name="Hullplate24" parent="." unique_id=1486518216 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, -1, 4)
physics_mode = 2
[node name="Hullplate25" parent="." unique_id=1880158566 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, 3)
physics_mode = 2
[node name="Hullplate26" parent="." unique_id=1506445603 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, 2)
physics_mode = 2
[node name="Hullplate27" parent="." unique_id=1749302489 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, 2, 1, 4)
physics_mode = 2
[node name="Hullplate31" parent="." unique_id=1965678834 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, -4)
physics_mode = 2
[node name="Hullplate32" parent="." unique_id=515940324 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, -3)
physics_mode = 2
[node name="Hullplate33" parent="." unique_id=313389603 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, -2)
physics_mode = 2
[node name="Hullplate34" parent="." unique_id=363616195 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, 0)
physics_mode = 2
[node name="Hullplate35" parent="." unique_id=568985619 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, -1)
physics_mode = 2
[node name="Hullplate36" parent="." unique_id=193191417 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, 1)
physics_mode = 2
[node name="Hullplate38" parent="." unique_id=1152815429 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 0, -4)
physics_mode = 2
[node name="Hullplate40" parent="." unique_id=1303768723 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, -1)
physics_mode = 2
[node name="Hullplate41" parent="." unique_id=1489680526 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, 0)
physics_mode = 2
[node name="Hullplate42" parent="." unique_id=1454642421 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, 1)
physics_mode = 2
[node name="Hullplate43" parent="." unique_id=1322280114 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, -3)
physics_mode = 2
[node name="Hullplate44" parent="." unique_id=1380061102 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, -4)
physics_mode = 2
[node name="Hullplate45" parent="." unique_id=1740305308 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, -2)
physics_mode = 2
[node name="Hullplate48" parent="." unique_id=587023569 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 0, 4)
physics_mode = 2
[node name="Hullplate49" parent="." unique_id=1103858035 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, 3)
physics_mode = 2
[node name="Hullplate50" parent="." unique_id=916625356 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, 2)
physics_mode = 2
[node name="Hullplate51" parent="." unique_id=2115734988 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, -1, 4)
physics_mode = 2
[node name="Hullplate52" parent="." unique_id=1715698306 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, 3)
physics_mode = 2
[node name="Hullplate53" parent="." unique_id=369018899 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, 2)
physics_mode = 2
[node name="Hullplate54" parent="." unique_id=1618415296 instance=ExtResource("2_shb7f")]
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2, 1, 4)
physics_mode = 2
[node name="Hullplate57" parent="." unique_id=1148292814 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5, 0, 4.5)
physics_mode = 2
[node name="Hullplate58" parent="." unique_id=1183219370 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.50098634, -1, 4.4908433)
physics_mode = 2
[node name="Hullplate59" parent="." unique_id=95522376 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.49901366, -1, 4.4908433)
physics_mode = 2
[node name="Hullplate60" parent="." unique_id=960534764 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5, -1, 4.5)
physics_mode = 2
[node name="Hullplate61" parent="." unique_id=1862079328 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.50098634, 1.000001, 4.4908433)
physics_mode = 2
[node name="Hullplate62" parent="." unique_id=876185578 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.49901366, 1.000001, 4.4908433)
physics_mode = 2
[node name="Hullplate64" parent="." unique_id=622302151 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, 9.536743e-07, 4.4908433)
physics_mode = 2
[node name="Hullplate65" parent="." unique_id=2027647666 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, -1, 4.4908433)
physics_mode = 2
[node name="Hullplate66" parent="." unique_id=335333911 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, 1.000001, 4.4908433)
physics_mode = 2
[node name="Hullplate63" parent="." unique_id=779321466 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5, 1, 4.5)
physics_mode = 2
[node name="Hullplate69" parent="." unique_id=391423682 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5009866, 9.536743e-07, -4.5091567)
physics_mode = 2
[node name="Hullplate70" parent="." unique_id=1436426809 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.50098634, -1, -4.5091567)
physics_mode = 2
[node name="Hullplate71" parent="." unique_id=1045660804 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.49901366, -1, -4.5091567)
physics_mode = 2
[node name="Hullplate72" parent="." unique_id=1696784058 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5009866, -1, -4.5091567)
physics_mode = 2
[node name="Hullplate73" parent="." unique_id=1709873058 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.50098634, 1.000001, -4.5091567)
physics_mode = 2
[node name="Hullplate74" parent="." unique_id=1071906843 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.49901366, 1.000001, -4.5091567)
physics_mode = 2
[node name="Hullplate75" parent="." unique_id=413542580 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, 9.536743e-07, -4.5091567)
physics_mode = 2
[node name="Hullplate76" parent="." unique_id=448578032 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, -1, -4.5091567)
physics_mode = 2
[node name="Hullplate77" parent="." unique_id=1162322851 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4990137, 1.000001, -4.5091567)
physics_mode = 2
[node name="Hullplate78" parent="." unique_id=790206161 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, 0, 1, 0, -0.014328662, 0, 0.99989736, -1.5009866, 1.000001, -4.5091567)
physics_mode = 2
[node name="Hullplate79" parent="." unique_id=1019136641 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, -3.009157)
physics_mode = 2
[node name="Hullplate80" parent="." unique_id=152922175 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, -3.009157)
physics_mode = 2
[node name="Hullplate81" parent="." unique_id=771888008 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, -3.009157)
physics_mode = 2
[node name="Hullplate82" parent="." unique_id=816092557 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, -2.009157)
physics_mode = 2
[node name="Hullplate83" parent="." unique_id=1871920861 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, -2.009157)
physics_mode = 2
[node name="Hullplate84" parent="." unique_id=103727539 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, -2.009157)
physics_mode = 2
[node name="Hullplate85" parent="." unique_id=1457444620 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, -4.009157)
physics_mode = 2
[node name="Hullplate86" parent="." unique_id=1402217859 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, -4.009157)
physics_mode = 2
[node name="Hullplate87" parent="." unique_id=293240152 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, -3.009157)
physics_mode = 2
[node name="Hullplate88" parent="." unique_id=158231735 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, -2.009157)
physics_mode = 2
[node name="Hullplate89" parent="." unique_id=2017317978 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, -4.009157)
physics_mode = 2
[node name="Hullplate90" parent="." unique_id=1810711362 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, -4.009157)
physics_mode = 2
[node name="Hullplate91" parent="." unique_id=648502427 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, -0.009156942)
physics_mode = 2
[node name="Hullplate92" parent="." unique_id=1280848561 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, -0.009156942)
physics_mode = 2
[node name="Hullplate93" parent="." unique_id=1000182357 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, -0.009156942)
physics_mode = 2
[node name="Hullplate94" parent="." unique_id=663755561 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, 0.99084306)
physics_mode = 2
[node name="Hullplate95" parent="." unique_id=977211031 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, 0.99084306)
physics_mode = 2
[node name="Hullplate96" parent="." unique_id=1017704164 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, 0.99084306)
physics_mode = 2
[node name="Hullplate97" parent="." unique_id=2095269489 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, -1.0091572)
physics_mode = 2
[node name="Hullplate98" parent="." unique_id=615154295 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, -1.0091572)
physics_mode = 2
[node name="Hullplate99" parent="." unique_id=1435686924 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, -0.009156942)
physics_mode = 2
[node name="Hullplate100" parent="." unique_id=361501534 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, 0.99084306)
physics_mode = 2
[node name="Hullplate101" parent="." unique_id=776176100 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, -1.0091572)
physics_mode = 2
[node name="Hullplate102" parent="." unique_id=1146417492 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, -1.0091572)
physics_mode = 2
[node name="Hullplate103" parent="." unique_id=1413321748 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, 2.990843)
physics_mode = 2
[node name="Hullplate104" parent="." unique_id=1044980803 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, 2.990843)
physics_mode = 2
[node name="Hullplate105" parent="." unique_id=1804409489 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, 2.990843)
physics_mode = 2
[node name="Hullplate106" parent="." unique_id=1076107521 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.5, 1.5, 4)
physics_mode = 2
[node name="Hullplate107" parent="." unique_id=1190510681 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.5, 1.5, 4)
physics_mode = 2
[node name="Hullplate108" parent="." unique_id=855909591 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, 3.990843)
physics_mode = 2
[node name="Hullplate109" parent="." unique_id=946006990 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, 1.5000012, 1.9908428)
physics_mode = 2
[node name="Hullplate110" parent="." unique_id=1957722835 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, 1.5000012, 1.9908428)
physics_mode = 2
[node name="Hullplate111" parent="." unique_id=1708941560 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, 2.990843)
physics_mode = 2
[node name="Hullplate112" parent="." unique_id=598393913 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, 3.990843)
physics_mode = 2
[node name="Hullplate113" parent="." unique_id=629535431 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, 1.5000012, 1.9908428)
physics_mode = 2
[node name="Hullplate114" parent="." unique_id=1483594858 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, 1.5000012, 1.9908428)
physics_mode = 2
[node name="Hullplate115" parent="." unique_id=1186769437 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, -3.009157)
physics_mode = 2
[node name="Hullplate116" parent="." unique_id=752889015 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, -3.009157)
physics_mode = 2
[node name="Hullplate117" parent="." unique_id=175698677 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, -3.009157)
physics_mode = 2
[node name="Hullplate118" parent="." unique_id=670641245 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, -2.009157)
physics_mode = 2
[node name="Hullplate119" parent="." unique_id=988678524 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, -2.009157)
physics_mode = 2
[node name="Hullplate120" parent="." unique_id=896262764 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, -2.009157)
physics_mode = 2
[node name="Hullplate121" parent="." unique_id=1336630931 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, -4.009157)
physics_mode = 2
[node name="Hullplate122" parent="." unique_id=101919359 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, -4.009157)
physics_mode = 2
[node name="Hullplate123" parent="." unique_id=1356736016 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, -3.009157)
physics_mode = 2
[node name="Hullplate124" parent="." unique_id=742815341 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, -2.009157)
physics_mode = 2
[node name="Hullplate125" parent="." unique_id=1651537246 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, -4.009157)
physics_mode = 2
[node name="Hullplate126" parent="." unique_id=1253078352 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, -4.009157)
physics_mode = 2
[node name="Hullplate127" parent="." unique_id=519787812 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, -0.009156942)
physics_mode = 2
[node name="Hullplate128" parent="." unique_id=629828036 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, -0.009156942)
physics_mode = 2
[node name="Hullplate129" parent="." unique_id=2010663580 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, -0.009156942)
physics_mode = 2
[node name="Hullplate130" parent="." unique_id=1705163002 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, 0.99084306)
physics_mode = 2
[node name="Hullplate131" parent="." unique_id=1635599014 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, 0.99084306)
physics_mode = 2
[node name="Hullplate132" parent="." unique_id=789401102 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, 0.99084306)
physics_mode = 2
[node name="Hullplate133" parent="." unique_id=1671040057 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, -1.0091572)
physics_mode = 2
[node name="Hullplate134" parent="." unique_id=2118015321 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, -1.0091572)
physics_mode = 2
[node name="Hullplate135" parent="." unique_id=1970124357 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, -0.009156942)
physics_mode = 2
[node name="Hullplate136" parent="." unique_id=2129372302 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, 0.99084306)
physics_mode = 2
[node name="Hullplate137" parent="." unique_id=543355427 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, -1.0091572)
physics_mode = 2
[node name="Hullplate138" parent="." unique_id=1885736043 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, -1.0091572)
physics_mode = 2
[node name="Hullplate139" parent="." unique_id=654209436 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, 2.990843)
physics_mode = 2
[node name="Hullplate140" parent="." unique_id=1938132143 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, 2.990843)
physics_mode = 2
[node name="Hullplate141" parent="." unique_id=486424951 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, 2.990843)
physics_mode = 2
[node name="Hullplate142" parent="." unique_id=910140496 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, 3.990843)
physics_mode = 2
[node name="Hullplate143" parent="." unique_id=515293159 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, 3.990843)
physics_mode = 2
[node name="Hullplate144" parent="." unique_id=890871001 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, 3.990843)
physics_mode = 2
[node name="Hullplate145" parent="." unique_id=1626468827 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, -0.50098634, -1.4999988, 1.9908428)
physics_mode = 2
[node name="Hullplate146" parent="." unique_id=578516444 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0.49901366, -1.4999988, 1.9908428)
physics_mode = 2
[node name="Hullplate147" parent="." unique_id=402255852 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, 2.990843)
physics_mode = 2
[node name="Hullplate148" parent="." unique_id=1631434711 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, 3.990843)
physics_mode = 2
[node name="Hullplate149" parent="." unique_id=726702930 instance=ExtResource("2_shb7f")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 1.4990137, -1.4999988, 1.9908428)
physics_mode = 2
[node name="Hullplate150" parent="." unique_id=1001521061 instance=ExtResource("2_shb7f")]
transform = Transform3D(0.99989736, 0, 0.014328662, -0.014328662, -4.371139e-08, 0.99989736, 6.263257e-10, -1, -4.37069e-08, -1.5009866, -1.4999988, 1.9908428)
physics_mode = 2
[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1071155008]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 1, -3)
[node name="OmniLight3D2" type="OmniLight3D" parent="." unique_id=151820223]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.6, 1, -3)
[node name="OmniLight3D3" type="OmniLight3D" parent="." unique_id=390575041]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.6, 1, 4)
[node name="OmniLight3D4" type="OmniLight3D" parent="." unique_id=1659652061]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.4, 1, 4)
[node name="Camera3D" type="Camera3D" parent="." unique_id=1905582997]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3)
current = true
[node name="Spawner" parent="." unique_id=6714366 instance=ExtResource("3_ism2t")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2)
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=2096937457]
replication_config = SubResource("SceneReplicationConfig_ism2t")

View File

@ -0,0 +1,26 @@
[gd_scene load_steps=4 format=3 uid="uid://xcgmicfdqqb1"]
[ext_resource type="Script" uid="uid://6co67nfy8ngb" path="res://scenes/ship/builder/module.gd" id="1_ogx5r"]
[ext_resource type="PackedScene" uid="uid://bsyufiv0m1018" path="res://scenes/ship/builder/pieces/hullplate.tscn" id="2_nyqc6"]
[ext_resource type="PackedScene" uid="uid://dvpy3urgtm62n" path="res://scenes/ship/components/hardware/spawner.tscn" id="3_3bya3"]
[node name="PhysicsTestingShip" type="RigidBody3D"]
script = ExtResource("1_ogx5r")
base_mass = 200.0
metadata/_custom_type_script = "uid://6co67nfy8ngb"
[node name="Hullplate" parent="." instance=ExtResource("2_nyqc6")]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, -1, 0)
[node name="Spawner" parent="." instance=ExtResource("3_3bya3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.021089494, 0)
[node name="OmniLight3D" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, -2)
[node name="OmniLight3D2" type="OmniLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, -2)
[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3)
current = true

View File

@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://cwblg6q5qse6c"]
[ext_resource type="PackedScene" uid="uid://d3hitk62fice4" path="res://scenes/ship/builder/pieces/bulkhead.tscn" id="1_obkto"]
[node name="TestShip" type="Node3D"]
[node name="Bulkhead" parent="." instance=ExtResource("1_obkto")]

View File

@ -10,9 +10,10 @@ config_version=5
[application]
config/name="space_simulation"
config/name="Millimeters of Aluminum"
config/version="0.1"
run/main_scene="uid://dogqi2c58qdc0"
config/features=PackedStringArray("4.5", "Forward Plus")
config/features=PackedStringArray("4.5", "Double Precision", "Forward Plus")
config/icon="res://icon.svg"
[autoload]
@ -21,6 +22,7 @@ OrbitalMechanics="*res://scripts/singletons/orbital_mechanics.gd"
GameManager="*res://scripts/singletons/game_manager.gd"
Constants="*res://scripts/singletons/constants.gd"
NetworkHandler="*res://scripts/network/network_handler.gd"
MotionUtils="*res://scripts/singletons/motion_utils.gd"
[display]
@ -166,18 +168,20 @@ left_click={
2d_physics/layer_4="projectiles"
2d_physics/layer_5="bulkheads"
2d_physics/layer_6="characters"
3d_physics/layer_15="weld"
3d_physics/layer_16="grip"
[physics]
common/physics_jitter_fix=0.0
3d/default_gravity=0.0
3d/default_gravity_vector=Vector3(0, 0, 0)
3d/default_linear_damp=0.0
3d/default_angular_damp=0.0
3d/sleep_threshold_linear=0.0
2d/default_gravity=0.0
2d/default_gravity_vector=Vector2(0, 0)
2d/default_linear_damp=0.0
2d/sleep_threshold_linear=0.0
common/physics_interpolation=true
[plugins]

View File

@ -0,0 +1,35 @@
extends Control
@onready var resume_button: Button = %ResumeButton
@onready var disconnect_button: Button = %DisconnectButton
@onready var quit_button: Button = %QuitButton
func _ready():
resume_button.pressed.connect(toggle_menu)
disconnect_button.pressed.connect(_on_disconnect_pressed)
quit_button.pressed.connect(_on_quit_pressed)
hide()
func _input(event):
if event.is_action_pressed("ui_cancel"):
toggle_menu()
func toggle_menu():
visible = !visible
if visible:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
else:
# Only capture mouse if we are actually playing a pawn
# You might need a smarter check here depending on game state
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _on_disconnect_pressed():
NetworkHandler.close_connection()
toggle_menu()
# Return to main menu
get_tree().change_scene_to_file("res://main.tscn")
func _on_quit_pressed():
get_tree().quit()

View File

@ -0,0 +1 @@
uid://2aoy8ivk2hgl

View File

@ -0,0 +1,58 @@
[gd_scene load_steps=2 format=3 uid="uid://pausemenu456"]
[ext_resource type="Script" uid="uid://2aoy8ivk2hgl" path="res://scenes/UI/ingame_menu/ingame_menu.gd" id="1_pm_script"]
[node name="IngameeMenu" type="Control" unique_id=8878860]
process_mode = 3
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_pm_script")
[node name="ColorRect" type="ColorRect" parent="." unique_id=1882361500]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 0.05)
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=1122355242]
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=1948011184]
layout_mode = 2
theme_override_constants/separation = 15
[node name="Label" type="Label" parent="CenterContainer/VBoxContainer" unique_id=214886966]
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Menu"
horizontal_alignment = 1
[node name="ResumeButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1856665966]
unique_name_in_owner = true
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
text = "Resume"
[node name="DisconnectButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=4948876]
unique_name_in_owner = true
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
text = "Disconnect"
[node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1695513560]
unique_name_in_owner = true
custom_minimum_size = Vector2(150, 40)
layout_mode = 2
text = "Quit Game"

View File

@ -0,0 +1,47 @@
extends Control
# @export var lobby_menu: Control
# @export var settings_menu: Control
# Buttons and input fields
@onready var host_button: Button = %HostButton
@onready var join_button: Button = %JoinButton
@onready var settings_button: Button = %SettingsButton
@onready var quit_button: Button = %QuitButton
@onready var address_entry: LineEdit = %AddressEntry
func _ready():
host_button.pressed.connect(_on_host_pressed)
join_button.pressed.connect(_on_join_pressed)
quit_button.pressed.connect(_on_quit_pressed)
# Ensure we start with a clean slate
# lobby_menu.visible = false
# If we just returned from a game, ensure mouse is visible
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
func _on_host_pressed():
# For a simple test, hosting immediately starts the server and the game
NetworkHandler.create_server()
NetworkHandler.on_peer_connected(multiplayer.get_unique_id())
_transition_to_game()
func _on_join_pressed():
var ip = address_entry.text
if ip.is_empty():
ip = "127.0.0.1" # Default to localhost
NetworkHandler.create_client(ip)
# The NetworkHandler signals will handle the actual transition once connected
# But for UI feedback, we might want to show a "Connecting..." label here.
func _on_quit_pressed():
get_tree().quit()
func _transition_to_game():
# This would typically load the main game scene.
# Since your main scene IS the game loop currently, we might need to
# just hide the menu if it's an overlay, OR change scenes.
# Assuming Main.tscn is the game world:
get_tree().change_scene_to_file("res://scripts/star_system.tscn")

View File

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

View File

@ -0,0 +1,78 @@
[gd_scene load_steps=2 format=3 uid="uid://ojcho3pi3u7n"]
[ext_resource type="Script" uid="uid://dypq4h3hy1l3v" path="res://scenes/UI/main_menu/main_menu.gd" id="1_script"]
[node name="MainMenu" type="Control" unique_id=2099645465]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_script")
[node name="Background" type="ColorRect" parent="." unique_id=2137889995]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0.05, 0.05, 0.08, 1)
[node name="CenterContainer" type="CenterContainer" parent="." unique_id=1954458945]
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=542164632]
layout_mode = 2
theme_override_constants/separation = 20
[node name="Label" type="Label" parent="CenterContainer/VBoxContainer" unique_id=973405608]
layout_mode = 2
theme_override_font_sizes/font_size = 48
text = "Millimeters of Aluminium"
horizontal_alignment = 1
[node name="AddressEntry" type="LineEdit" parent="CenterContainer/VBoxContainer" unique_id=994010326]
unique_name_in_owner = true
layout_mode = 2
placeholder_text = "127.0.0.1"
alignment = 1
[node name="HostButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1548149031]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
text = "Host Game"
[node name="JoinButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1826215269]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
text = "Join Game"
[node name="SettingsButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=811999044]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
text = "Settings"
[node name="QuitButton" type="Button" parent="CenterContainer/VBoxContainer" unique_id=1005717980]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 50)
layout_mode = 2
text = "Quit to Desktop"
[node name="LobbyMenu" type="Control" parent="." unique_id=604668798]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

View File

@ -7,7 +7,7 @@ signal follow_requested(body: Node2D)
@onready var name_label: Label = $NameLabel
var body_reference: OrbitalBody2D
var body_reference: OrbitalBody3D
var dot_color: Color = Color.WHITE
var hover_tween: Tween
@ -27,17 +27,10 @@ func _ready() -> void:
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
func initialize(body: OrbitalBody2D):
func initialize(body: OrbitalBody3D):
body_reference = body
name_label.text = body.name
if body is Star:
dot_color = Color.GOLD
elif body is Planet:
dot_color = Color.DODGER_BLUE
elif body is Moon:
dot_color = Color.PURPLE
else:
dot_color = Color.CYAN
self.tooltip_text = _generate_tooltip_text()
@ -98,7 +91,7 @@ func _on_mouse_exited():
func _generate_tooltip_text() -> String:
var info = [body_reference.name]
if body_reference is Planet:
if body_reference is CelestialBody:
var planet_system = body_reference.get_parent() as Barycenter
var period_seconds = OrbitalMechanics.get_orbital_time_in_seconds(planet_system, GameManager.get_system_data().star)
@ -106,18 +99,11 @@ func _generate_tooltip_text() -> String:
var moon_count = 0
for child in planet_system.get_internal_attractors():
if child is Moon:
if child is CelestialBody:
moon_count += 1
if moon_count > 0:
info.append("Moons: %d" % moon_count)
if body_reference is Moon:
var planet_system = body_reference.get_parent() as Barycenter
var period_seconds = OrbitalMechanics.get_orbital_time_in_seconds(body_reference as Moon, planet_system)
info.append("Orbital Period: %s" % _format_seconds_to_mmss(period_seconds))
if body_reference is Module:
info.append("Class: Player Vessel")
info.append("Mass: %.0f kg" % body_reference.mass)

View File

@ -1,6 +1,6 @@
# scripts/barycenter.gd
class_name Barycenter
extends OrbitalBody2D
extends OrbitalBody3D
func _ready():
physics_mode = PhysicsMode.INDEPENDENT
@ -10,10 +10,10 @@ func _ready():
# We only need physics_process to integrate our own movement.
set_physics_process(true)
func get_internal_attractors() -> Array[OrbitalBody2D]:
var internal_attractors: Array[OrbitalBody2D] = []
func get_internal_attractors() -> Array[OrbitalBody3D]:
var internal_attractors: Array[OrbitalBody3D] = []
for child in get_children():
if child is OrbitalBody2D:
if child is OrbitalBody3D:
internal_attractors.append(child)
return internal_attractors

View File

@ -0,0 +1,22 @@
[gd_scene load_steps=3 format=3 uid="uid://b7bh45nrtdom5"]
[ext_resource type="Script" uid="uid://b2hb3bwrlh40c" path="res://scenes/celestial_bodies/barycenter.gd" id="1_e776o"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_e776o"]
properties/0/path = NodePath(".:linear_velocity")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:position")
properties/1/spawn = true
properties/1/replication_mode = 1
[node name="Barycenter" type="RigidBody3D" unique_id=1389317234]
script = ExtResource("1_e776o")
metadata/_custom_type_script = "uid://wlm40n8ywr"
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=717759965]
replication_config = SubResource("SceneReplicationConfig_e776o")
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="." unique_id=2061784354]
_spawnable_scenes = PackedStringArray("uid://dv18eg4xrlefe")
spawn_path = NodePath("..")

View File

@ -0,0 +1,16 @@
class_name CelestialBody extends OrbitalBody3D
# --- Set in corresponding scene ---
# var auto_proxy_gravity = false
@export var radius: float = 100.0:
set(value):
radius = value
_set_radi()
func _set_radi():
if $Surface.mesh is SphereMesh:
$Surface.mesh.radius = radius
$Surface.mesh.height = radius * 2.0
if $CollisionShape3D.shape is SphereShape3D:
$CollisionShape3D.shape.radius = radius

View File

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

View File

@ -0,0 +1,48 @@
[gd_scene load_steps=6 format=3 uid="uid://dv18eg4xrlefe"]
[ext_resource type="Script" uid="uid://dok35h0q4pseh" path="res://scenes/celestial_bodies/celestial_body.gd" id="1_uxu4s"]
[ext_resource type="Material" uid="uid://de0xnmjf12ted" path="res://scenes/celestial_bodies/materials/sun_mat.tres" id="2_vi0nt"]
[sub_resource type="SphereMesh" id="SphereMesh_vi0nt"]
resource_local_to_scene = true
material = ExtResource("2_vi0nt")
radius = 2000.0
height = 4000.0
[sub_resource type="SphereShape3D" id="SphereShape3D_uxu4s"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_vi0nt"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:rotation")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath(".:linear_velocity")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath(".:angular_velocity")
properties/3/spawn = true
properties/3/replication_mode = 1
properties/4/path = NodePath(".:radius")
properties/4/spawn = true
properties/4/replication_mode = 1
[node name="CelestialBody" type="RigidBody3D" unique_id=345490070]
script = ExtResource("1_uxu4s")
auto_proxy_gravity = false
metadata/_custom_type_script = "uid://dok35h0q4pseh"
[node name="Surface" type="MeshInstance3D" parent="." unique_id=193823349]
mesh = SubResource("SphereMesh_vi0nt")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=232085687]
shape = SubResource("SphereShape3D_uxu4s")
[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1965995953]
light_color = Color(0.958646, 0.7997282, 0.55087835, 1)
omni_range = 200000.0
omni_attenuation = 2.0
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=2090029903]
replication_config = SubResource("SceneReplicationConfig_vi0nt")

View File

@ -0,0 +1,52 @@
// https://godotshaders.com/shader/3d-sun-shader/
shader_type spatial;
render_mode specular_schlick_ggx;
uniform float Glow_Power : hint_range(0,10) = 3;
uniform float Lightness_Difference : hint_range(0,10) = 3;
uniform vec4 Sun_Color: source_color;
uniform sampler2D voronoi_noise;
uniform sampler2D emission_noise;
varying vec3 vertex_pos;
uniform float waveSpeed : hint_range(0,1) = 0.1;
uniform float fresnel : hint_range(0,2) = 1.0;
uniform float scale : hint_range(0,2) = 0.01;
uniform float blendSharpness : hint_range(0,2) = 0.0;
// TRIPLANAR FUNCTION
vec4 triplanar_texture(vec3 position, vec3 normal, vec2 offset, sampler2D noise) {
vec4 colX = texture(noise, position.xy * scale + offset);
vec4 colY = texture(noise, position.xz * scale + offset);
vec4 colZ = texture(noise, position.zy * scale + offset);
vec3 blendWeight = abs(normal);
blendWeight = vec3(pow(blendWeight.x, blendSharpness), pow(blendWeight.y, blendSharpness), pow(blendWeight.z, blendSharpness));
blendWeight /= (blendWeight.x + blendWeight.y + blendWeight.z);
return colX * blendWeight.x + colY * blendWeight.y + colZ * blendWeight.z;
}
void vertex() {
vertex_pos = VERTEX;
}
void fragment() {
// Fresnel
float fresnel_out = pow(fresnel - clamp(dot(NORMAL, VIEW), 0.0, fresnel), fresnel);
vec2 waveOffsetA = vec2(TIME * waveSpeed, TIME * waveSpeed * 0.8);
vec2 waveOffsetB = vec2(TIME * waveSpeed * - 0.8, TIME * waveSpeed * -0.3);
vec2 result_offset = waveOffsetA + waveOffsetB;
vec3 cloud_tex = triplanar_texture(vertex_pos, NORMAL, result_offset, voronoi_noise).rgb;
vec3 cloud_tex_with_light = cloud_tex * vec3(Lightness_Difference);
vec3 cloud_tex_with_light_with_color = cloud_tex_with_light * Sun_Color.rgb;
vec3 cloud_tex_with_light_with_color_with_glow = vec3(Glow_Power) * cloud_tex_with_light_with_color;
vec3 noise_tex = triplanar_texture(vertex_pos, NORMAL, result_offset, emission_noise).rgb;
vec3 result = cloud_tex_with_light_with_color_with_glow * noise_tex;
EMISSION = vec3(fresnel_out) * result;
}

View File

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

View File

@ -0,0 +1,27 @@
[gd_resource type="ShaderMaterial" load_steps=6 format=3 uid="uid://de0xnmjf12ted"]
[ext_resource type="Shader" uid="uid://0cjdd62t25g1" path="res://scenes/celestial_bodies/materials/sun_mat.gdshader" id="1_f1bp4"]
[sub_resource type="FastNoiseLite" id="FastNoiseLite_f1bp4"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_1eyo5"]
noise = SubResource("FastNoiseLite_f1bp4")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_6484p"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_oxjal"]
noise = SubResource("FastNoiseLite_6484p")
in_3d_space = true
[resource]
render_priority = 0
shader = ExtResource("1_f1bp4")
shader_parameter/Glow_Power = 10.0
shader_parameter/Lightness_Difference = 4.26400020254
shader_parameter/Sun_Color = Color(0.90528274, 0.8164857, 0.6356678, 1)
shader_parameter/voronoi_noise = SubResource("NoiseTexture2D_oxjal")
shader_parameter/emission_noise = SubResource("NoiseTexture2D_1eyo5")
shader_parameter/waveSpeed = 0.1
shader_parameter/fresnel = 1.0
shader_parameter/scale = 0.01
shader_parameter/blendSharpness = 0.0

View File

@ -1,5 +1,5 @@
# CharacterPawn.gd
extends CharacterBody3D
extends OrbitalBody3D
class_name CharacterPawn3D
## Core Parameters
@ -17,8 +17,8 @@ var _pitch_yaw_input: Vector2 = Vector2.ZERO
## Rotation Variables
@onready var camera_anchor: Marker3D = $CameraAnchor
@onready var camera_pivot: Node3D = $CameraPivot
@onready var camera: Camera3D = $CameraPivot/SpringArm/Camera3D
@onready var camera_pivot: Node3D = $CameraAnchor/CameraPivot
@onready var camera: Camera3D = $CameraAnchor/CameraPivot/SpringArm/Camera3D
@export_range(0.1, PI / 2.0) var max_yaw_rad: float = deg_to_rad(80.0)
@export_range(-PI / 2.0 + 0.01, 0) var min_pitch_rad: float = deg_to_rad(-75.0)
@export_range(0, PI / 2.0 - 0.01) var max_pitch_rad: float = deg_to_rad(60.0)
@ -29,7 +29,6 @@ var _pitch_yaw_input: Vector2 = Vector2.ZERO
@onready var zero_g_movemement_component: ZeroGMovementComponent = $ZeroGMovementComponent
## Physics State (Managed by Pawn)
var angular_velocity: Vector3 = Vector3.ZERO
@export var angular_damping: float = 0.95 # Base damping
## Other State Variables
@ -53,42 +52,39 @@ func _ready():
else:
printerr("GripDetector Area3D node not found on CharacterPawn!")
if is_multiplayer_authority():
if name == str(multiplayer.get_unique_id()):
camera.make_current()
camera.process_mode = Node.PROCESS_MODE_ALWAYS
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
camera_pivot.global_transform = camera_anchor.get_global_transform_interpolated()
func _physics_process(delta: float):
# 1. Apply Mouse Rotation (Universal head look)
func _physics_process(_delta: float):
_apply_mouse_rotation()
if zero_g_movemement_component: # Fallback to ZeroG controller (for initiating reach)
zero_g_movemement_component.process_movement(delta, _move_input, _vertical_input, _roll_input, _l_click_input, _r_click_input)
# 3. Integrate Angular Velocity (Universal)
_integrate_angular_velocity(delta)
# 4. Apply Linear Velocity & Collision (Universal)
# Use move_and_slide for states affected by gravity/floor or zero-g collisions
move_and_slide()
# Check for collision response AFTER move_and_slide
var collision_count = get_slide_collision_count()
if collision_count > 0:
var collision = get_slide_collision(collision_count - 1) # Get last collision
# Delegate or handle basic bounce
if eva_suit_component:
eva_suit_component.handle_collision(collision, collision_energy_loss)
else:
_handle_basic_collision(collision)
# 5. Reset Inputs
_reset_inputs()
func _integrate_forces(state: PhysicsDirectBodyState3D):
if not is_multiplayer_authority(): return
super (state)
# print("Integrating forces for pawn %s" % name)
# print(" Move Input: %s, Vertical Input: %f, Roll Input: %f" % [_move_input, _vertical_input, _roll_input])
# Zero-G Movement
if zero_g_movemement_component:
# We pass the physics state
zero_g_movemement_component.process_movement(state, _move_input, _vertical_input, _roll_input, _l_click_input, _r_click_input)
# EVA Suit Movement
if eva_suit_component and zero_g_movemement_component.movement_state == ZeroGMovementComponent.MovementState.IDLE:
eva_suit_component.process_eva_movement(state, _move_input, _vertical_input, _roll_input, _r_click_input)
# --- Universal Rotation ---
func _apply_mouse_rotation():
if _pitch_yaw_input != Vector2.ZERO:
@ -112,28 +108,6 @@ func _integrate_angular_velocity(delta: float):
if angular_velocity.length_squared() < 0.0001:
angular_velocity = Vector3.ZERO
func _handle_basic_collision(collision: KinematicCollision3D):
var surface_normal = collision.get_normal()
velocity = velocity.bounce(surface_normal)
velocity *= (1.0 - collision_energy_loss * 0.5)
# --- Public Helper for Controllers ---
# Applies torque affecting angular velocity
func add_torque(torque_global: Vector3, delta: float):
# Calculate effective inertia (base + suit multiplier if applicable)
var effective_inertia = base_inertia * (eva_suit_component.inertia_multiplier if eva_suit_component else 1.0)
if effective_inertia <= 0: effective_inertia = 1.0 # Safety prevent division by zero
# Apply change directly to angular velocity using the global torque
angular_velocity += (torque_global / effective_inertia) * delta
# --- Movement Implementations (Keep non-EVA ones here) ---
func _apply_walking_movement(_delta: float): pass # TODO
func _apply_ladder_floating_drag(delta: float):
velocity = velocity.lerp(Vector3.ZERO, delta * 2.0);
angular_velocity = angular_velocity.lerp(Vector3.ZERO, delta * 2.0)
func _apply_ladder_movement(_delta: float): pass # TODO
# --- Input Setters/Resets (Add vertical to set_movement_input) ---
func set_movement_input(move: Vector2, roll: float, vertical: float): _move_input = move; _roll_input = roll; _vertical_input = vertical
func set_interaction_input(interact_input: PlayerController3D.KeyInput): _interact_input = interact_input
@ -157,7 +131,8 @@ func _reset_head_yaw(delta: float):
# Smoothly apply the reset target to the actual pivot rotation
camera_anchor.rotation.y = lerpf(camera_anchor.rotation.y, 0.0, delta * head_turn_lerp_speed)
func _notification(what: int) -> void:
match what:
NOTIFICATION_ENTER_TREE:
set_multiplayer_authority(int(name))
# TODO: Re-enable when multiplayer authority per pawn is functional
# func _notification(what: int) -> void:
# match what:
# NOTIFICATION_ENTER_TREE:
# set_multiplayer_authority(int(name))

View File

@ -0,0 +1,81 @@
[gd_scene load_steps=10 format=3 uid="uid://7yc6a07xoccy"]
[ext_resource type="Script" uid="uid://cdmmiixa75f3x" path="res://scenes/character/character_pawn_3d.gd" id="1_4frsu"]
[ext_resource type="PackedScene" uid="uid://bm1rbv4tuppbc" path="res://scenes/character/eva_suit_controller.tscn" id="3_gnddn"]
[ext_resource type="Script" uid="uid://y3vo40i16ek3" path="res://scenes/character/zero_g_movement_component.gd" id="4_8jhjh"]
[ext_resource type="PackedScene" uid="uid://ba3ijdstp2bvt" path="res://scenes/character/player_controller_3d.tscn" id="4_bcy3l"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6vm80"]
[sub_resource type="CapsuleMesh" id="CapsuleMesh_6vm80"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_673rh"]
radius = 0.1
height = 1.0
[sub_resource type="SphereShape3D" id="SphereShape3D_gnddn"]
radius = 1.0
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_gnddn"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:rotation")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath("CameraAnchor:rotation")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath(".:linear_velocity")
properties/3/spawn = false
properties/3/replication_mode = 0
properties/4/path = NodePath(".:angular_velocity")
properties/4/spawn = false
properties/4/replication_mode = 0
[node name="CharacterPawn3D" type="RigidBody3D" unique_id=288275840]
physics_interpolation_mode = 1
top_level = true
script = ExtResource("1_4frsu")
metadata/_custom_type_script = "uid://cdmmiixa75f3x"
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1967015232]
shape = SubResource("CapsuleShape3D_6vm80")
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1703183586]
mesh = SubResource("CapsuleMesh_6vm80")
[node name="CameraAnchor" type="Marker3D" parent="." unique_id=462168232]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.7000000000000001, 0)
[node name="CameraPivot" type="Node3D" parent="CameraAnchor" unique_id=794640520]
physics_interpolation_mode = 1
[node name="SpringArm" type="SpringArm3D" parent="CameraAnchor/CameraPivot" unique_id=1399441728]
shape = SubResource("CapsuleShape3D_673rh")
spring_length = 2.0
margin = 0.1
[node name="Camera3D" type="Camera3D" parent="CameraAnchor/CameraPivot/SpringArm" unique_id=1779046272]
far = 200000.0
[node name="GripDetector" type="Area3D" parent="." unique_id=734413990]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1)
collision_layer = 0
collision_mask = 32768
monitorable = false
[node name="CollisionShape3D" type="CollisionShape3D" parent="GripDetector" unique_id=1939219836]
shape = SubResource("SphereShape3D_gnddn")
[node name="ZeroGMovementComponent" type="Node3D" parent="." unique_id=594953523]
script = ExtResource("4_8jhjh")
metadata/_custom_type_script = "uid://y3vo40i16ek3"
[node name="EVAMovementComponent" parent="." unique_id=1806288315 instance=ExtResource("3_gnddn")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.13939085347041424, 0.5148942200402955)
[node name="PlayerController3d" parent="." unique_id=1450011826 instance=ExtResource("4_bcy3l")]
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=732324183]
replication_config = SubResource("SceneReplicationConfig_gnddn")

View File

@ -6,14 +6,18 @@ class_name EVAMovementComponent
var pawn: CharacterPawn3D
## EVA Parameters (Moved from ZeroGPawn)
@export var orientation_speed: float = 2.0 # Used for orienting body to camera
@export var move_speed: float = 2.0
@export var roll_torque: float = 2.5
@export var orientation_speed: float = 1.0 # Used for orienting body to camera
@export var linear_acceleration: float = 1.0
@export var roll_torque_acceleration: float = 0.25
@export var angular_damping: float = 0.95 # Base damping applied by pawn, suit might add more?
@export var inertia_multiplier: float = 1.0 # How much the suit adds to pawn's base inertia (placeholder)
@export var stabilization_kp: float = 5.0
@export var stabilization_kd: float = 1.0
var _auto_orient_target: Basis = Basis() # Stores the target orientation
var _is_auto_orienting: bool = false # Flag to signal the pawn
@export var auto_orient_stop_velocity_threshold: float = 0.01 # (in rad/s)
## State
var stabilization_target: Node3D = null
var stabilization_enabled: bool = false
@ -23,54 +27,21 @@ func _ready():
if not pawn:
printerr("EVAMovementComponent must be a child of a CharacterBody3D pawn.")
return
# Make sure the paths match your CharacterPawn scene structure
# if camera_anchor:
# camera = camera_anchor.get_node_or_null("SpringArm/Camera3D") # Adjusted path for SpringArm
# if not camera_anchor or not camera:
# printerr("EVAMovementComponent could not find CameraPivot/SpringArm/Camera3D on pawn.")
## Called by Pawn's _integrate_forces when suit equipped
func process_eva_movement(state: PhysicsDirectBodyState3D, move_input: Vector2, vertical_input: float, roll_input: float, orienting_input: PlayerController3D.KeyInput):
# --- 1. Handle Orient Input ---
if orienting_input.pressed or orienting_input.held:
_set_auto_orient_target(state)
# --- Standardized Movement API ---
## Called by Pawn's _physics_process when in FLOATING state with suit equipped
func process_movement(delta: float, move_input: Vector2, vertical_input: float, roll_input: float, orienting_input: PlayerController3D.KeyInput):
var orienting = orienting_input.held
if not is_instance_valid(pawn): return
if orienting:
_orient_pawn(delta)
_process_auto_orientation(state) # [Function 2] Run the controller
# Check if stabilization is active and handle it first
if stabilization_enabled and is_instance_valid(stabilization_target):
_apply_stabilization_torques(delta)
_apply_stabilization_torques(state)
else:
# Apply regular movement/torque only if not stabilizing
_apply_floating_movement(delta, move_input, vertical_input, roll_input)
func apply_thrusters(pawn: CharacterPawn3D, delta: float, move_input: Vector2, vertical_input: float, roll_input: float):
if not is_instance_valid(pawn): return
# Apply Linear Velocity
var pawn_forward = -pawn.global_basis.z
var pawn_right = pawn.global_basis.x
var pawn_up = pawn.global_basis.y
var move_dir_horizontal = (pawn_forward * move_input.y + pawn_right * move_input.x)
var move_dir_vertical = pawn_up * vertical_input
var combined_move_dir = move_dir_horizontal + move_dir_vertical
if combined_move_dir != Vector3.ZERO:
pawn.velocity += combined_move_dir.normalized() * move_speed * delta
# Apply Roll Torque
var roll_torque_global = -pawn.global_basis.z * (roll_input) * roll_torque # Sign fixed
pawn.add_torque(roll_torque_global, delta)
## Called by Pawn to handle collision response in FLOATING state
func handle_collision(collision: KinematicCollision3D, collision_energy_loss: float):
if not is_instance_valid(pawn): return
var surface_normal = collision.get_normal()
var reflected_velocity = pawn.velocity.bounce(surface_normal)
reflected_velocity *= (1.0 - collision_energy_loss)
pawn.velocity = reflected_velocity # Update pawn's velocity directly
_apply_floating_movement(state, move_input, vertical_input, roll_input)
## Called by Pawn when entering FLOATING state with suit
func on_enter_state():
@ -84,78 +55,67 @@ func on_exit_state():
# --- Internal Logic ---
func _apply_floating_movement(delta: float, move_input: Vector2, vertical_input: float, roll_input: float):
func _apply_floating_movement(state: PhysicsDirectBodyState3D, move_input: Vector2, vertical_input: float, roll_input: float):
# Apply Linear Velocity
var pawn_forward = -pawn.global_basis.z
var pawn_right = pawn.global_basis.x # Use pawn's right for consistency
var pawn_up = pawn.global_basis.y
var move_dir_horizontal = (pawn_forward * move_input.y + pawn_right * move_input.x)
var move_dir_vertical = pawn_up * vertical_input
var move_dir_horizontal = (-state.transform.basis.z * move_input.y + state.transform.basis.x * move_input.x)
var move_dir_vertical = state.transform.basis.y * vertical_input
var combined_move_dir = move_dir_horizontal + move_dir_vertical
if combined_move_dir != Vector3.ZERO:
pawn.velocity += combined_move_dir.normalized() * move_speed * delta
state.apply_central_force(combined_move_dir.normalized() * linear_acceleration)
# --- Apply Roll Torque ---
# Calculate torque magnitude based on input
var roll_torque_vector = pawn.transform.basis.z * (-roll_input) * roll_torque
if roll_input != 0.0:
_is_auto_orienting = false # Cancel auto-orientation if rolling manually
var roll_acceleration = state.transform.basis.z * (-roll_input) * roll_torque_acceleration
# Apply the global torque vector using the pawn's helper function
pawn.add_torque(roll_torque_vector, delta)
state.apply_torque(roll_acceleration)
func _set_auto_orient_target(state: PhysicsDirectBodyState3D):
# Set the target to where the camera is currently looking
var target_forward = - pawn.camera_anchor.global_basis.z # Look where camera looks
var target_up = state.transform.basis.y
_auto_orient_target = Basis.looking_at(target_forward, target_up)
_is_auto_orienting = true # Start the orientation process
# --- Auto-Orientation Logic ---
func _orient_pawn(delta: float):
# 1. Determine Target Orientation Basis
var initial_cam_basis = pawn.camera_anchor.global_basis
var target_forward = -pawn.camera_anchor.global_basis.z # Look where camera looks
var target_up = Vector3.UP # Default up initially
func _process_auto_orientation(state: PhysicsDirectBodyState3D):
# This function runs every physics frame
if not _is_auto_orienting:
return # Not orienting, do nothing
# --- THE FIX: Adjust how target_up is calculated ---
# Calculate velocity components relative to camera orientation
var _forward_velocity_component = pawn.velocity.dot(target_forward)
var _right_velocity_component = pawn.velocity.dot(pawn.camera_anchor.global_basis.x)
# 2. Calculate Torque using PD Controller
var torque = MotionUtils.calculate_pd_rotation_torque(
_auto_orient_target,
state.transform.basis,
state.angular_velocity, # Read from state
orientation_speed, # Kp
2 * sqrt(orientation_speed) # Kd (Critically Damped)
)
# Only apply strong "feet trailing" if significant forward/backward movement dominates
# and we are actually moving.
#if abs(forward_velocity_component) > abs(right_velocity_component) * 0.5 and velocity.length_squared() > 0.1:
#target_up = -velocity.normalized()
## Orthogonalize to prevent basis skew
#var target_right = target_up.cross(target_forward).normalized()
## If vectors are parallel, cross product is zero. Fallback needed.
#if target_right.is_zero_approx():
#target_up = transform.basis.y # Fallback to current up
#else:
#target_up = target_forward.cross(target_right).normalized()
#else:
## If primarily strafing or stationary relative to forward,
## maintain the current body's roll orientation (its local Y-axis).
target_up = pawn.transform.basis.y
# 2. Apply the torque to the physics state
state.apply_torque(torque)
# Create the target basis
var target_basis = Basis.looking_at(target_forward, target_up)
# 3. Check for stop condition
var ang_vel_mag = state.angular_velocity.length()
var axis = state.angular_velocity.normalized()
# Optional Pitch Offset (Experimental):
# Apply the desired 70-degree pitch relative to the forward direction
# var target_pitch_rad = deg_to_rad(target_body_pitch_degrees)
# target_basis = target_basis.rotated(target_basis.x, target_pitch_rad) # Rotate around the target right vector
# If we are close enough AND slow enough, stop.
if ang_vel_mag < auto_orient_stop_velocity_threshold:
_is_auto_orienting = false
_auto_orient_target = pawn.global_basis # Set target to current for next time
# 2. Smoothly Interpolate Towards Target Basis
var current_basis = pawn.global_basis
var new_basis = current_basis.slerp(target_basis, delta * orientation_speed).get_rotation_quaternion()
if axis.is_normalized():
var physics_rotation = Basis().rotated(axis, ang_vel_mag * state.step)
# Store the body's yaw *before* applying the new basis
var _old_body_yaw = current_basis.get_euler().y
var _old_body_pitch = current_basis.get_euler().x
pawn.camera_anchor.transform.basis = physics_rotation.inverse() * pawn.camera_anchor.transform.basis
# 3. Apply the new orientation
pawn.global_basis = new_basis
# 4. Reset camera pivot to rotation to what it was before we rotated the parent
pawn.camera_anchor.global_basis = initial_cam_basis
# --- Add new function placeholder ---
# TODO: Implement Rotation Stabilization Logic
func _apply_stabilization_torques(_delta: float):
func _apply_stabilization_torques(_state: PhysicsDirectBodyState3D):
if not is_instance_valid(stabilization_target):
stabilization_enabled = false
return
@ -176,7 +136,7 @@ func _apply_stabilization_torques(_delta: float):
# - Proportional Term (based on orientation error): P = rotational_error * stabilization_kp
# - Derivative Term (based on relative spin): D = relative_angular_velocity * stabilization_kd
# - Required Torque = -(P + D) # Negative to counteract error/spin
var required_torque = -(rotational_error * stabilization_kp + relative_angular_velocity * stabilization_kd)
var required_torque = - (rotational_error * stabilization_kp + relative_angular_velocity * stabilization_kd)
print("Applying stabilization torque: ", required_torque)
# 4. Convert Required Torque into Thruster Actions:
@ -186,7 +146,27 @@ func _apply_stabilization_torques(_delta: float):
# - Apply the forces/torques (similar to how _apply_floating_movement applies roll torque).
# Example (highly simplified, assumes direct torque application possible):
# angular_velocity += (required_torque / inertia) * delta
# --- Old logic for feet trailing (commented out) ---
# --- THE FIX: Adjust how target_up is calculated ---
# Calculate velocity components relative to camera orientation
# var _forward_velocity_component = pawn.velocity.dot(target_forward)
# var _right_velocity_component = pawn.velocity.dot(pawn.camera_anchor.global_basis.x)
# Only apply strong "feet trailing" if significant forward/backward movement dominates
# and we are actually moving.
#if abs(forward_velocity_component) > abs(right_velocity_component) * 0.5 and velocity.length_squared() > 0.1:
#target_up = -velocity.normalized()
## Orthogonalize to prevent basis skew
#var target_right = target_up.cross(target_forward).normalized()
## If vectors are parallel, cross product is zero. Fallback needed.
#if target_right.is_zero_approx():
#target_up = transform.basis.y # Fallback to current up
#else:
#target_up = target_forward.cross(target_right).normalized()
#else:
## If primarily strafing or stationary relative to forward,
## maintain the current body's roll orientation (its local Y-axis).
# --- Add methods for enabling/disabling stabilization, setting target etc. ---
func set_stabilization_enabled(enable: bool):

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=3 format=3 uid="uid://bm1rbv4tuppbc"]
[ext_resource type="Script" uid="uid://d4jka2etva22s" path="res://scenes/character/eva_movement_component.gd" id="1_mb22m"]
[sub_resource type="BoxMesh" id="BoxMesh_rlk1u"]
size = Vector3(1, 1, 0.4)
[node name="EVASuitController" type="Node3D" unique_id=241443807]
script = ExtResource("1_mb22m")
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1434211019]
mesh = SubResource("BoxMesh_rlk1u")

View File

@ -8,6 +8,7 @@ class_name PlayerController3D
@export var mouse_sensitivity: float = 0.002 # Radians per pixel motion
var _mouse_motion_input: Vector2 = Vector2.ZERO
class KeyInput:
var pressed: bool = false
var held: bool = false
@ -18,9 +19,26 @@ class KeyInput:
held = _h
released = _r
func _to_dict():
return {
"pressed": pressed,
"held": held,
"released": released
}
static func from_dict(dict: Dictionary) -> KeyInput:
return KeyInput.new(dict.get("pressed", false), dict.get("held", false), dict.get("released", false))
func _ready():
# If we are spawned dynamically, the owner_id might be set by GameManager.
# Fallback: assume the pawn's name is the player ID (common pattern).
if get_parent().name.is_valid_int():
set_multiplayer_authority(int(get_parent().name))
func _unhandled_input(event: InputEvent):
# Check if THIS client is the owner of this controller
if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn):
# print("Peer ID: %s, Node Authority: %s" % [multiplayer.get_unique_id(), get_multiplayer_authority()])
return
# Handle mouse motion input directly here
@ -28,6 +46,7 @@ func _unhandled_input(event: InputEvent):
_mouse_motion_input += Vector2(event.relative.x, -event.relative.y)
func _physics_process(_delta):
# Check if THIS client is the owner
if not is_multiplayer_authority() or not is_instance_valid(possessed_pawn):
return
@ -36,7 +55,7 @@ func _physics_process(_delta):
var sensitivity_modified_mouse_input = Vector2(_mouse_motion_input.x, _mouse_motion_input.y) * mouse_sensitivity
# Send rotation input via RPC immediately
server_process_rotation_input.rpc_id(multiplayer.get_unique_id(), sensitivity_modified_mouse_input)
server_process_rotation_input.rpc_id(1, sensitivity_modified_mouse_input)
# Reset the buffer
_mouse_motion_input = Vector2.ZERO
@ -50,29 +69,29 @@ func _physics_process(_delta):
var l_input = KeyInput.new(Input.is_action_just_pressed("left_click"), Input.is_action_pressed("left_click"), Input.is_action_just_released("left_click"))
var r_input = KeyInput.new(Input.is_action_just_pressed("right_click"), Input.is_action_pressed("right_click"), Input.is_action_just_released("right_click"))
server_process_movement_input.rpc_id(multiplayer.get_unique_id(), move_vec, roll_input, vertical_input)
server_process_interaction_input.rpc_id(multiplayer.get_unique_id(), interact_input)
server_process_clicks.rpc_id(multiplayer.get_unique_id(), l_input, r_input)
server_process_movement_input.rpc_id(1, move_vec, roll_input, vertical_input)
server_process_interaction_input.rpc_id(1, interact_input._to_dict())
server_process_clicks.rpc_id(1, l_input._to_dict(), r_input._to_dict())
@rpc("authority", "call_local")
@rpc("any_peer", "call_local")
func server_process_movement_input(move: Vector2, roll: float, vertical: float):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_movement_input(move, roll, vertical)
@rpc("authority", "call_local")
func server_process_interaction_input(interact_input: KeyInput):
@rpc("any_peer", "call_local")
func server_process_interaction_input(interact_input: Dictionary):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_interaction_input(interact_input)
possessed_pawn.set_interaction_input(KeyInput.from_dict(interact_input))
@rpc("authority", "call_local")
@rpc("any_peer", "call_local")
func server_process_rotation_input(input: Vector2):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_rotation_input(input)
@rpc("authority", "call_local")
func server_process_clicks(l_action: KeyInput, r_action: KeyInput):
@rpc("any_peer", "call_local")
func server_process_clicks(l_action: Dictionary, r_action: Dictionary):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_click_input(l_action, r_action)
possessed_pawn.set_click_input(KeyInput.from_dict(l_action), KeyInput.from_dict(r_action))
func possess(pawn_to_control: CharacterPawn3D):
possessed_pawn = pawn_to_control
@ -87,7 +106,7 @@ func _notification(what):
NOTIFICATION_WM_WINDOW_FOCUS_IN:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
NOTIFICATION_EXIT_TREE:
print("PlayerController %s exited tree" % multiplayer.get_unique_id())
print("PlayerController exited tree")
NOTIFICATION_ENTER_TREE:
print("PlayerController %s entered tree" % multiplayer.get_unique_id())

Some files were not shown because too many files have changed in this diff Show More