This commit is contained in:
Bastian Wagner 2025-04-26 15:06:04 +02:00
parent 758c358eea
commit 9007d94b0d
14 changed files with 104 additions and 56 deletions

BIN
public/title-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -8,6 +8,9 @@
<div class="ui-body"> <div class="ui-body">
<div class="ui-text-secondary" style="padding: 16px;">👥 Bevölkerung: {{ population }} 🚀</div> <div class="ui-text-secondary" style="padding: 16px;">👥 Bevölkerung: {{ population }} 🚀</div>
<div class="ui-section">
<div>Landebuchten: {{ planet.dockCapacity }}</div>
</div>
<div class="ui-section"> <div class="ui-section">
<div>🏭 Produktion:</div> <div>🏭 Produktion:</div>
<ul> <ul>
@ -33,12 +36,20 @@
<li>{{ item.type }}: {{ item.demandRate * planet.population | number:'0.0-1' }}/s</li> <li>{{ item.type }}: {{ item.demandRate * planet.population | number:'0.0-1' }}/s</li>
} }
</ul> </ul>
</div>
<div class="ui-section">
<div>📦 In Lieferung:</div>
<ul>
@for (item of goodsInTransit; track $index) {
<li>{{ item.type }}: {{ item.amount | number:'0.0-1' }}</li>
}
</ul>
</div> </div>
</div> </div>
<div class="ui-section"> <div class="ui-section">
<button class="button">Produktion upgraden</button> <button class="button" (click)="upgradeHarbour()" >Raumhafen upgraden</button>
<button class="button">Siedeln</button> <button class="button">Siedeln</button>
<button class="button" (click)="close()" >Schließen</button> <button class="button" (click)="close()" >Schließen</button>
</div> </div>

View File

@ -1,5 +1,5 @@
import { Component, inject, InjectionToken } from '@angular/core'; import { Component, inject, InjectionToken } from '@angular/core';
import { Planet } from '../../../model/planet.model'; import { Planet, TradeInstance } from '../../../model/planet.model';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Good } from '../../../model/goods/good.interface'; import { Good } from '../../../model/goods/good.interface';
@ -58,5 +58,14 @@ export class PlanetDialogComponent {
close() { close() {
this.gameService.showPlanetInfo = undefined; this.gameService.showPlanetInfo = undefined;
} }
get goodsInTransit(): TradeInstance[] {
return this.planet.goodsInTransit;
}
upgradeHarbour() {
this.planet.upgradeHarbour()
}
} }

View File

@ -54,17 +54,26 @@
<div>📦 Fracht:</div> <div>📦 Fracht:</div>
<ul> <ul>
@for (item of cargo; track $index) { @for (item of cargo; track $index) {
<li>{{ item.type }}: {{ item.amount | number:'0.0-0' }}</li> <li>{{ item.type }}: {{ item.amount | number:'0.0-0' }} => {{ item.target }}</li>
} }
</ul> </ul>
</div> </div>
<div class="ui-section"> <div class="ui-section">
<button class="button">Produktion upgraden</button> <div>🛣️ Aktive Route:</div>
<button class="button">Siedeln</button> <div class="flex route">
<button class="button" (click)="onClick($event); close();" >Schließen</button> @for (item of activeRoute; track $index) {
<div>{{ item }}</div>
}
</div>
</div> </div>
</div>
<div class="ui-section">
<button class="button">Produktion upgraden</button>
<button class="button">Siedeln</button>
<button class="button" (click)="onClick($event); close();" >Schließen</button>
</div> </div>
</div> </div>

View File

@ -16,3 +16,7 @@ img {
width: 92px; width: 92px;
height: 92px; height: 92px;
} }
.route {
padding: 16px 8px;
}

View File

@ -38,4 +38,8 @@ export class ShipDialogComponent {
event.stopPropagation(); event.stopPropagation();
console.log(event) console.log(event)
} }
get activeRoute(): string[] {
return this.ship.route?.planetNames ?? []
}
} }

