docking
This commit is contained in:
parent
9d1f4649d7
commit
758c358eea
2
package-lock.json
generated
2
package-lock.json
generated
@ -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"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div #gameContainer class="game-container"></div>
|
||||
@if (gameService.showShipInfo) {
|
||||
<app-ship-dialog />
|
||||
<app-ship-dialog cdkDrag cdkDragBoundary="html"/>
|
||||
}
|
||||
@if (gameService.showPlanetInfo) {
|
||||
<app-planet-dialog></app-planet-dialog>
|
||||
<app-planet-dialog cdkDrag cdkDragBoundary="html"></app-planet-dialog>
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
.game-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
|
||||
@ -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'
|
||||
})
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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'
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="ui-panel">
|
||||
<div class="ui-panel" (click)="onClick($event)">
|
||||
|
||||
<div class="ui-title">
|
||||
<div class="image"><img [src]="'sprites/ships/01-starter.png'" alt=""></div>
|
||||
@ -26,6 +26,10 @@
|
||||
<ul>
|
||||
<li class="flex">
|
||||
<div>Geschwindigkeit: </div>
|
||||
<div>{{ ship.currentSpeed | number:'0.0-0' }} km/s</div>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<div>Max Geschwindigkeit: </div>
|
||||
<div>{{ ship.maxSpeed | number:'0.0-0' }} km/s</div>
|
||||
</li>
|
||||
<li class="flex">
|
||||
@ -58,7 +62,7 @@
|
||||
<div class="ui-section">
|
||||
<button class="button">Produktion upgraden</button>
|
||||
<button class="button">Siedeln</button>
|
||||
<button class="button" (click)="close()" >Schließen</button>
|
||||
<button class="button" (click)="onClick($event); close();" >Schließen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -33,4 +33,9 @@ export class ShipDialogComponent {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
onClick(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
console.log(event)
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@ export class Planet {
|
||||
demandSecondsBuffer = 30; // Anfrage immer mindestens 30 Sekunden überleben
|
||||
|
||||
private productionLevel: Map<GoodType, number> = 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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
|
||||
@ -22,6 +22,6 @@ export class GameService {
|
||||
}
|
||||
|
||||
get canDrag(): boolean {
|
||||
return true;
|
||||
return this.showShipInfo == undefined && this.showPlanetInfo == undefined;
|
||||
}
|
||||
}
|
||||
@ -10,3 +10,9 @@ html {
|
||||
));
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user