This commit is contained in:
Bastian Wagner 2025-04-26 22:31:08 +02:00
parent cb000d29aa
commit 3dd86fa224
26 changed files with 292 additions and 175 deletions

View File

@ -1,59 +1,3 @@
# StellarLines Docker:
- docker build -t bastianwagner/stellar_lines_client:latest .
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.9. - docker push bastianwagner/stellar_lines_client:latest
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@ -55,7 +55,7 @@ export class AppComponent {
physics: { physics: {
default: 'arcade', default: 'arcade',
arcade: { arcade: {
debug: true, debug: false,
timeScale: 1 timeScale: 1
} }
} }

View File

@ -8,7 +8,7 @@
<div class="ui-body"> <div class="ui-body">
@if (planet.hasHarbour) { @if (planet.hasHarbour) {
<div class="ui-text-secondary" style="padding: 16px;">👥 Bevölkerung: {{ population }} 🚀</div> <div class="ui-text-secondary" style="padding: 16px;">👥 Bevölkerung: {{ population }} {{ planet.isGrowing ? '🚀' : '⬇️' }}</div>
<div class="ui-section"> <div class="ui-section">
<div>Landebuchten: {{ planet.dockCapacity }}</div> <div>Landebuchten: {{ planet.dockCapacity }}</div>
</div> </div>
@ -16,7 +16,12 @@
<div>🏭 Produktion:</div> <div>🏭 Produktion:</div>
<ul> <ul>
@for (item of producedItems; track $index) { @for (item of producedItems; track $index) {
<li>{{ item.type }}: +{{ item.productionRate | number:'0.0-2' }}/s</li> <li>
<div class="flex">
<div>{{ item.type }}: +{{ getProductionAmount(item) | number:'0.0-2' }}/s</div>
<div>Angebot: {{ getOfferedAmount(item) | number:'0.0-0'}}</div>
</div>
</li>
} }
</ul> </ul>
</div> </div>
@ -41,6 +46,15 @@
</ul> </ul>
</div> </div>
<!-- <div class="ui-section">
<div>📦 Anfrage:</div>
<ul>
@for (item of requested; track $index) {
<li>{{ item.type }}: {{ item.amount | number:'0.0-1' }}/s</li>
}
</ul>
</div> -->
@if (goodsInTransit.length > 0) { @if (goodsInTransit.length > 0) {
<div class="ui-section"> <div class="ui-section">
<div>📦 In Lieferung:</div> <div>📦 In Lieferung:</div>
@ -59,7 +73,11 @@
<div>🏭 Produktion:</div> <div>🏭 Produktion:</div>
<ul> <ul>
@for (item of producedItems; track $index) { @for (item of producedItems; track $index) {
<li>{{ item.type }}: +{{ item.productionRate | number:'0.0-2' }}/s</li> <li >
<div class="flex">
<div>{{ item.type }}: +{{ getProductionAmount(item) | number:'0.0-2' }}/s</div>
</div>
</li>
} }
</ul> </ul>
</div> </div>
@ -70,7 +88,10 @@
@if (planet.hasHarbour) { @if (planet.hasHarbour) {
<button class="button" (click)="upgradeHarbour()" <button class="button" (click)="upgradeHarbour()"
matTooltip="Stellt eine zusätzliche Landebucht zur Verfügung. Pro Landebucht kann ein Raumschiff verladen werden. Das Upgrade kostet allerdings Geld." matTooltip="Stellt eine zusätzliche Landebucht zur Verfügung. Pro Landebucht kann ein Raumschiff verladen werden. Das Upgrade kostet allerdings Geld."
>Raumhafen upgraden</button> >Raumhafen aufwerten</button>
<button class="button" (click)="upgradeProduction()" [disabled]="!canUpgradeProduction"
matTooltip="Kosten: {{this.planet.productionLvlUpgradeCost | number}} Credits">Produktion aufwerten</button>
} @else { } @else {
<button class="button" (click)="buildHarbour()" <button class="button" (click)="buildHarbour()"
matTooltip="Baue einen Raumhafen um den Planeten anfliegen zu können." matTooltip="Baue einen Raumhafen um den Planeten anfliegen zu können."

View File

@ -35,7 +35,7 @@ export class PlanetDialogComponent {
} }
get storedItems(): Good[] { get storedItems(): Good[] {
return this.planet.getAllGoods() return this.planet.getAllGoods().filter(item => item.demandRate || item.amount)
} }
get consumedItems(): Good[] { get consumedItems(): Good[] {
@ -51,7 +51,11 @@ export class PlanetDialogComponent {
} }
getFillPercentange(item: Good): number { getFillPercentange(item: Good): number {
return (item.amount / (item.demandRate * this.planet.population * 30)) * 100; const val = (item.amount / (item.demandRate * this.planet.population * this.planet.demandSecondsBuffer)) * 100;
if (isNaN(val)) {
return 0;
}
return Math.min(Math.floor(val), 100);
} }
@ -72,5 +76,28 @@ export class PlanetDialogComponent {
this.planet.buildHarbour() this.planet.buildHarbour()
} }
getOfferedAmount(item: Good): number {
const offers = this.planet.offeredGoods;
return offers.find(o => o.type == item.type)?.amount ?? 0;
}
upgradeProduction() {
this.planet.upgradeProduction();
}
getProductionAmount(item: Good): number {
if (!item.productionRate) { return 0; }
return this.planet.getProductionLvl(item) * item.productionRate;
}
get canUpgradeProduction(): boolean {
return this.gameService.money > this.planet.productionLvlUpgradeCost;
}
get requested(): TradeInstance[] {
return this.planet.requestedGoods;
}
} }

View File

@ -1,7 +1,7 @@
<div class="ui-panel" (click)="onClick($event)"> <div class="ui-panel" (click)="onClick($event)">
<div class="ui-title" cdkDragHandle> <div class="ui-title" cdkDragHandle>
<div class="image"><img [src]="'sprites/ships/01-starter.png'" alt=""></div> <div class="image"><img [src]="'sprites/ships/' + ship.texture +'.png'" alt=""></div>
<div>{{ ship.name }}</div> <div>{{ ship.name }}</div>
</div> </div>
@ -44,6 +44,10 @@
<div>Ladegeschwindigkeit: </div> <div>Ladegeschwindigkeit: </div>
<div>{{ ship.loadingSpeed | number:'0.0-2' }} t/s</div> <div>{{ ship.loadingSpeed | number:'0.0-2' }} t/s</div>
</li> </li>
<li class="flex">
<div>Balance:</div>
<div>{{ ship.balance | number:'0.0-2' }} Credits</div>
</li>
</ul> </ul>
<div> <div>

View File

@ -45,5 +45,6 @@ export class ShipDialogComponent {
sell() { sell() {
this.gameService.sellShip(this.ship); this.gameService.sellShip(this.ship);
this.close();
} }
} }

View File

@ -4,25 +4,32 @@
</div> </div>
<div class="ui-section"> <div class="ui-section">
<div>🚀 Neues Schiff: Pioneer-1</div> <div>
<div style="margin-top: 12px; font-size: 12px;">{{ config.desciption }}</div> <select name="selectship" id="slship" [(ngModel)]="selectedModel">
<option value="1" selected>Pioneer-01</option>
<option value="2">Colony Carrier</option>
<option value="3">Industrial Tanker</option>
</select>
</div>
<div>🚀 Neues Schiff: {{ selectedShip.name }}</div>
<div style="margin-top: 12px; font-size: 12px;">{{ selectedShip.desciption }}</div>
<ul> <ul>
<li class="flex"> <li class="flex">
<div>Kosten: </div> <div>Kosten: </div>
<div>{{ config.buyCost | number:'0.0-2'}} Credits</div> <div>{{ selectedShip.buyCost | number:'0.0-2'}} Credits</div>
</li> </li>
<li class="flex"> <li class="flex">
<div>Frachtraum: </div> <div>Frachtraum: </div>
<div>{{ config.cargoSize | number: '0.0-2'}} t</div> <div>{{ selectedShip.cargoSize | number: '0.0-2'}} t</div>
</li> </li>
<li class="flex"> <li class="flex">
<div>Max Geschwindigkeit: </div> <div>Max Geschwindigkeit: </div>
<div>{{ config.maxSpeed | number: '0.0-2'}} km/s</div> <div>{{ selectedShip.maxSpeed | number: '0.0-2'}} km/s</div>
</li> </li>
<li class="flex"> <li class="flex">
<div>Unterhalt: </div> <div>Unterhalt: </div>
<div>{{ config.cost | number: '0.0-2'}} Credits</div> <div>{{ selectedShip.cost | number: '0.0-2'}} Credits</div>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -4,10 +4,11 @@ import { GameService } from '../../../service/game.service';
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
import { Planet } from '../../../model/planet.model'; import { Planet } from '../../../model/planet.model';
import { ShipConfig } from '../../../model/ships/ship.model'; import { ShipConfig } from '../../../model/ships/ship.model';
import { FormsModule } from '@angular/forms';
@Component({ @Component({
selector: 'app-buy', selector: 'app-buy',
imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle], imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, FormsModule],
templateUrl: './buy.component.html', templateUrl: './buy.component.html',
styleUrl: './buy.component.scss' styleUrl: './buy.component.scss'
}) })
@ -19,13 +20,53 @@ export class BuyComponent {
config: ShipConfig = { config: ShipConfig = {
name: 'Pioneer-01', name: 'Pioneer-01',
acceleration: 500, acceleration: 500,
cargoSize: 10, cargoSize: 20,
cost: 0.6, cost: 0.6,
loadingSpeed: 25, loadingSpeed: 15,
maxSpeed: 500, maxSpeed: 300,
planetRoute: [], planetRoute: [],
buyCost: 2000, buyCost: 2000,
desciption: 'Ein kleines, schnelles Schiff. Es hat wenig Platz für Ladung, lädt aber schnell und ist sehr wendig.' desciption: 'Ein kleines, schnelles Schiff. Es hat wenig Platz für Ladung, lädt aber schnell und ist sehr wendig.',
texture: 'swift-hauler'
}
config2: ShipConfig = {
name: 'Colony Carrier',
acceleration: 200,
cargoSize: 50,
cost: 1.6,
loadingSpeed: 20,
maxSpeed: 200,
planetRoute: [],
buyCost: 4000,
desciption: 'Eine größere Version von der Pioneer-01. Der größere Frachtraum geht zu Lasten der Beschleunigung und Maximalgeschwindigkeit.',
texture: 'colony-carrier'
}
config3: ShipConfig = {
name: 'Industrial Tanker',
acceleration: 5,
cargoSize: 300,
cost: 3.8,
loadingSpeed: 5,
maxSpeed: 600,
planetRoute: [],
buyCost: 8000,
desciption: 'Ein großes behäbiges Schiff. Es wird durchaus schnell, beschleunigt aber sehr langsam und erreicht die Spitzengeschwindigkeit nur selten.',
texture: 'industrial-tanker'
}
selectedModel = '1';
get selectedShip(): ShipConfig {
if (this.selectedModel == '1') {
return this.config;
} else if (this.selectedModel == '3') {
return this.config3
}
return this.config2;
} }
canAffordShip() { canAffordShip() {
@ -35,7 +76,7 @@ export class BuyComponent {
buyShip() { buyShip() {
if (!this.canAffordShip()) return; if (!this.canAffordShip()) return;
this.gameService.createShip({ this.gameService.createShip({
...this.config, ...this.selectedShip,
planetRoute: this.selectedPlanets planetRoute: this.selectedPlanets
}) })
this.close(); this.close();
@ -57,4 +98,5 @@ export class BuyComponent {
); );
} }
} }
} }