View File

@ -4,6 +4,7 @@ import { GOODS_DATA } from "./goods/good-config";
import { Good } from "./goods/good.interface"; import { Good } from "./goods/good.interface";
import { ShipUi } from "./ship"; import { ShipUi } from "./ship";
import { Ship } from "./ships/ship.model"; import { Ship } from "./ships/ship.model";
import { GameService } from "../service/game.service";
export class Planet { export class Planet {
public population: number = 100; public population: number = 100;
@ -20,14 +21,16 @@ export class Planet {
private productionLevel: Map<GoodType, number> = new Map(); private productionLevel: Map<GoodType, number> = new Map();
private dockedShips: Ship[] = []; private dockedShips: Ship[] = [];
private dockCapacity: number = 0; public dockCapacity: number = 0;
private shipsWaitingForDocking: Ship[] = []; private shipsWaitingForDocking: Ship[] = [];
private gameService: GameService;
constructor(config: PlanetInit) {
constructor(config: PlanetInit, gameService: GameService) {
this.name = config.name; this.name = config.name;
this.image = config.name.toLowerCase().split(' ').join('-') this.image = config.name.toLowerCase().split(' ').join('-')
this.gameService = gameService;
/** set all empty */ /** set all empty */
Object.values(GoodType).forEach(c => { Object.values(GoodType).forEach(c => {
this.goods.set(c, { this.goods.set(c, {
@ -128,9 +131,16 @@ export class Planet {
continue; // Skip if we have enough in stock continue; // Skip if we have enough in stock
} }
const inTransit = this.goodsInTransit.filter(g => g.type == good.type).reduce((acc, succ) => acc + succ.amount, 0);
if (demand < good.amount + inTransit) {
continue; // Es werden genug geliefert.
}
result.push({ result.push({
amount: demand * 3, // Request 3x buffer amount amount: demand * 3, // Request 3x buffer amount
type: good.type type: good.type,
target: this.name,
}) })
} }
@ -138,7 +148,7 @@ export class Planet {
} }
get offeredGoods(): TradeInstance[] { get offeredGoods(): TradeInstance[] {
return this.getAllGoods().filter(g => g.productionRate && g.amount).map(g => { return { type: g.type, amount: g.amount}}) return this.getAllGoods().filter(g => g.productionRate && g.amount).map(g => { return { type: g.type, amount: g.amount, target: this.name }})
} }
// request(ship: ShipUi) { // request(ship: ShipUi) {
@ -282,6 +292,17 @@ export class Planet {
console.log('wartend: ',this.shipsWaitingForDocking.map(d => d.name).join(',')) console.log('wartend: ',this.shipsWaitingForDocking.map(d => d.name).join(','))
} }
get goodsInTransit(): TradeInstance[] {
return this.gameService.ships
.flatMap(ship => ship.cargoSpace)
.filter(item => item.target === this.name);
}
upgradeHarbour() {
this.dockCapacity++;
}
} }
export interface PlanetInit { export interface PlanetInit {
@ -296,4 +317,5 @@ export interface PlanetInit {
export interface TradeInstance { export interface TradeInstance {
type: GoodType; type: GoodType;
amount: number; amount: number;
target: string;
} }

View File

@ -46,6 +46,10 @@ export class TradeRoute {
} }
return demands; return demands;
} }
get planetNames(): string[] {
return this.route.map(r => r.target.name);
}
} }
interface ITradePlanet { interface ITradePlanet {

View File

@ -159,27 +159,6 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
} }
} }
loadCargo(cargo: TradeInstance): number {
const loaded = this.model.cargoSpace.reduce((acc, succ) => acc + succ.amount, 0);
if (loaded >= this.model.cargoSize) { return 0; }
const remaining = this.model.cargoSize - loaded;
const load = Math.min(remaining, cargo.amount);
const exists = this.model.cargoSpace.find(c => c.type == cargo.type);
if (exists) {
exists.amount += load
} else {
this.model.cargoSpace.push({
amount: load,
type: cargo.type
})
}
console.log(`Eingeladen: ${cargo.type}: ${load}`)
return load;
}
activateRoute() { activateRoute() {
if (!this.model.route) { return; } if (!this.model.route) { return; }

View File

@ -1,3 +1,4 @@
import { Good } from "../goods/good.interface";
import { Planet, TradeInstance } from "../planet.model"; import { Planet, TradeInstance } from "../planet.model";
import { TradeRoute } from "../routes/trade-route.model"; import { TradeRoute } from "../routes/trade-route.model";
@ -6,11 +7,11 @@ export class Ship {
public maxSpeed = 100; public maxSpeed = 100;
public currentSpeed = 0; public currentSpeed = 0;
public cargoSize = 100; public cargoSize = 50;
cargoSpace: TradeInstance[] = []; cargoSpace: TradeInstance[] = [];
public route: TradeRoute | undefined = new TradeRoute([]) public route: TradeRoute | undefined = new TradeRoute([])
public name = "Pioneer-01-" + Math.round(Math.random() * 100); public name = "Pioneer-01-" + Math.round(Math.random() * 100);
public loadingSpeed = 1.5; public loadingSpeed = 3.5;
public status: 'loading' | 'unloading' | 'waiting' | 'idle' | 'traveling' = 'idle'; public status: 'loading' | 'unloading' | 'waiting' | 'idle' | 'traveling' = 'idle';
public flightMode: FlightMode = FlightMode.Normal; public flightMode: FlightMode = FlightMode.Normal;
@ -38,22 +39,15 @@ export class Ship {
private async unloadGoods(planet: Planet) { private async unloadGoods(planet: Planet) {
if (!planet || this.cargoSpace.length == 0) { return Promise.resolve(null); } if (!planet || this.cargoSpace.length == 0) { return Promise.resolve(null); }
const planetRequests = planet.requestedGoods; const delivering = this.cargoSpace.filter(cargo => cargo.target == planet.name);
if (planetRequests.length == 0) { return Promise.resolve(null); }
this.status = 'unloading';
for (const request of planetRequests) { for (let dgood of delivering) {
const offer = this.cargoSpace.find(cargo => cargo.type == request.type); const good = planet.getGood(dgood.type);
if (!offer) { continue; }
const transfer = Math.min(offer.amount, request.amount);
const good = planet.getGood(offer.type);
if (!good) { continue; } if (!good) { continue; }
await this.removeFromCargoSpace({type: offer.type, amount: transfer}); await this.removeFromCargoSpace({type: dgood.type, amount: dgood.amount, target: dgood.target}, good);
good.amount += transfer;
} }
this.cargoSpace = this.cargoSpace.filter(c => c.amount != 0)
this.cargoSpace = this.cargoSpace.filter(c => c.amount != 0 && c.amount != undefined)
return Promise.resolve(null); return Promise.resolve(null);
} }
@ -77,7 +71,7 @@ export class Ship {
demand.amount = Math.max(demand.amount, 0) demand.amount = Math.max(demand.amount, 0)
if (demand.amount == 0) { continue; } if (demand.amount == 0) { continue; }
const received = planet.request(demand); const received = planet.request(demand);
await this.addToCargoSpace({type: demand.type, amount: received}) await this.addToCargoSpace({type: demand.type, amount: received, target: demand.target})
} }
return Promise.resolve(null) return Promise.resolve(null)
@ -87,13 +81,14 @@ export class Ship {
return this.cargoSize - this.cargoSpace.reduce((acc, succ) => acc + succ.amount, 0) return this.cargoSize - this.cargoSpace.reduce((acc, succ) => acc + succ.amount, 0)
} }
private async removeFromCargoSpace(cargo: TradeInstance) { private async removeFromCargoSpace(cargo: TradeInstance, to: Good) {
const steps = Math.ceil(cargo.amount) const steps = Math.ceil(cargo.amount)
for (let i = 1; i <= steps; i++) { for (let i = 1; i <= steps; i++) {
const existing = this.cargoSpace.find(c => c.type == cargo.type); const existing = this.cargoSpace.find(c => c.type == cargo.type);
if (existing) { if (existing) {
existing.amount -= (cargo.amount / steps); existing.amount -= (cargo.amount / steps);
to.amount += (cargo.amount / steps);
} }
await this.waitForLoading(1) await this.waitForLoading(1)
} }
@ -112,7 +107,8 @@ export class Ship {
} else { } else {
this.cargoSpace.push({ this.cargoSpace.push({
type: cargo.type, type: cargo.type,
amount: cargo.amount / steps amount: cargo.amount / steps,
target: cargo.target
}); });
} }

View File

@ -106,6 +106,7 @@ export class MapScene extends Phaser.Scene {
this.ships.push(s) this.ships.push(s)
this.physics.world.enable(s); this.physics.world.enable(s);
s.activateRoute(); s.activateRoute();
this.gameService.ships.push(s.model)
} }
override update(time: number, delta: number): void { override update(time: number, delta: number): void {
@ -120,7 +121,7 @@ export class MapScene extends Phaser.Scene {
} }
private buildPlanets() { private buildPlanets() {
for (let config of PLANETCONFIGS) { for (let config of PLANETCONFIGS) {
const planet = new PlanetUi(this, config.x, config.y, config.texture, config.config); const planet = new PlanetUi(this, config.x, config.y, config.texture, config.config, this.gameService);
planet.rightClick.subscribe(planet => { planet.rightClick.subscribe(planet => {
this.ship.moveTo(planet); this.ship.moveTo(planet);
this.isDragging = false; this.isDragging = false;
@ -130,6 +131,7 @@ export class MapScene extends Phaser.Scene {
this.isDragging = false; this.isDragging = false;
}); });
this.planets.push(planet); this.planets.push(planet);
this.gameService.planets.push(planet.model);
} }
} }

View File

@ -11,6 +11,9 @@ export class GameService {
public showPlanetInfo: Planet | undefined; public showPlanetInfo: Planet | undefined;
public showShipInfo: Ship | undefined; public showShipInfo: Ship | undefined;
public ships: Ship[] = [];
public planets: Planet[] = [];
constructor() {} constructor() {}
showDialog(planet: Planet) { showDialog(planet: Planet) {

View File

@ -2,6 +2,7 @@ import { EventEmitter } from "@angular/core";
import { interval } from "rxjs"; import { interval } from "rxjs";
import { Planet } from "../model/planet.model"; import { Planet } from "../model/planet.model";
import { PlanetStatus } from "./planet-status.ui"; import { PlanetStatus } from "./planet-status.ui";
import { GameService } from "../service/game.service";
export class PlanetUi extends Phaser.Physics.Arcade.Sprite { export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
@ -12,12 +13,12 @@ export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
private updateInterval = interval(1000) private updateInterval = interval(1000)
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, config: any) { constructor(scene: Phaser.Scene, x: number, y: number, texture: string, config: any, gameService: GameService) {
super(scene, x, y, texture); super(scene, x, y, texture);
// this.setDisplaySize(200, 200); // this.setDisplaySize(200, 200);
this.model = new Planet(config); this.model = new Planet(config, gameService);
scene.add.existing(this); scene.add.existing(this);
this.setInteractive({ useHandCursor: true }); this.setInteractive({ useHandCursor: true });

View File

@ -95,7 +95,7 @@ $font-size-base: 14px;
.ui-section { .ui-section {
padding: 16px; padding: 16px;
border-top: 1px solid #202d3e;
ul { ul {
margin: 8px 0 0 16px; margin: 8px 0 0 16px;
@ -114,3 +114,7 @@ $font-size-base: 14px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.ui-body > .ui-section {
border-top: 1px solid #202d3e;
}