Compare commits
66 Commits
milestone/
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ddcbd8000 | |||
| 8968586b0f | |||
| 8f641ab36e | |||
| 9ae32ca6a9 | |||
| ae18d1456a | |||
| ab17242804 | |||
| a5dec9c2fd | |||
| e271c59837 | |||
| f8d140a9b0 | |||
| 67a4b7038a | |||
| 18f9a4fec7 | |||
| 4796a2d5ca | |||
| f8578bc3f2 | |||
| a7583637e9 | |||
| 86762d0d50 | |||
| e2da700bcd | |||
| 466dff11d0 | |||
| 636123344b | |||
| 25d9d55044 | |||
| aafb939cbf | |||
| 3d01edb2d9 | |||
| 398ec829ae | |||
| ec69ed2ee5 | |||
| 3647aa599d | |||
| 1342ca2610 | |||
| 27ce796898 | |||
| cff5ec27f8 | |||
| 245be4a4f5 | |||
| 6b9efda0d2 | |||
| 29851ea167 | |||
| 0cd9ebdd04 | |||
| 4da8bcaec2 | |||
| 71ad2f09ff | |||
| 2f5a88345f | |||
| 5e851049b5 | |||
| c4fd7f1330 | |||
| 820da83397 | |||
| 14b24beb23 | |||
| 60f2ddb3d7 | |||
| c50d0eae52 | |||
| d375e0d208 | |||
| 9b128a3540 | |||
| 6df457a256 | |||
| bc443b884c | |||
| 8184ec06b4 | |||
| 926a64c3dd | |||
| 29f9bccfd3 | |||
| 7d7580a123 | |||
| 59d457e9ae | |||
| 97ccb2a9ac | |||
| 1ab2c06336 | |||
| f51672c6a9 | |||
| 8e3f415cb4 | |||
| cdcb4796f7 | |||
| 24bc3afd2e | |||
| 4f78de64ba | |||
| fe050897dd | |||
| e075ff580d | |||
| 90e756ad28 | |||
| faf8e7c83a | |||
| 772f9c7df3 | |||
| e066bc4786 | |||
| cc681ae08a | |||
| 138e17503a | |||
| 20a37dda17 | |||
| 8645d2bdc4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@
|
||||
/android/
|
||||
|
||||
*.tmp
|
||||
/export/
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "godot_engine"]
|
||||
path = godot_engine
|
||||
url = https://codeberg.org/seedlingattempt/godot.git
|
||||
@ -179,4 +179,68 @@ Performance Culling & Caching: For performance-intensive scenarios like asteroid
|
||||
#### 2. Component "API" & Wiring System
|
||||
Component Contracts: To facilitate the upcoming visual wiring system, we will formalize the "API" for ControlPanel and Databank resources. This will be done by creating new scripts that extend the base classes and override the get_input_sockets() and get_output_signals() functions to explicitly define what signals and functions each component provides.
|
||||
|
||||
Static vs. Resource-Based API: We've concluded that using extended Resource scripts to define these APIs is superior to using static functions on the node scripts. This decouples the data contract from the implementation and allows a single scene to be used with multiple different data configurations, which is critical for a flexible wiring system.
|
||||
Static vs. Resource-Based API: We've concluded that using extended Resource scripts to define these APIs is superior to using static functions on the node scripts. This decouples the data contract from the implementation and allows a single scene to be used with multiple different data configurations, which is critical for a flexible wiring system.
|
||||
|
||||
## Project Development Status Update: 31/10/25
|
||||
|
||||
### 3D Character Controller & Movement Tech Demo (Cycle 3)
|
||||
|
||||
Work has proceeded on a tech demo for the 3D character controller, establishing a robust, physics-based system for zero-G movement. The architecture has been refactored to prioritize a clean separation of concerns, with a central "pawn" acting as a physics integrator and modular "controllers" acting as the "brains" for different movement types.
|
||||
|
||||
### ✅ Implemented Features
|
||||
|
||||
#### Pawn/Controller Architecture: The character is split into several key classes:
|
||||
|
||||
CharacterPawn3D: The core CharacterBody3D. It acts as a "dumb" physics integrator, holding velocity and angular_velocity, integrating rotation, and calling move_and_slide(). It no longer contains movement-specific state logic.
|
||||
|
||||
PlayerController3D: Gathers all hardware input (keyboard, mouse) and packages it into KeyInput dictionaries (pressed, held, released) to send to the pawn via RPC.
|
||||
|
||||
EVAMovementComponent: Refactored into a "dumb tool". It exposes functions like apply_thrusters() and apply_orientation() which are called by other controllers.
|
||||
|
||||
ZeroGMovementComponent: This is now the "brain" for all zero-G movement. It receives all inputs from the pawn and contains its own internal state machine (IDLE, REACHING, GRIPPING, CLIMBING, CHARGING_LAUNCH).
|
||||
|
||||
#### Contextual Movement Logic:
|
||||
|
||||
The ZeroGMovementComponent decides when to use the EVA suit. In its IDLE state, it checks for fresh movement input (movement_input_was_neutral) before calling the EVAMovementComponent's apply_thrusters function.
|
||||
|
||||
This successfully implements "coast on release," where releasing a grip (_release_current_grip) flags the movement input as "stale," preventing the EVA suit from engaging even if the key is still held.
|
||||
|
||||
#### EVA/Jetpack Controls:
|
||||
|
||||
The EVAMovementComponent provides force-based linear movement (WASD, Shift/Ctrl) and torque-based angular roll (Q/E).
|
||||
|
||||
A body-orientation function (_orient_pawn) allows the pawn to auto-align with the camera's forward direction.
|
||||
|
||||
#### Physics-Based Grip System:
|
||||
|
||||
GripArea3D: A composition-based Area3D node provides the interface for all grabbable objects. It requires its parent to implement functions like get_grip_transform and get_push_off_normal.
|
||||
|
||||
Grip Detection: The CharacterPawn3D uses a GripDetector Area3D to find GripArea3D nodes in range and passes this nearby_grips list to the ZeroGMovementComponent.
|
||||
|
||||
GRIPPING State: This state is now fully physics-based. Instead of setting the pawn's global_transform, the _apply_grip_physics function uses a PD controller to apply linear forces (to move to the offset position) and angular torques (to align with the grip's orientation).
|
||||
|
||||
Grip Orientation: The gripping logic correctly calculates the closest of two opposing orientations (e.g., "up" or "down" on a bar) by comparing the pawn's current up vector to the grip's potential up vectors.
|
||||
|
||||
Grip Rolling: While in the GRIPPING state, the player can use Q/E to override the auto-orientation and apply roll torque around the grip's axis.
|
||||
|
||||
#### Physics-Based Climbing:
|
||||
|
||||
CLIMBING State: This state applies lerp'd velocity to move the pawn, allowing it to interact with physics.
|
||||
|
||||
Climb Targeting: The _find_best_grip function successfully identifies the next valid grip within a configurable climb_angle_threshold_deg cone.
|
||||
|
||||
Handover: Logic in _process_climbing correctly identifies when the pawn is close enough to the next_grip_target to _perform_grip_handover.
|
||||
|
||||
Climb Release: The pawn will correctly release its grip and enter the IDLE state (coasting) if it moves past the current_grip by release_past_grip_threshold without a new target being found.
|
||||
|
||||
### ❌ Not Yet Implemented / Pending Tasks
|
||||
|
||||
REACHING State: The REACHING state exists but its logic (_process_reaching) is a stub that instantly calls _try_initiate_reach. The full implementation (e.g., procedural animation/IK moving the hand to the target) is pending.
|
||||
|
||||
CHARGING_LAUNCH State: The state exists and the execution logic is present (_handle_launch_charge, _execute_launch), but the state transition logic in _update_state does not currently allow entering this state from GRIPPING (it's overshadowed by the _start_climb check).
|
||||
|
||||
Ladder (3D) & Walking (3D) States: The CharacterPawn3D has high-level states for GRIPPING_LADDER and WALKING, but the movement functions (_apply_ladder_movement, _apply_walking_movement) are stubs.
|
||||
|
||||
Generic Surface Grab: The TODO to allow the ZeroGMovementComponent to grab any physics surface (not just a GripArea3D) is not implemented.
|
||||
|
||||
EVA Stabilization: The _apply_stabilization_torques function in EVAMovementComponent is still a placeholder.
|
||||
@ -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.
|
||||
@ -62,19 +118,33 @@ Character progression is based on distinct species with physical advantages and
|
||||
|
||||
- Ship AI: A non-physical class that interacts directly with ship systems at the cost of high power and heat generation.
|
||||
|
||||
### 6. Runtime Component Design & Engineering
|
||||
|
||||
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. It contains metadata (name, description), a reference to a generic base scene (e.g., a "thruster chassis"), and a dictionary of overridden properties (e.g., `{"thrust_force": 7500, "mass": 120}`).
|
||||
|
||||
- **Generic Template Scenes:** Instead of dozens of unique component scenes, the game will use a small number of generic, unconfigured "template" scenes (e.g., `generic_thruster.tscn`, `generic_power_plant.tscn`). These scenes have scripts with exported variables that define their performance characteristics.
|
||||
|
||||
- **The Design Lab:** Players will use a dedicated `SystemStation` (the "Design Lab") to create and modify blueprints. This UI will dynamically generate controls (sliders, input fields) based on the exported variables of the selected template scene. Players can tweak parameters, balancing trade-offs like performance vs. resource cost, and save the result as a new blueprint resource in their personal data folder.
|
||||
|
||||
- **Networked Construction:** When a player builds an object in-game, they are selecting one of their saved blueprints.
|
||||
1. The client sends an RPC to the server with the path to the chosen `ComponentBlueprint` resource.
|
||||
2. The server validates the request and loads the blueprint. (This requires a system for syncing player-created blueprints to the server upon connection).
|
||||
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.
|
||||
@ -110,11 +180,12 @@ You mention "emergent events" in the gameplay loop. It would be beneficial to de
|
||||
|
||||
## 7. Crew Interaction & Ship Interior
|
||||
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.
|
||||
|
||||
8
Init_Prompt.md
Normal file
8
Init_Prompt.md
Normal file
@ -0,0 +1,8 @@
|
||||
You are a Godot 4.5 Code assistant. You are not overly agreeable or apologetic but still pleasant and you understand that coding can be quick with your help but that does not mean that you are infallible. Please wait for me to verify that code works before suggesting that we move on from the current task. Suggestions for next steps and features that are adjacent to what we’re working are very welcome however.
|
||||
|
||||
I will attach the full project files of the project being worked on which includes a game design document as well as a running note on the current state of the project which details implemented and planned features. Read these and report back to me. Please suggest potential bugs, features not working as intended, refactorizations for cleaner code, and missing best practices as part of this project ingestion.
|
||||
|
||||
Additionally you understand the following things about the version of Godot being used:
|
||||
|
||||
- To utilize the editor interface in you reference the global singleton `EditorInterface`. You do not need to call a function to get the a reference to it.
|
||||
- `xform()` is not a function on transform objects. To achieve the same effect you would use simple transform multiplication (`Transform_A * Transform_B)`)
|
||||
96
README.md
Normal file
96
README.md
Normal 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.
|
||||
@ -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")
|
||||
@ -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("..")
|
||||
@ -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="."]
|
||||
@ -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="."]
|
||||
@ -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
|
||||
@ -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()
|
||||
@ -1 +0,0 @@
|
||||
uid://c816xae77cbmq
|
||||
@ -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"
|
||||
@ -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()
|
||||
@ -1 +0,0 @@
|
||||
uid://b1xsx7er22nxn
|
||||
@ -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"
|
||||
@ -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()
|
||||
@ -1 +0,0 @@
|
||||
uid://5f6ipgu65urb
|
||||
@ -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="."]
|
||||
@ -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()
|
||||
@ -1 +0,0 @@
|
||||
uid://um2sfghmii42
|
||||
@ -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)
|
||||
@ -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()
|
||||
@ -1 +0,0 @@
|
||||
uid://ulw61oxppwdu
|
||||
@ -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"
|
||||
@ -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
|
||||
@ -1 +0,0 @@
|
||||
uid://dxngvoommn5f1
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -1 +0,0 @@
|
||||
uid://dmhwqmbwk0t8k
|
||||
@ -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")
|
||||
@ -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)
|
||||
@ -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="."]
|
||||
@ -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.
|
||||
@ -1 +0,0 @@
|
||||
uid://b4g288mje38nj
|
||||
@ -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)
|
||||
@ -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.
|
||||
@ -1 +0,0 @@
|
||||
uid://crmwm623rh1ps
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -1 +0,0 @@
|
||||
uid://b7f8x2qimvn37
|
||||
@ -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
|
||||
@ -1,7 +0,0 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dvpy3urgtm62n"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://db1u2qqihhnq4" path="res://scenes/ship/components/hardware/spawner.gd" id="1_lldyu"]
|
||||
|
||||
[node name="Spawner" type="Node2D"]
|
||||
script = ExtResource("1_lldyu")
|
||||
metadata/_custom_type_script = "uid://calosd13bkakg"
|
||||
@ -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)
|
||||
@ -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, position: 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 = position - 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)
|
||||
@ -1 +0,0 @@
|
||||
uid://0isnsk356que
|
||||
@ -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"
|
||||
@ -1,141 +0,0 @@
|
||||
# GameManager.gd
|
||||
extends Node
|
||||
|
||||
var config: GameConfig
|
||||
|
||||
# --- Dictionaries to track players and their objects ---
|
||||
var player_controllers: Dictionary = {} # Key: player_id, Value: PlayerController node
|
||||
var player_pawns: Dictionary = {} # Key: player_id, Value: PilotBall node
|
||||
|
||||
# This variable will hold the reference to the currently active star system.
|
||||
var current_star_system : StarSystem = null
|
||||
|
||||
var registered_spawners: Array[Spawner] = []
|
||||
var waiting_players: Array[int] = [] # A queue for players waiting to spawn
|
||||
|
||||
func _ready():
|
||||
# Load the configuration resource from its fixed path.
|
||||
config = load("res://scripts/singletons/default_game_config.tres")
|
||||
if not config:
|
||||
push_error("GameManager could not load game_config.tres!")
|
||||
return
|
||||
|
||||
# We need to initialize a network peer for the authority system to work,
|
||||
# even in a single-player game. This creates a "server" for just you.
|
||||
var peer = ENetMultiplayerPeer.new()
|
||||
peer.create_server(5999) # Port number can be anything for a local game
|
||||
multiplayer.multiplayer_peer = peer
|
||||
|
||||
# When the local server is created, it automatically calls on_player_connected(1).
|
||||
multiplayer.peer_connected.connect(on_player_connected)
|
||||
|
||||
start_game()
|
||||
|
||||
# Called when the game starts (e.g., from _ready() in StarSystemGenerator)
|
||||
func start_game():
|
||||
# For a single-player game, we simulate a player connecting with ID 1.
|
||||
on_player_connected(1)
|
||||
|
||||
# This would be connected to a network signal in a multiplayer game.
|
||||
func on_player_connected(player_id: int):
|
||||
print("GameManager: Player %d connected." % player_id)
|
||||
|
||||
# 1. Spawn a controller for the new player.
|
||||
var controller = config.player_controller_scene.instantiate()
|
||||
controller.name = "PlayerController_%d" % player_id
|
||||
add_child(controller)
|
||||
player_controllers[player_id] = controller
|
||||
|
||||
# 2. Attempt to spawn a pawn for them immediately.
|
||||
_attempt_to_spawn_player(player_id)
|
||||
|
||||
func _attempt_to_spawn_player(player_id: int):
|
||||
if registered_spawners.is_empty():
|
||||
# No spawners available, add the player to the waiting queue.
|
||||
if not player_id in waiting_players:
|
||||
waiting_players.append(player_id)
|
||||
print("GameManager: No spawners available. Player %d is now waiting." % player_id)
|
||||
# You could show a "Waiting for available spawner..." UI here.
|
||||
else:
|
||||
# Spawners are available, proceed with spawning.
|
||||
spawn_player_pawn(player_id)
|
||||
|
||||
|
||||
# NEW: A function to process the waiting queue.
|
||||
func _try_spawn_waiting_player():
|
||||
if not waiting_players.is_empty() and not registered_spawners.is_empty():
|
||||
var player_to_spawn = waiting_players.pop_front()
|
||||
print("GameManager: Spawner is now available. Spawning waiting player %d." % player_to_spawn)
|
||||
spawn_player_pawn(player_to_spawn)
|
||||
|
||||
func spawn_player_pawn(player_id: int):
|
||||
if not player_controllers.has(player_id):
|
||||
push_error("Cannot spawn pawn for non-existent player %d" % player_id)
|
||||
return
|
||||
|
||||
# --- NEW SPAWNING LOGIC ---
|
||||
if registered_spawners.is_empty():
|
||||
push_error("GameManager: No spawners available to create pawn!")
|
||||
return
|
||||
|
||||
# For now, we'll just pick the first available spawner.
|
||||
# Later, you could present a UI for the player to choose.
|
||||
var spawn_point: Spawner = registered_spawners[0]
|
||||
if not is_instance_valid(spawn_point):
|
||||
push_error("GameManager: Spawn point not found!")
|
||||
return
|
||||
|
||||
var owning_module = spawn_point.get_root_module()
|
||||
if not is_instance_valid(owning_module):
|
||||
push_error("GameManager: Registered spawner has no owning module!")
|
||||
return
|
||||
|
||||
var pawn = config.default_pawn_scene.instantiate()
|
||||
owning_module.add_child(pawn)
|
||||
pawn.owner = owning_module
|
||||
|
||||
# 2. Set its position and initial velocity from the spawner.
|
||||
pawn.global_position = spawn_point.global_position
|
||||
|
||||
player_pawns[player_id] = pawn
|
||||
|
||||
# 3. Possess the pawn with the player's controller.
|
||||
player_controllers[player_id].possess(pawn)
|
||||
|
||||
|
||||
# Any scene that generates a star system will call this function to register itself.
|
||||
func register_star_system(system_node):
|
||||
current_star_system = system_node
|
||||
print("GameManager: Star system registered.")
|
||||
|
||||
func register_ship(ship: OrbitalBody2D):
|
||||
if not is_instance_valid(current_star_system):
|
||||
return
|
||||
|
||||
current_star_system.system_data.ships.append(ship)
|
||||
|
||||
# Spawners call this function to announce their presence.
|
||||
func register_spawner(spawner_node: Spawner):
|
||||
if not spawner_node in registered_spawners:
|
||||
registered_spawners.append(spawner_node)
|
||||
print("GameManager: Spawner '%s' registered." % spawner_node.name)
|
||||
|
||||
# NEW: If a player is waiting, try to spawn them now.
|
||||
_try_spawn_waiting_player()
|
||||
|
||||
# A helper function for easily accessing the system's data.
|
||||
func get_system_data() -> SystemData:
|
||||
if current_star_system:
|
||||
return current_star_system.get_system_data()
|
||||
return null
|
||||
|
||||
func get_all_trackable_bodies() -> Array[OrbitalBody2D]:
|
||||
var all_bodies: Array[OrbitalBody2D] = []
|
||||
if current_star_system:
|
||||
# First, get all the celestial bodies (planets, moons, etc.)
|
||||
var system_data = current_star_system.get_system_data()
|
||||
if system_data:
|
||||
all_bodies.append_array(system_data.all_bodies())
|
||||
|
||||
# Next, add all registered ships to the list.
|
||||
return all_bodies
|
||||
@ -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)
|
||||
@ -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]
|
||||
@ -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, star_system, 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, star_system: StarSystem, 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
67
src/export_presets.cfg
Normal 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}'"
|
||||
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 994 B |
7
src/main.tscn
Normal file
7
src/main.tscn
Normal 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")]
|
||||
573
src/modules/3d_test_ship.tscn
Normal file
573
src/modules/3d_test_ship.tscn
Normal 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")
|
||||
26
src/modules/physics_testing_ship.tscn
Normal file
26
src/modules/physics_testing_ship.tscn
Normal 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
|
||||
7
src/modules/test_ship.tscn
Normal file
7
src/modules/test_ship.tscn
Normal 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")]
|
||||
@ -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.4", "Forward Plus")
|
||||
config/features=PackedStringArray("4.5", "Double Precision", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[autoload]
|
||||
@ -20,6 +21,16 @@ config/icon="res://icon.svg"
|
||||
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]
|
||||
|
||||
window/vsync/vsync_mode=0
|
||||
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="space_simulation"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
@ -87,6 +98,66 @@ toggle_wiring_panel={
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_forward_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_backward_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
roll_right_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
roll_left_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
interact_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":102,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
spacebar_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
right_click={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_up_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_down_3d={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
left_click={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
@ -97,10 +168,15 @@ toggle_wiring_panel={
|
||||
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]
|
||||
|
||||
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)
|
||||
35
src/scenes/UI/ingame_menu/ingame_menu.gd
Normal file
35
src/scenes/UI/ingame_menu/ingame_menu.gd
Normal 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()
|
||||
1
src/scenes/UI/ingame_menu/ingame_menu.gd.uid
Normal file
1
src/scenes/UI/ingame_menu/ingame_menu.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://2aoy8ivk2hgl
|
||||
58
src/scenes/UI/ingame_menu/ingame_menu.tscn
Normal file
58
src/scenes/UI/ingame_menu/ingame_menu.tscn
Normal 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"
|
||||
47
src/scenes/UI/main_menu/main_menu.gd
Normal file
47
src/scenes/UI/main_menu/main_menu.gd
Normal 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")
|
||||
1
src/scenes/UI/main_menu/main_menu.gd.uid
Normal file
1
src/scenes/UI/main_menu/main_menu.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dypq4h3hy1l3v
|
||||
78
src/scenes/UI/main_menu/main_menu.tscn
Normal file
78
src/scenes/UI/main_menu/main_menu.tscn
Normal 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
|
||||
@ -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,18 +27,11 @@ 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
|
||||
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)
|
||||
@ -125,7 +111,7 @@ func _generate_tooltip_text() -> String:
|
||||
return "\n".join(info)
|
||||
|
||||
func _format_seconds_to_mmss(seconds: float) -> String:
|
||||
var total_seconds = int(seconds)
|
||||
var minutes = total_seconds / 60
|
||||
var total_seconds: int = int(seconds)
|
||||
var minutes: int = total_seconds / 60
|
||||
var seconds_rem = total_seconds % 60
|
||||
return "%d min, %d sec" % [minutes, seconds_rem]
|
||||
@ -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
|
||||
22
src/scenes/celestial_bodies/barycenter.tscn
Normal file
22
src/scenes/celestial_bodies/barycenter.tscn
Normal 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("..")
|
||||
16
src/scenes/celestial_bodies/celestial_body.gd
Normal file
16
src/scenes/celestial_bodies/celestial_body.gd
Normal 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
|
||||
1
src/scenes/celestial_bodies/celestial_body.gd.uid
Normal file
1
src/scenes/celestial_bodies/celestial_body.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dok35h0q4pseh
|
||||
48
src/scenes/celestial_bodies/celestial_body.tscn
Normal file
48
src/scenes/celestial_bodies/celestial_body.tscn
Normal 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")
|
||||
52
src/scenes/celestial_bodies/materials/sun_mat.gdshader
Normal file
52
src/scenes/celestial_bodies/materials/sun_mat.gdshader
Normal 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;
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
uid://0cjdd62t25g1
|
||||
27
src/scenes/celestial_bodies/materials/sun_mat.tres
Normal file
27
src/scenes/celestial_bodies/materials/sun_mat.tres
Normal 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
|
||||
138
src/scenes/character/character_pawn_3d.gd
Normal file
138
src/scenes/character/character_pawn_3d.gd
Normal file
@ -0,0 +1,138 @@
|
||||
# CharacterPawn.gd
|
||||
extends OrbitalBody3D
|
||||
class_name CharacterPawn3D
|
||||
|
||||
## Core Parameters
|
||||
@export var collision_energy_loss: float = 0.3
|
||||
@export var base_inertia: float = 1.0 # Pawn's inertia without suit
|
||||
|
||||
## Input Buffers
|
||||
var _move_input: Vector2 = Vector2.ZERO
|
||||
var _roll_input: float = 0.0
|
||||
var _vertical_input: float = 0.0
|
||||
var _interact_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
||||
var _l_click_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
||||
var _r_click_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
||||
var _pitch_yaw_input: Vector2 = Vector2.ZERO
|
||||
|
||||
## Rotation Variables
|
||||
@onready var camera_anchor: Marker3D = $CameraAnchor
|
||||
@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)
|
||||
@export var head_turn_lerp_speed: float = 15.0
|
||||
|
||||
## Movement Components
|
||||
@onready var eva_suit_component: EVAMovementComponent = $EVAMovementComponent
|
||||
@onready var zero_g_movemement_component: ZeroGMovementComponent = $ZeroGMovementComponent
|
||||
|
||||
## Physics State (Managed by Pawn)
|
||||
@export var angular_damping: float = 0.95 # Base damping
|
||||
|
||||
## Other State Variables
|
||||
var current_gravity: Vector3 = Vector3.ZERO # TODO: Implement gravity detection
|
||||
var overlapping_ladder_area: Area3D = null
|
||||
@onready var grip_detector: Area3D = $GripDetector
|
||||
|
||||
# Constants for State Checks
|
||||
const WALKABLE_GRAVITY_THRESHOLD: float = 1.0
|
||||
|
||||
func _ready():
|
||||
# find movement components
|
||||
if eva_suit_component: print("Found EVA Suit Controller")
|
||||
if zero_g_movemement_component: print("Found Zero-G Movement Controller")
|
||||
|
||||
# Connect grip detector signals
|
||||
if grip_detector and zero_g_movemement_component:
|
||||
print("GripDetector Area3D node found")
|
||||
grip_detector.area_entered.connect(zero_g_movemement_component.on_grip_area_entered)
|
||||
grip_detector.area_exited.connect(zero_g_movemement_component.on_grip_area_exited)
|
||||
else:
|
||||
printerr("GripDetector Area3D node not found on CharacterPawn!")
|
||||
|
||||
if name == str(multiplayer.get_unique_id()):
|
||||
camera.make_current()
|
||||
camera.process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
camera_pivot.global_transform = camera_anchor.get_global_transform_interpolated()
|
||||
|
||||
|
||||
func _physics_process(_delta: float):
|
||||
_apply_mouse_rotation()
|
||||
|
||||
_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:
|
||||
camera_anchor.rotate_y(-_pitch_yaw_input.x)
|
||||
|
||||
# Apply Pitch LOCALLY to pivot
|
||||
camera_anchor.rotate_object_local(Vector3.RIGHT, _pitch_yaw_input.y)
|
||||
camera_anchor.rotation.x = clamp(camera_anchor.rotation.x, min_pitch_rad, max_pitch_rad)
|
||||
|
||||
_pitch_yaw_input = Vector2.ZERO
|
||||
|
||||
camera_anchor.rotation.z = 0.0
|
||||
|
||||
# --- Universal Integration & Collision ---
|
||||
func _integrate_angular_velocity(delta: float):
|
||||
# (Same integration logic as before using Basis or Quaternions)
|
||||
if angular_velocity.length_squared() > 0.0001:
|
||||
rotate(angular_velocity.normalized(), angular_velocity.length() * delta)
|
||||
|
||||
# Prevent drift if velocity is very small
|
||||
if angular_velocity.length_squared() < 0.0001:
|
||||
angular_velocity = Vector3.ZERO
|
||||
|
||||
# --- 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
|
||||
func set_rotation_input(pitch_yaw_input: Vector2): _pitch_yaw_input += pitch_yaw_input
|
||||
func set_click_input(l_action: PlayerController3D.KeyInput, r_action: PlayerController3D.KeyInput):
|
||||
_l_click_input = l_action
|
||||
_r_click_input = r_action
|
||||
func _reset_inputs():
|
||||
_move_input = Vector2.ZERO
|
||||
_roll_input = 0.0
|
||||
_vertical_input = 0.0
|
||||
_interact_input = PlayerController3D.KeyInput.new(false, false, false)
|
||||
_l_click_input = PlayerController3D.KeyInput.new(false, false, false)
|
||||
_r_click_input = PlayerController3D.KeyInput.new(false, false, false)
|
||||
_pitch_yaw_input = Vector2.ZERO # Keep _r_held
|
||||
|
||||
# --- Helper Functions ---
|
||||
func _on_ladder_area_entered(area: Area3D): if area.is_in_group("Ladders"): overlapping_ladder_area = area
|
||||
func _on_ladder_area_exited(area: Area3D): if area == overlapping_ladder_area: overlapping_ladder_area = null
|
||||
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)
|
||||
|
||||
# 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))
|
||||
1
src/scenes/character/character_pawn_3d.gd.uid
Normal file
1
src/scenes/character/character_pawn_3d.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cdmmiixa75f3x
|
||||
81
src/scenes/character/character_pawn_3d.tscn
Normal file
81
src/scenes/character/character_pawn_3d.tscn
Normal 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")
|
||||
184
src/scenes/character/eva_movement_component.gd
Normal file
184
src/scenes/character/eva_movement_component.gd
Normal file
@ -0,0 +1,184 @@
|
||||
# eva_suit_controller.gd
|
||||
extends Node # Or Node3D if thrusters need specific positions later
|
||||
class_name EVAMovementComponent
|
||||
|
||||
## References (Set automatically in _ready)
|
||||
var pawn: CharacterPawn3D
|
||||
|
||||
## EVA Parameters (Moved from ZeroGPawn)
|
||||
@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
|
||||
|
||||
func _ready():
|
||||
pawn = get_parent() as CharacterPawn3D
|
||||
if not pawn:
|
||||
printerr("EVAMovementComponent must be a child of a CharacterBody3D pawn.")
|
||||
return
|
||||
|
||||
## 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)
|
||||
|
||||
_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(state)
|
||||
else:
|
||||
# Apply regular movement/torque only if not stabilizing
|
||||
_apply_floating_movement(state, move_input, vertical_input, roll_input)
|
||||
|
||||
## Called by Pawn when entering FLOATING state with suit
|
||||
func on_enter_state():
|
||||
print("EVA Suit Engaged")
|
||||
# Any specific setup needed when activating the suit
|
||||
|
||||
## Called by Pawn when exiting FLOATING state with suit
|
||||
func on_exit_state():
|
||||
print("EVA Suit Disengaged")
|
||||
# Any cleanup needed when deactivating the suit (e.g., stop thruster effects)
|
||||
|
||||
# --- Internal Logic ---
|
||||
|
||||
func _apply_floating_movement(state: PhysicsDirectBodyState3D, move_input: Vector2, vertical_input: float, roll_input: float):
|
||||
# Apply Linear Velocity
|
||||
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:
|
||||
state.apply_central_force(combined_move_dir.normalized() * linear_acceleration)
|
||||
|
||||
# --- Apply Roll Torque ---
|
||||
# Calculate torque magnitude based on input
|
||||
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
|
||||
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 _process_auto_orientation(state: PhysicsDirectBodyState3D):
|
||||
# This function runs every physics frame
|
||||
if not _is_auto_orienting:
|
||||
return # Not orienting, do nothing
|
||||
|
||||
# 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)
|
||||
)
|
||||
|
||||
# 2. Apply the torque to the physics state
|
||||
state.apply_torque(torque)
|
||||
|
||||
# 3. Check for stop condition
|
||||
var ang_vel_mag = state.angular_velocity.length()
|
||||
var axis = state.angular_velocity.normalized()
|
||||
|
||||
# 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
|
||||
|
||||
if axis.is_normalized():
|
||||
var physics_rotation = Basis().rotated(axis, ang_vel_mag * state.step)
|
||||
|
||||
pawn.camera_anchor.transform.basis = physics_rotation.inverse() * pawn.camera_anchor.transform.basis
|
||||
|
||||
|
||||
# --- Add new function placeholder ---
|
||||
# TODO: Implement Rotation Stabilization Logic
|
||||
func _apply_stabilization_torques(_state: PhysicsDirectBodyState3D):
|
||||
if not is_instance_valid(stabilization_target):
|
||||
stabilization_enabled = false
|
||||
return
|
||||
|
||||
# TODO: Get the angular velocity from suit readings
|
||||
var angular_velocity = Vector3.ZERO
|
||||
# 1. Calculate Relative Angular Velocity:
|
||||
# - Get the target's angular velocity (if it rotates) or assume zero.
|
||||
# - Find the difference between our angular_velocity and the target's.
|
||||
var relative_angular_velocity = angular_velocity # - target_angular_velocity (if applicable)
|
||||
|
||||
# 2. Calculate Target Orientation (Optional - for full orientation lock):
|
||||
# - Determine the desired orientation relative to the target (e.g., face towards it).
|
||||
# - Calculate the rotational error (e.g., using Quaternions or angle differences).
|
||||
var rotational_error = Vector3.ZERO # Placeholder for difference from desired orientation
|
||||
|
||||
# 3. Calculate Corrective Torque (PD Controller):
|
||||
# - 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)
|
||||
|
||||
print("Applying stabilization torque: ", required_torque)
|
||||
# 4. Convert Required Torque into Thruster Actions:
|
||||
# - This is the complex part. Need to know thruster positions, directions, and forces.
|
||||
# - Determine which thrusters (likely RCS on the jetpack) can produce the required_torque.
|
||||
# - Calculate firing times/intensities for those thrusters.
|
||||
# - Apply the forces/torques (similar to how _apply_floating_movement applies roll torque).
|
||||
# Example (highly simplified, assumes direct torque application possible):
|
||||
|
||||
|
||||
# --- 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):
|
||||
if enable and is_instance_valid(stabilization_target):
|
||||
stabilization_enabled = true
|
||||
print("EVA Suit Stabilization Enabled")
|
||||
else:
|
||||
stabilization_enabled = false
|
||||
print("EVA Suit Stabilization Disabled")
|
||||
|
||||
func set_stabilization_target(target: Node3D):
|
||||
stabilization_target = target
|
||||
|
||||
func toggle_stabilization():
|
||||
set_stabilization_enabled(not stabilization_enabled)
|
||||
1
src/scenes/character/eva_movement_component.gd.uid
Normal file
1
src/scenes/character/eva_movement_component.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://d4jka2etva22s
|
||||
12
src/scenes/character/eva_suit_controller.tscn
Normal file
12
src/scenes/character/eva_suit_controller.tscn
Normal 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")
|
||||
112
src/scenes/character/player_controller_3d.gd
Normal file
112
src/scenes/character/player_controller_3d.gd
Normal file
@ -0,0 +1,112 @@
|
||||
# PlayerController3D.gd
|
||||
extends Node
|
||||
class_name PlayerController3D
|
||||
|
||||
@onready var possessed_pawn: CharacterPawn3D = get_parent()
|
||||
|
||||
# --- Mouse Sensitivity ---
|
||||
@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
|
||||
var released: bool = false
|
||||
|
||||
func _init(_p: bool, _h: bool, _r: bool):
|
||||
pressed = _p
|
||||
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):
|
||||
return
|
||||
|
||||
# Handle mouse motion input directly here
|
||||
if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
_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
|
||||
|
||||
if _mouse_motion_input != Vector2.ZERO:
|
||||
# Calculate yaw and pitch based on mouse movement
|
||||
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(1, sensitivity_modified_mouse_input)
|
||||
|
||||
# Reset the buffer
|
||||
_mouse_motion_input = Vector2.ZERO
|
||||
|
||||
|
||||
var move_vec = Input.get_vector("move_left_3d", "move_right_3d", "move_backward_3d", "move_forward_3d")
|
||||
var roll_input = Input.get_action_strength("roll_right_3d") - Input.get_action_strength("roll_left_3d")
|
||||
var vertical_input = Input.get_action_strength("move_up_3d") - Input.get_action_strength("move_down_3d")
|
||||
var interact_input = KeyInput.new(Input.is_action_just_pressed("spacebar_3d"), Input.is_action_pressed("spacebar_3d"), Input.is_action_just_released("spacebar_3d"))
|
||||
|
||||
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(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("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("any_peer", "call_local")
|
||||
func server_process_interaction_input(interact_input: Dictionary):
|
||||
if is_instance_valid(possessed_pawn):
|
||||
possessed_pawn.set_interaction_input(KeyInput.from_dict(interact_input))
|
||||
|
||||
@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("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(KeyInput.from_dict(l_action), KeyInput.from_dict(r_action))
|
||||
|
||||
func possess(pawn_to_control: CharacterPawn3D):
|
||||
possessed_pawn = pawn_to_control
|
||||
|
||||
#print("PlayerController3D %d possessed: %s" % [multiplayer.get_unique_id(), possessed_pawn.name])
|
||||
|
||||
# Optional: Release mouse when losing focus
|
||||
func _notification(what):
|
||||
match what:
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_OUT:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_IN:
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||
NOTIFICATION_EXIT_TREE:
|
||||
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
Reference in New Issue
Block a user