View File

@ -1,8 +1,8 @@
<div (click)="onClick($event)" (mousedown)="onClick($event)" class="flex gamebar"> <div (click)="onClick($event)" (mousedown)="onClick($event)" class="flex gamebar">
<div class="gold"> <div class="gold">
Credits: {{gameService.money | number:'0.0-0'}} {{gameService.money | number:'0.0-0'}} Credits
</div> </div>
<button class="button" (click)="toggleBuy()" >Schiffe</button> <button class="button" (click)="toggleBuy()" >Route anlegen</button>
</div> </div>

View File

@ -3,97 +3,97 @@ import { PlanetInit } from "../model/planet.model";
export const PLANETCONFIGS: { x: number, y: number, texture: string, config: PlanetInit}[] = [ export const PLANETCONFIGS: { x: number, y: number, texture: string, config: PlanetInit}[] = [
{ {
x: 600, x: 900,
y: 800, y: 950,
texture: 'terra-nova', texture: 'terra-nova',
config: { config: {
name: 'Terra Nova', name: 'Terra Nova',
initialGoods: [ initialGoods: [
{ type: GoodType.Wasser, amount: 15 }, { type: GoodType.Wasser, amount: 10 },
{ type: GoodType.Nahrung, amount: 8, productionBonus: 1.3 }, { type: GoodType.Nahrung, amount: 10, productionBonus: 1.3 },
] ]
} }
}, },
{ {
x: 3000, x: 4000,
y: 900, y: 1500,
texture: 'mechanica-prime', texture: 'mechanica-prime',
config: { config: {
name: 'Mechanica Prime', name: 'Mechanica Prime',
initialGoods: [ initialGoods: [
{ type: GoodType.Wasser, amount: 120 }, { type: GoodType.Wasser, amount: 10 },
{ type: GoodType.Nahrung, amount: 80 }, { type: GoodType.Nahrung, amount: 10 },
{ type: GoodType.Metall, amount: 50, productionBonus: 1.5 }, { type: GoodType.Metall, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Bauteile, amount: 20, productionBonus: 1.3 }, { type: GoodType.Bauteile, amount: 10, productionBonus: 1.3 },
{ type: GoodType.Elektronik, amount: 10 } { type: GoodType.Elektronik, amount: 10 }
] ]
} }
}, },
{ {
x: 1800, x: 1800,
y: 2800, y: 3800,
texture: 'aqualis', texture: 'aqualis',
config: { config: {
name: 'Aqualis', name: 'Aqualis',
initialGoods: [ initialGoods: [
{ type: GoodType.Wasser, amount: 30, productionBonus: 2.0 }, { type: GoodType.Wasser, amount: 200, productionBonus: 2.0 },
{ type: GoodType.Nahrung, amount: 15 }, { type: GoodType.Nahrung, amount: 10 },
{ type: GoodType.Treibstoff, amount: 10 } { type: GoodType.Treibstoff, amount: 10 }
] ]
} }
}, },
{ {
x: 3900, x: 4500,
y: 2500, y: 5500,
texture: 'planet', texture: 'planet',
config: { config: {
name: 'Ferron', name: 'Ferron',
initialGoods: [ initialGoods: [
{ type: GoodType.Wasser, amount: 120 }, { type: GoodType.Wasser, amount: 10 },
{ type: GoodType.Nahrung, amount: 80 }, { type: GoodType.Nahrung, amount: 10 },
{ type: GoodType.Erz, amount: 200, productionBonus: 1.8 }, { type: GoodType.Erz, amount: 10, productionBonus: 1.8 },
{ type: GoodType.Metall, amount: 40 }, { type: GoodType.Metall, amount: 10 },
{ type: GoodType.Treibstoff, amount: 20 } { type: GoodType.Treibstoff, amount: 10 }
] ]
} }
}, },
{ {
x: 5400, x: 7400,
y: 1400, y: 2400,
texture: 'novus-reach', texture: 'novus-reach',
config: { config: {
name: 'Novus Reach', name: 'Novus Reach',
initialGoods: [ initialGoods: [
{ type: GoodType.Nahrung, amount: 90, productionBonus: 1.5 }, { type: GoodType.Nahrung, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Wasser, amount: 50 }, { type: GoodType.Wasser, amount: 10 },
{ type: GoodType.Bauteile, amount: 15 } { type: GoodType.Bauteile, amount: 10 }
] ]
} }
} }
, ,
{ {
x: 3700, x: 3700,
y: 4600, y: 7600,
texture: 'novus-reach', texture: 'volaris',
config: { config: {
name: 'Novus Reach 2', name: 'Volaris',
initialGoods: [ initialGoods: [
{ type: GoodType.Nahrung, amount: 90, productionBonus: 1.5 }, { type: GoodType.Nahrung, amount: 10 },
{ type: GoodType.Wasser, amount: 50 }, { type: GoodType.Wasser, amount: 10, productionBonus: 1.1 },
{ type: GoodType.Bauteile, amount: 15 } { type: GoodType.Bauteile, amount: 10 }
] ]
} }
}, },
{ {
x: 6000, x: 8000,
y: 3600, y: 6600,
texture: 'terra-nova', texture: 'ignis-prime',
config: { config: {
name: 'Terra Nova 2', name: 'Ignis Prime',
initialGoods: [ initialGoods: [
{ type: GoodType.Wasser, amount: 120 }, { type: GoodType.Wasser, amount: 10 },
{ type: GoodType.Nahrung, amount: 80, productionBonus: 1.3 }, { type: GoodType.Nahrung, amount: 10 },
{ type: GoodType.Erz, amount: 100 } { type: GoodType.Treibstoff, amount: 10, productionBonus: 0.8 }
] ]
} }
} }

View File

@ -8,11 +8,11 @@ export interface GoodConfig {
} }
export const GOODS_DATA: Record<GoodType, GoodConfig> = { export const GOODS_DATA: Record<GoodType, GoodConfig> = {
[GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0, storageLimit: 500, isRawResource: true }, [GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0.001, storageLimit: 500, isRawResource: true },
[GoodType.Wasser]: { baseProduction: 1, baseDemand: 0.0015, storageLimit: 300, isRawResource: true }, [GoodType.Wasser]: { baseProduction: 1, baseDemand: 0.0015, storageLimit: 300, isRawResource: true },
[GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 200, isRawResource: true }, [GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 200, isRawResource: true },
[GoodType.Metall]: { baseProduction: 1, baseDemand: 0, storageLimit: 300, isRawResource: false }, [GoodType.Metall]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 300, isRawResource: false },
[GoodType.Treibstoff]: { baseProduction: 1, baseDemand: 0, storageLimit: 150, isRawResource: false }, [GoodType.Treibstoff]: { baseProduction: 1, baseDemand: 0, storageLimit: 150, isRawResource: false },
[GoodType.Elektronik]: { baseProduction: 1, baseDemand: 0, storageLimit: 100, isRawResource: false }, [GoodType.Elektronik]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 100, isRawResource: false },
[GoodType.Bauteile]: { baseProduction: 1, baseDemand: 0, storageLimit: 100, isRawResource: false } [GoodType.Bauteile]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 100, isRawResource: false }
}; };

