diff --git a/package-lock.json b/package-lock.json
index 418b4d6..458b642 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "stellar-lines",
"version": "0.0.0",
"dependencies": {
+ "@angular/cdk": "^19.2.11",
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
@@ -500,7 +501,6 @@
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.11.tgz",
"integrity": "sha512-G568yWIJlnsuS563WxvCofmxc1405+wRQvDGQ32+qWOblJScFkHgr4jeDkZGcyt/r8OudaW0H0/rNeg1dzdnIQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"parse5": "^7.1.2",
"tslib": "^2.3.0"
diff --git a/package.json b/package.json
index 6287245..3249158 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
},
"private": true,
"dependencies": {
+ "@angular/cdk": "^19.2.11",
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 1c9adfa..e94a5be 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,7 +1,7 @@
@if (gameService.showShipInfo) {
-
+
}
@if (gameService.showPlanetInfo) {
-
+
}
\ No newline at end of file
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 1fa9853..9e05a98 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -1,5 +1,4 @@
.game-container {
- position: absolute;
top: 0;
left: 0;
width: 100vw;
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 3559aa4..792e63a 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -5,10 +5,11 @@ import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { GameService } from './service/game.service';
import { ShipDialogComponent } from './components/dialog/ship-dialog/ship-dialog.component';
import { PlanetDialogComponent } from './components/dialog/planet-dialog/planet-dialog.component';
+import { DragDropModule } from '@angular/cdk/drag-drop';
@Component({
selector: 'app-root',
- imports: [MatDialogModule, ShipDialogComponent, PlanetDialogComponent],
+ imports: [MatDialogModule, ShipDialogComponent, PlanetDialogComponent, DragDropModule],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
diff --git a/src/app/components/dialog/planet-dialog/planet-dialog.component.scss b/src/app/components/dialog/planet-dialog/planet-dialog.component.scss
index 3c3bec3..5e1336a 100644
--- a/src/app/components/dialog/planet-dialog/planet-dialog.component.scss
+++ b/src/app/components/dialog/planet-dialog/planet-dialog.component.scss
@@ -2,10 +2,6 @@
display: flex;
flex-direction: column;
position: absolute;
- // top: 24px;
- // left: 24px;
- // width: 240px;
- // height: 240px;
- // background-color: var(--background-color);
- // color: var(--primary-color);
+ top: 24px;
+ left: 24px;
}
\ No newline at end of file
diff --git a/src/app/components/dialog/planet-dialog/planet-dialog.component.ts b/src/app/components/dialog/planet-dialog/planet-dialog.component.ts
index 1415165..44af38f 100644
--- a/src/app/components/dialog/planet-dialog/planet-dialog.component.ts
+++ b/src/app/components/dialog/planet-dialog/planet-dialog.component.ts
@@ -5,10 +5,11 @@ import { CommonModule } from '@angular/common';
import { Good } from '../../../model/goods/good.interface';
import { GoodType } from '../../../model/goods/good-type.enum';
import { GameService } from '../../../service/game.service';
+import {DragDropModule} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-planet-dialog',
- imports: [CommonModule, MatDialogModule],
+ imports: [CommonModule, MatDialogModule, DragDropModule],
templateUrl: './planet-dialog.component.html',
styleUrl: './planet-dialog.component.scss'
})
diff --git a/src/app/components/dialog/ship-dialog/ship-dialog.component.html b/src/app/components/dialog/ship-dialog/ship-dialog.component.html
index 39d2e2e..fcf0112 100644
--- a/src/app/components/dialog/ship-dialog/ship-dialog.component.html
+++ b/src/app/components/dialog/ship-dialog/ship-dialog.component.html
@@ -1,4 +1,4 @@
-
+
@@ -26,6 +26,10 @@
-
Geschwindigkeit:
+ {{ ship.currentSpeed | number:'0.0-0' }} km/s
+
+ -
+
Max Geschwindigkeit:
{{ ship.maxSpeed | number:'0.0-0' }} km/s
-
@@ -58,7 +62,7 @@
-
+
diff --git a/src/app/components/dialog/ship-dialog/ship-dialog.component.scss b/src/app/components/dialog/ship-dialog/ship-dialog.component.scss
index 8d7606f..6c32757 100644
--- a/src/app/components/dialog/ship-dialog/ship-dialog.component.scss
+++ b/src/app/components/dialog/ship-dialog/ship-dialog.component.scss
@@ -1,13 +1,15 @@
:host {
+ position: absolute;
display: flex;
flex-direction: column;
position: absolute;
- // top: 24px;
- // left: 24px;
+ top: 24px;
+ left: 24px;
// width: 240px;
// height: 240px;
// background-color: var(--background-color);
// color: var(--primary-color);
+ z-index: 1;
}
img {
diff --git a/src/app/components/dialog/ship-dialog/ship-dialog.component.ts b/src/app/components/dialog/ship-dialog/ship-dialog.component.ts
index e78d5d0..c528943 100644
--- a/src/app/components/dialog/ship-dialog/ship-dialog.component.ts
+++ b/src/app/components/dialog/ship-dialog/ship-dialog.component.ts
@@ -33,4 +33,9 @@ export class ShipDialogComponent {
}
return ''
}
+
+ onClick(event: MouseEvent) {
+ event.stopPropagation();
+ console.log(event)
+ }
}
diff --git a/src/app/model/planet.model.ts b/src/app/model/planet.model.ts
index 89950d4..d6f0649 100644
--- a/src/app/model/planet.model.ts
+++ b/src/app/model/planet.model.ts
@@ -19,6 +19,9 @@ export class Planet {
demandSecondsBuffer = 30; // Anfrage immer mindestens 30 Sekunden überleben
private productionLevel: Map
= new Map();
+ private dockedShips: Ship[] = [];
+ private dockCapacity: number = 0;
+ private shipsWaitingForDocking: Ship[] = [];
constructor(config: PlanetInit) {
@@ -49,6 +52,9 @@ export class Planet {
});
this.updatePopulation(0)
+ setTimeout(() => {
+ this.dockCapacity = 1;
+ }, 10000)
}
@@ -229,6 +235,53 @@ export class Planet {
return critical;
}
+ shipCanDock(ship: Ship): boolean {
+ if (this.dockedShips.includes(ship)) { return true; } // bereits gedockt
+ /** Dock ist voll */
+ if (this.dockedShips.length >= this.dockCapacity) {
+ if (!this.shipsWaitingForDocking.includes(ship)) {
+ /** Schiff auf Warteliste */
+ this.shipsWaitingForDocking.push(ship);
+ }
+ return false;
+ }
+ if (this.shipsWaitingForDocking.length == 0) {
+ this.dock(ship)
+ return true;
+ } // keine wartenden schiffe
+ /* Dock ist nicht voll, aber es gibt Wartende Schiffe */
+
+ /* in warteschlange pushen */
+ if (!this.shipsWaitingForDocking.includes(ship)) {
+ this.shipsWaitingForDocking.push(ship)
+ } else {
+ /* Schiff ist schon registriert */
+ const first = this.shipsWaitingForDocking.indexOf(ship) == 0; // ist an erster stelle?
+ if (first) {
+ this.dock(ship);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ dock(ship: Ship) {
+ console.log("DOCK:", ship.name)
+ if (!this.dockedShips.includes(ship)) {
+ console.log("DOCK:", ship.name)
+ this.dockedShips.push(ship);
+ this.shipsWaitingForDocking = this.shipsWaitingForDocking.filter(s => s != ship); // aus warteschlange entfernen
+ }
+ }
+
+ undock(ship: Ship) {
+ console.log("undock", ship.name)
+ this.dockedShips = this.dockedShips.filter(d => d != ship);
+ console.log('Angedockt: ',this.dockedShips.map(d => d.name).join(','))
+ console.log('wartend: ',this.shipsWaitingForDocking.map(d => d.name).join(','))
+ }
+
}
export interface PlanetInit {
diff --git a/src/app/model/ship.ts b/src/app/model/ship.ts
index 2c68fc0..319a263 100644
--- a/src/app/model/ship.ts
+++ b/src/app/model/ship.ts
@@ -2,7 +2,7 @@ import { MapScene } from "../scene/map.scene";
import { GameService } from "../service/game.service";
import { PlanetUi } from "../ui/planet.ui";
import { TradeInstance } from "./planet.model";
-import { Ship } from "./ships/ship.model";
+import { FlightMode, Ship } from "./ships/ship.model";
export class ShipUi extends Phaser.Physics.Arcade.Sprite {
private velocity = new Phaser.Math.Vector2(0, 0);
@@ -11,6 +11,10 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
public model: Ship = new Ship();
public gameService: GameService;
+ private orbitAngle: number = 0;
+ private orbitRadius: number = 120;
+ private orbitSpeed: number = Math.PI / 25; // Eine Runde in 2 Sekunden
+
constructor(scene: MapScene, x: number, y: number, gameService: GameService) {
super(scene, x, y, 'ship');
this.gameService = gameService;
@@ -37,6 +41,8 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
});
}
+
+
moveTo(target: PlanetUi | undefined) {
if (!target) { return; }
this.target = target;
@@ -46,49 +52,109 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
}
override update(time: number, delta: number): void {
+ if (!this.target) { return; }
if (!this.targetVector) return;
if (!(this.body instanceof Phaser.Physics.Arcade.Body)) { return; }
const dt = delta / 1000; // ms → Sekunden
+ const from = this.getWorldPoint();
+ const to = this.target.getWorldPoint();
+ const distance = Phaser.Math.Distance.Between(from.x, from.y, to.x, to.y);
- const toTarget = new Phaser.Math.Vector2(this.targetVector.x - this.getWorldPoint().x, this.targetVector.y - this.getWorldPoint().y);
- const distance = toTarget.length();
-
- this.setRotation(this.velocity.angle() + Phaser.Math.DegToRad(180)); // Zeigt Schiff zur Zielrichtung
-
- if (distance < 4) {
- this.body.setVelocity(0, 0);
- this.velocity.set(0, 0);
- if (this.targetVector && this.target) {
- this.targetReached(this.target);
- }
- this.targetVector = null;
- return;
+ switch (this.model.flightMode) {
+ case FlightMode.Normal:
+ if (distance < 150) {
+ this.startOrbit(this.target);
+ return;
+ }
+ this.flyDirectlyToTarget(to, dt);
+ break;
+ case FlightMode.Orbiting:
+ this.updateOrbit(to, dt);
+ break;
+ case FlightMode.Docking:
+ this.flyDirectlyToTarget(to, dt);
+ if (distance < 5) {
+ this.land();
+ }
+ break;
+ case FlightMode.Undocking:
+ if (distance > 160) {
+ this.model.flightMode = FlightMode.Normal;
+ }
}
+
+ }
- // Richtung zum Ziel
- const desiredDirection = toTarget.normalize();
+ startOrbit(planet: PlanetUi) {
+ this.model.flightMode = FlightMode.Orbiting;
+ const from = this.getWorldPoint();
+ const to = planet.getWorldPoint();
- // Zielgeschwindigkeit abhängig von Entfernung (langsamer bremsen)
- const targetSpeed = (distance < this.model.slowDownRadius)
- ? this.model.maxSpeed * (distance / this.model.slowDownRadius)
- : this.model.maxSpeed;
-
- const desiredVelocity = desiredDirection.scale(targetSpeed);
-
- // Steuerung per "Lerp" zur Zielgeschwindigkeit (weiches Beschleunigen)
- this.velocity.lerp(desiredVelocity, this.model.acceleration * dt / this.model.maxSpeed);
-
- this.body.setVelocity(this.velocity.x, this.velocity.y);
+ // Starte Orbit am aktuellen Winkel zum Planeten
+ this.orbitAngle = Phaser.Math.Angle.Between(to.x, to.y, from.x, from.y);
}
+ flyDirectlyToTarget(target: any, dt: any) {
+ if (!(this.body instanceof Phaser.Physics.Arcade.Body)) { return; }
+ const angle = Phaser.Math.Angle.Between(this.getWorldPoint().x, this.getWorldPoint().y, target.x, target.y);
+
+ // Sanfte Beschleunigung
+ this.model.currentSpeed = Phaser.Math.Clamp(this.model.currentSpeed + this.model.acceleration * dt, 0, this.model.maxSpeed);
+
+ this.velocity.set(Math.cos(angle) * this.model.currentSpeed, Math.sin(angle) * this.model.currentSpeed);
+ this.body.setVelocity(this.velocity.x, this.velocity.y);
+ this.setRotation(this.velocity.angle() + Phaser.Math.DegToRad(180));
+ }
+
+ private updateOrbit(center: Phaser.Math.Vector2, dt: number): void {
+ if (!(this.body instanceof Phaser.Physics.Arcade.Body)) { return; }
+ this.orbitAngle += this.orbitSpeed * dt;
+
+ const targetX = center.x + Math.cos(this.orbitAngle) * this.orbitRadius;
+ const targetY = center.y + Math.sin(this.orbitAngle) * this.orbitRadius;
+
+ const desiredVelocityX = (targetX - this.getWorldPoint().x) * 5;
+ const desiredVelocityY = (targetY - this.getWorldPoint().y) * 5;
+ this.body.setVelocity(desiredVelocityX, desiredVelocityY);
+
+ this.setRotation(this.body.velocity.angle() + Phaser.Math.DegToRad(180));
+
+ // Umdrehungen zählen
+ if (this.orbitAngle >= Math.PI * 2) {
+ this.orbitAngle -= Math.PI * 2;
+ }
+
+ if (this.target?.model.shipCanDock(this.model)) {
+ this.startDocking();
+ }
+ }
+
+ private startDocking() {
+ this.model.flightMode = FlightMode.Docking;
+ // this.currentSpeed = 0;
+ }
+
+ private land() {
+ if (!(this.body instanceof Phaser.Physics.Arcade.Body)) { return; }
+ if (!this.target) { return; }
+ this.body.setVelocity(0, 0);
+ this.velocity.set(0, 0);
+ this.model.flightMode = FlightMode.Docked;
+ this.targetReached(this.target); // Deine Dock-Funktion
+ this.model.currentSpeed = 0;
+ }
+
async targetReached(target: PlanetUi) {
+
await this.model.exchangeGoods(target.model)
if (this.model.route) {
this.model.route.routePointReached();
- const target = (this.scene as MapScene).getPlanetUiByName(this.model.route.nextPlanetName);
+ const newTarget = (this.scene as MapScene).getPlanetUiByName(this.model.route.nextPlanetName);
setTimeout(() => {
- this.moveTo(target);
+ this.model.flightMode = FlightMode.Undocking;
+ this.model.undock(target.model)
+ this.moveTo(newTarget);
}, 1000)
}
}
@@ -122,4 +188,7 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
this.moveTo(planet)
}
-}
\ No newline at end of file
+
+}
+
+
diff --git a/src/app/model/ships/ship.model.ts b/src/app/model/ships/ship.model.ts
index 458033d..2e9e3b4 100644
--- a/src/app/model/ships/ship.model.ts
+++ b/src/app/model/ships/ship.model.ts
@@ -1,18 +1,19 @@
-import { GoodType } from "../goods/good-type.enum";
import { Planet, TradeInstance } from "../planet.model";
import { TradeRoute } from "../routes/trade-route.model";
export class Ship {
- public acceleration = 200; // Pixel pro Sekunde²
- public maxSpeed = 4000;
- public slowDownRadius = 2000; // Startet Bremsen, wenn Ziel nahe ist
+ public acceleration = 10; // Pixel pro Sekunde²
+ public maxSpeed = 100;
+ public currentSpeed = 0;
+
public cargoSize = 100;
cargoSpace: TradeInstance[] = [];
public route: TradeRoute | undefined = new TradeRoute([])
- public name = "Pioneer-01";
+ public name = "Pioneer-01-" + Math.round(Math.random() * 100);
public loadingSpeed = 1.5;
- public status: 'loading' | 'unloading' | 'waiting' | 'idle' | 'traveling' = 'idle'
+ public status: 'loading' | 'unloading' | 'waiting' | 'idle' | 'traveling' = 'idle';
+ public flightMode: FlightMode = FlightMode.Normal;
async exchangeGoods(planet: Planet) {
if (!this.route) {
@@ -25,6 +26,15 @@ export class Ship {
}
+ dock(planet: Planet) {
+ planet.dock(this)
+ }
+
+ undock(planet: Planet) {
+ planet.undock(this);
+ }
+
+
private async unloadGoods(planet: Planet) {
if (!planet || this.cargoSpace.length == 0) { return Promise.resolve(null); }
@@ -120,4 +130,12 @@ export class Ship {
}, amount * 1000 / this.loadingSpeed)
})
}
+}
+
+export enum FlightMode {
+ Normal, // Normales Fliegen zum Planeten
+ Orbiting, // Kreisen um den Planeten
+ Docking, // Gerader Flug und Landung
+ Undocking,
+ Docked
}
\ No newline at end of file
diff --git a/src/app/scene/map.scene.ts b/src/app/scene/map.scene.ts
index 1494127..e82ad73 100644
--- a/src/app/scene/map.scene.ts
+++ b/src/app/scene/map.scene.ts
@@ -1,5 +1,4 @@
import { PLANETCONFIGS } from "../data/planets.data";
-import { GoodType } from "../model/goods/good-type.enum";
import { TradeRoute } from "../model/routes/trade-route.model";
import { ShipUi } from "../model/ship";
import { GameService } from "../service/game.service";
@@ -67,7 +66,7 @@ export class MapScene extends Phaser.Scene {
});
this.input.on('pointermove', (pointer: Phaser.Input.Pointer) => {
- if (!this.isDragging) return;
+ if (!this.isDragging || !this.gameService.canDrag) return;
const dragX = pointer.x - this.dragStart.x;
const dragY = pointer.y - this.dragStart.y;
this.camera.scrollX -= dragX / this.camera.zoom;
@@ -92,18 +91,23 @@ export class MapScene extends Phaser.Scene {
this.camera.setZoom(Phaser.Math.Clamp(this.camera.zoom, minScale, 1));
});
+ this.createShip();
setTimeout(() => {
- const s = new ShipUi(this, 100, 100, this.gameService);
- s.model.route = new TradeRoute(this.planets.filter(planet => ['Terra Nova', 'Aqualis'].includes(planet.model.name)).map(planet => planet.model))
- this.ships.push(s)
- this.physics.world.enable(s);
- s.activateRoute();
- }, 1000)
+ this.createShip();
+ }, 100)
}
+ createShip() {
+ const s = new ShipUi(this, 100, 100, this.gameService);
+ s.model.route = new TradeRoute(this.planets.filter(planet => ['Terra Nova', 'Aqualis'].includes(planet.model.name)).map(planet => planet.model))
+ this.ships.push(s)
+ this.physics.world.enable(s);
+ s.activateRoute();
+ }
+
override update(time: number, delta: number): void {
if (this.ship) {
diff --git a/src/app/service/game.service.ts b/src/app/service/game.service.ts
index 0fb6865..d19069f 100644
--- a/src/app/service/game.service.ts
+++ b/src/app/service/game.service.ts
@@ -22,6 +22,6 @@ export class GameService {
}
get canDrag(): boolean {
- return true;
+ return this.showShipInfo == undefined && this.showPlanetInfo == undefined;
}
}
\ No newline at end of file
diff --git a/src/styles.scss b/src/styles.scss
index 232473a..31ce052 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -10,3 +10,9 @@ html {
));
}
+html, body {
+ width: 100vw;
+ height: 100vh;
+ margin: 0;
+ padding: 0;
+}
\ No newline at end of file