View File

@ -11,12 +11,12 @@ export class Planet {
public image: string; public image: string;
private goods: Map<string, Good> = new Map(); private goods: Map<string, Good> = new Map();
private updateSubscription = interval(5000).subscribe(() => {this.update(5)}); // alle 5s private updateSubscription = interval(1000).subscribe(() => {this.update(1)}); // alle 5s
public isGrowing: boolean = false; public isGrowing: boolean = false;
private populationGrowthRate = 0.002; // Basiswachstum pro Tick (%) private populationGrowthRate = 0.002; // Basiswachstum pro Tick (%)
private populationDeclineRate = 0.005; // Basisrückgang bei Mangel (%) private populationDeclineRate = 0.005; // Basisrückgang bei Mangel (%)
demandSecondsBuffer = 150; // Anfrage immer mindestens 30 Sekunden überleben public demandSecondsBuffer = 150; // Anfrage immer mindestens 30 Sekunden überleben
private productionLevel: Map<GoodType, number> = new Map(); private productionLevel: Map<GoodType, number> = new Map();
private dockedShips: Ship[] = []; private dockedShips: Ship[] = [];
@ -60,13 +60,56 @@ export class Planet {
this.dockCapacity = 1; this.dockCapacity = 1;
}, 10000) }, 10000)
} }
adjustPopulationDemands(good: Good) {
switch(good.type) {
case GoodType.Erz:
if (this.population > 250) {
good.demandRate = GOODS_DATA.Erz.baseDemand
} else {
good.demandRate = 0;
}
break;
case GoodType.Bauteile:
if (this.population > 500) {
good.demandRate = GOODS_DATA.Bauteile.baseDemand
} else {
good.demandRate = 0;
}
break;
case GoodType.Elektronik:
if (this.population > 700) {
good.demandRate = GOODS_DATA.Elektronik.baseDemand
} else {
good.demandRate = 0;
}
break;
case GoodType.Metall:
if (this.population > 750) {
good.demandRate = GOODS_DATA.Metall.baseDemand
} else {
good.demandRate = 0;
}
break;
case GoodType.Treibstoff:
if (this.population > 900) {
good.demandRate = GOODS_DATA.Treibstoff.baseDemand
} else {
good.demandRate = 0;
}
break;
}
}
private update(seconds: number): void { private update(seconds: number): void {
this.goods.forEach((good: Good, key: string) => { this.goods.forEach((good: Good, key: string) => {
this.adjustPopulationDemands(good)
const lvlMultiplier = this.productionLevel.get(good.type) ?? 1; const lvlMultiplier = this.productionLevel.get(good.type) ?? 1;
good.amount += good.productionRate * seconds * lvlMultiplier; good.amount += good.productionRate * seconds * lvlMultiplier;
good.amount -= good.demandRate * seconds;
// Min 0 // Min 0
good.amount = Math.max(0, good.amount); good.amount = Math.max(0, good.amount);
@ -77,23 +120,11 @@ export class Planet {
this.updatePopulation(seconds) this.updatePopulation(seconds)
return; return;
// Debug-Ausgabe (später ersetzen durch Events/Callback/Service) // Debug-Ausgabe (später ersetzen durch Events/Callback/Service)
console.log(`[${this.name}] Wirtschaft aktualisiert:`);
this.goods.forEach(good => {
console.log(` ${good.type}: ${good.amount.toFixed(2)}`);
});
console.log("Angeboten:")
for (let good of this.offeredGoods) {
console.log(` ${good.type}: ${good.amount.toFixed(2)}`)
}
console.log("Nachgefragt:")
for (let good of this.requestedGoods) {
console.log(` ${good.type}: ${good.amount.toFixed(2)}`)
}
@ -134,9 +165,8 @@ export class Planet {
const inTransit = this.goodsInTransit.filter(g => g.type == good.type).reduce((acc, succ) => acc + succ.amount, 0); const inTransit = this.goodsInTransit.filter(g => g.type == good.type).reduce((acc, succ) => acc + succ.amount, 0);
if (demand < good.amount + inTransit) { if (demand < good.amount + inTransit) {
console.log(`[${this.name}] Skipping request for ${good.type}: ${inTransit} in transit + ${good.amount} > ${demand}`);
continue; // Es werden genug geliefert. continue; // Es werden genug geliefert.
} }
@ -151,19 +181,9 @@ 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, target: this.name }}) return this.getAllGoods().filter(g => g.productionRate && g.amount).map(g => { return { type: g.type, amount: g.amount - (g.demandRate * this.population * this.demandSecondsBuffer), target: this.name }}).filter(g => g.amount > 0)
} }
// request(ship: ShipUi) {
// const offers = this.offeredGoods;
// if (offers.length == 0) { return; }
// const loaded = ship.loadCargo(offers[0]);
// offers[0].amount -= loaded;
// const reduce = this.goods.get(offers[0].type)
// if (!reduce) { return; }
// reduce.amount = Math.max(0, reduce.amount - loaded);
// }
request(demandedGood: {type: GoodType, amount: number}) { request(demandedGood: {type: GoodType, amount: number}) {
const offers = this.offeredGoods; const offers = this.offeredGoods;
if (offers.length == 0) { return 0; } if (offers.length == 0) { return 0; }
@ -225,14 +245,18 @@ export class Planet {
*/ */
private calculateNaturalDemand(seconds: number): Map<GoodType, number> { private calculateNaturalDemand(seconds: number): Map<GoodType, number> {
const demand = new Map<GoodType, number>(); const demand = new Map<GoodType, number>();
for (const goodType in GOODS_DATA) { for (let good of this.getAllGoods()) {
const config = GOODS_DATA[goodType as GoodType]; demand.set(good.type ,this.population * good.demandRate * seconds )
if (config.baseDemand > 0) {
demand.set(goodType as GoodType, this.population * config.baseDemand * seconds);
}
} }
// for (const goodType in GOODS_DATA) {
// const config = GOODS_DATA[goodType as GoodType];
// if (config.baseDemand > 0) {
// demand.set(goodType as GoodType, this.population * config.baseDemand * seconds);
// }
// }
return demand; return demand;
} }
@ -280,9 +304,7 @@ export class Planet {
dock(ship: Ship) { dock(ship: Ship) {
console.log("DOCK:", ship.name)
if (!this.dockedShips.includes(ship)) { if (!this.dockedShips.includes(ship)) {
console.log("DOCK:", ship.name)
this.dockedShips.push(ship); this.dockedShips.push(ship);
this.shipsWaitingForDocking = this.shipsWaitingForDocking.filter(s => s != ship); // aus warteschlange entfernen this.shipsWaitingForDocking = this.shipsWaitingForDocking.filter(s => s != ship); // aus warteschlange entfernen
} }
@ -309,6 +331,36 @@ export class Planet {
this.gameService.money -= 3000; this.gameService.money -= 3000;
} }
upgradeProduction() {
if (this.gameService.money < this.productionLvlUpgradeCost) { return; }
this.gameService.money -= this.productionLvlUpgradeCost;
this.getAllGoods().filter(g => g.productionRate).forEach(good => {
const lvl = this.productionLevel.get(good.type);
if (lvl) {
this.productionLevel.set(good.type, lvl + 1);
} else {
this.productionLevel.set(good.type, 2)
}
if (good.productionStorage) {
good.productionStorage = good.productionStorage * 1.5;
}
})
}
getProductionLvl(good: Good): number {
return this.productionLevel.get(good.type) ?? 1;
}
get productionLvlUpgradeCost(): number {
const lvls = Array.from(this.productionLevel.values());
lvls.push(1); // falls es leer ist
const lvl = Math.max(...lvls);
return lvl * 1000;
}
} }
export interface PlanetInit { export interface PlanetInit {

View File

@ -1,7 +1,6 @@
import { MapScene } from "../scene/map.scene"; import { MapScene } from "../scene/map.scene";
import { GameService } from "../service/game.service"; import { GameService } from "../service/game.service";
import { PlanetUi } from "../ui/planet.ui"; import { PlanetUi } from "../ui/planet.ui";
import { TradeInstance } from "./planet.model";
import { FlightMode, Ship } from "./ships/ship.model"; import { FlightMode, Ship } from "./ships/ship.model";
export class ShipUi extends Phaser.Physics.Arcade.Sprite { export class ShipUi extends Phaser.Physics.Arcade.Sprite {
@ -16,7 +15,7 @@ export class ShipUi extends Phaser.Physics.Arcade.Sprite {
private orbitSpeed: number = Math.PI / 25; // Eine Runde in 2 Sekunden private orbitSpeed: number = Math.PI / 25; // Eine Runde in 2 Sekunden
constructor(scene: MapScene, x: number, y: number, gameService: GameService, ship: Ship) { constructor(scene: MapScene, x: number, y: number, gameService: GameService, ship: Ship) {
super(scene, x, y, 'ship'); super(scene, x, y, ship.texture);
this.gameService = gameService; this.gameService = gameService;
this.model = ship; this.model = ship;

View File

@ -14,6 +14,7 @@ export interface ShipConfig {
planetRoute: Planet[]; planetRoute: Planet[];
buyCost: number; buyCost: number;
desciption: string; desciption: string;
texture: string;
} }
export class Ship { export class Ship {
@ -29,6 +30,10 @@ export class Ship {
public cost = 0.5 public cost = 0.5
public buyCost = 0; public buyCost = 0;
public description: string = ""; public description: string = "";
texture: string;
public totalCost: number = 0;
public totalEarnings: number = 0;
private updateInterval = interval(1000).subscribe(() => { private updateInterval = interval(1000).subscribe(() => {
this.update(); this.update();
@ -44,7 +49,10 @@ export class Ship {
this.cost = config.cost; this.cost = config.cost;
this.buyCost = config.buyCost; this.buyCost = config.buyCost;
this.description = config.desciption; this.description = config.desciption;
this.texture = config.texture;
this.route = new TradeRoute(config.planetRoute) this.route = new TradeRoute(config.planetRoute)
this.name = config.name + ' - ' + Math.round(Math.random() * 100);
this.totalCost = config.buyCost;
} }
@ -80,10 +88,10 @@ export class Ship {
for (let dgood of delivering) { for (let dgood of delivering) {
const good = planet.getGood(dgood.type); const good = planet.getGood(dgood.type);
if (!good) { continue; } if (!good) { continue; }
await this.removeFromCargoSpace({type: dgood.type, amount: dgood.amount, target: dgood.target}, good); await this.removeFromCargoSpace(dgood, good);
} }
this.cargoSpace = this.cargoSpace.filter(c => c.amount != 0 && c.amount != undefined) this.cargoSpace = this.cargoSpace.filter(c => c.amount != 0 && c.amount != undefined && c.amount > 0);
return Promise.resolve(null); return Promise.resolve(null);
} }
@ -101,7 +109,7 @@ export class Ship {
for (let demand of demands) { for (let demand of demands) {
// requested amount: demand - storage // requested amount: demand - storage
const stored = this.cargoSpace.find(c => c.type == demand.type); const stored = this.cargoSpace.find(c => c.type == demand.type && demand.target == c.target);
if (stored) { demand.amount -= stored.amount; }; if (stored) { demand.amount -= stored.amount; };
demand.amount = Math.min(demand.amount, this.freeCargoSpace); demand.amount = Math.min(demand.amount, this.freeCargoSpace);
demand.amount = Math.max(demand.amount, 0) demand.amount = Math.max(demand.amount, 0)
@ -118,18 +126,21 @@ export class Ship {
} }
private async removeFromCargoSpace(cargo: TradeInstance, to: Good) { private async removeFromCargoSpace(cargo: TradeInstance, to: Good) {
const steps = Math.ceil(cargo.amount) const steps = Math.ceil(cargo.amount);
const transferPerStep = cargo.amount / steps;
for (let i = 1; i <= steps; i++) { for (let i = 1; i <= steps; i++) {
const existing = this.cargoSpace.find(c => c.type == cargo.type); if (cargo) {
if (existing) { cargo.amount -= transferPerStep;
existing.amount -= (cargo.amount / steps); to.amount += transferPerStep;
to.amount += (cargo.amount / steps); this.gameService.money += 18 * transferPerStep;
this.gameService.money += 5; this.totalEarnings += 18 * transferPerStep;
} }
await this.waitForLoading(1) await this.waitForLoading(1)
} }
to.amount += cargo.amount;
cargo.amount = 0;
return Promise.resolve(null) return Promise.resolve(null)
} }
@ -138,7 +149,7 @@ export class Ship {
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 && cargo.target == c.target);
if (existing) { if (existing) {
existing.amount += (cargo.amount / steps); existing.amount += (cargo.amount / steps);
} else { } else {
@ -166,12 +177,17 @@ export class Ship {
update() { update() {
this.gameService.money -= this.cost; this.gameService.money -= this.cost;
this.totalCost += this.cost;
} }
sell() { sell() {
this.updateInterval.unsubscribe(); this.updateInterval.unsubscribe();
} }
get balance(): number {
return this.totalEarnings - this.totalCost;
}
} }
export enum FlightMode { export enum FlightMode {

View File

@ -21,10 +21,14 @@ export class MapScene extends Phaser.Scene {
} }
preload() { preload() {
this.load.image('ship', 'sprites/ships/swift-hauler.png'); this.load.image('swift-hauler', 'sprites/ships/swift-hauler.png');
this.load.image('colony-carrier', 'sprites/ships/colony-carrier.png');
this.load.image('industrial-tanker', 'sprites/ships/industrial-tanker.png');
this.load.image('harbour', 'sprites/buildings/harbour.png'); this.load.image('harbour', 'sprites/buildings/harbour.png');
this.load.image('planet', 'sprites/planets/planet-1.png'); this.load.image('planet', 'sprites/planets/planet-1.png');
this.load.image('terra-nova', 'sprites/planets/terra-nova.png'); this.load.image('terra-nova', 'sprites/planets/terra-nova.png');
this.load.image('volaris', 'sprites/planets/volaris.png');
this.load.image('ignis-prime', 'sprites/planets/ignis-prime.png');
this.load.image('mechanica-prime', 'sprites/planets/mechanica-prime.png'); this.load.image('mechanica-prime', 'sprites/planets/mechanica-prime.png');
this.load.image('novus-reach', 'sprites/planets/novus-reach.png'); this.load.image('novus-reach', 'sprites/planets/novus-reach.png');
this.load.image('aqualis', 'sprites/planets/aqualis.png'); this.load.image('aqualis', 'sprites/planets/aqualis.png');
@ -47,7 +51,7 @@ export class MapScene extends Phaser.Scene {
for (let i = 0; i< 1000; i++) { for (let i = 0; i< 1000; i++) {
const x = Phaser.Math.Between(0, 10000); const x = Phaser.Math.Between(0, 10000);
const y = Phaser.Math.Between(0, 10000); const y = Phaser.Math.Between(0, 10000);
this.add.circle(x, y, Phaser.Math.Between(1, 3), 0x88ccff) this.add.circle(x, y, Phaser.Math.Between(2, 10), 0x88ccff)
} }
// Beispiel: ein paar Kreise (Planeten) zeichnen // Beispiel: ein paar Kreise (Planeten) zeichnen

View File

@ -15,7 +15,7 @@ export class GameService {
showTutorial = true; showTutorial = true;
public money = 12000; public money = 12500;
onShipCreate: EventEmitter<Ship> = new EventEmitter(); onShipCreate: EventEmitter<Ship> = new EventEmitter();
onShipDestroy: EventEmitter<Ship> = new EventEmitter(); onShipDestroy: EventEmitter<Ship> = new EventEmitter();