img
62
README.md
@ -1,59 +1,3 @@
|
||||
# StellarLines
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.9.
|
||||
|
||||
## 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.
|
||||
Docker:
|
||||
- docker build -t bastianwagner/stellar_lines_client:latest .
|
||||
- docker push bastianwagner/stellar_lines_client:latest
|
||||
BIN
public/sprites/planets/ignis-prime.png
Normal file
|
After Width: | Height: | Size: 914 KiB |
BIN
public/sprites/planets/sm/ignis-prime.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
public/sprites/planets/sm/terra-nova-2.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/sprites/planets/sm/volaris.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/sprites/planets/terra-nova-2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/sprites/planets/volaris.png
Normal file
|
After Width: | Height: | Size: 903 KiB |
|
After Width: | Height: | Size: 737 KiB |
BIN
public/sprites/ships/bulk-freighter.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
public/sprites/ships/colony-carrier.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
public/sprites/ships/industrial-tanker.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
@ -55,7 +55,7 @@ export class AppComponent {
|
||||
physics: {
|
||||
default: 'arcade',
|
||||
arcade: {
|
||||
debug: true,
|
||||
debug: false,
|
||||
timeScale: 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
<div class="ui-body">
|
||||
@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>Landebuchten: {{ planet.dockCapacity }}</div>
|
||||
</div>
|
||||
@ -16,7 +16,12 @@
|
||||
<div>🏭 Produktion:</div>
|
||||
<ul>
|
||||
@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>
|
||||
</div>
|
||||
@ -41,6 +46,15 @@
|
||||
</ul>
|
||||
</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) {
|
||||
<div class="ui-section">
|
||||
<div>📦 In Lieferung:</div>
|
||||
@ -59,7 +73,11 @@
|
||||
<div>🏭 Produktion:</div>
|
||||
<ul>
|
||||
@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>
|
||||
</div>
|
||||
@ -70,7 +88,10 @@
|
||||
@if (planet.hasHarbour) {
|
||||
<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."
|
||||
>Raumhafen upgraden</button>
|
||||
>Raumhafen aufwerten</button>
|
||||
|
||||
<button class="button" (click)="upgradeProduction()" [disabled]="!canUpgradeProduction"
|
||||
matTooltip="Kosten: {{this.planet.productionLvlUpgradeCost | number}} Credits">Produktion aufwerten</button>
|
||||
} @else {
|
||||
<button class="button" (click)="buildHarbour()"
|
||||
matTooltip="Baue einen Raumhafen um den Planeten anfliegen zu können."
|
||||
|
||||
@ -35,7 +35,7 @@ export class PlanetDialogComponent {
|
||||
}
|
||||
|
||||
get storedItems(): Good[] {
|
||||
return this.planet.getAllGoods()
|
||||
return this.planet.getAllGoods().filter(item => item.demandRate || item.amount)
|
||||
}
|
||||
|
||||
get consumedItems(): Good[] {
|
||||
@ -51,7 +51,11 @@ export class PlanetDialogComponent {
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="ui-panel" (click)="onClick($event)">
|
||||
|
||||
<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>
|
||||
|
||||
@ -44,6 +44,10 @@
|
||||
<div>Ladegeschwindigkeit: </div>
|
||||
<div>{{ ship.loadingSpeed | number:'0.0-2' }} t/s</div>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<div>Balance:</div>
|
||||
<div>{{ ship.balance | number:'0.0-2' }} Credits</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
|
||||
|
||||
@ -45,5 +45,6 @@ export class ShipDialogComponent {
|
||||
|
||||
sell() {
|
||||
this.gameService.sellShip(this.ship);
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,25 +4,32 @@
|
||||
</div>
|
||||
|
||||
<div class="ui-section">
|
||||
<div>🚀 Neues Schiff: Pioneer-1</div>
|
||||
<div style="margin-top: 12px; font-size: 12px;">{{ config.desciption }}</div>
|
||||
<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>
|
||||
<li class="flex">
|
||||
<div>Kosten: </div>
|
||||
<div>{{ config.buyCost | number:'0.0-2'}} Credits</div>
|
||||
<div>{{ selectedShip.buyCost | number:'0.0-2'}} Credits</div>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<div>Frachtraum: </div>
|
||||
<div>{{ config.cargoSize | number: '0.0-2'}} t</div>
|
||||
<div>{{ selectedShip.cargoSize | number: '0.0-2'}} t</div>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<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 class="flex">
|
||||
<div>Unterhalt: </div>
|
||||
<div>{{ config.cost | number: '0.0-2'}} Credits</div>
|
||||
<div>{{ selectedShip.cost | number: '0.0-2'}} Credits</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -4,10 +4,11 @@ import { GameService } from '../../../service/game.service';
|
||||
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
|
||||
import { Planet } from '../../../model/planet.model';
|
||||
import { ShipConfig } from '../../../model/ships/ship.model';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-buy',
|
||||
imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle],
|
||||
imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, FormsModule],
|
||||
templateUrl: './buy.component.html',
|
||||
styleUrl: './buy.component.scss'
|
||||
})
|
||||
@ -19,13 +20,53 @@ export class BuyComponent {
|
||||
config: ShipConfig = {
|
||||
name: 'Pioneer-01',
|
||||
acceleration: 500,
|
||||
cargoSize: 10,
|
||||
cargoSize: 20,
|
||||
cost: 0.6,
|
||||
loadingSpeed: 25,
|
||||
maxSpeed: 500,
|
||||
loadingSpeed: 15,
|
||||
maxSpeed: 300,
|
||||
planetRoute: [],
|
||||
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() {
|
||||
@ -35,7 +76,7 @@ export class BuyComponent {
|
||||
buyShip() {
|
||||
if (!this.canAffordShip()) return;
|
||||
this.gameService.createShip({
|
||||
...this.config,
|
||||
...this.selectedShip,
|
||||
planetRoute: this.selectedPlanets
|
||||
})
|
||||
this.close();
|
||||
@ -57,4 +98,5 @@ export class BuyComponent {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<div (click)="onClick($event)" (mousedown)="onClick($event)" class="flex gamebar">
|
||||
|
||||
<div class="gold">
|
||||
Credits: {{gameService.money | number:'0.0-0'}}
|
||||
{{gameService.money | number:'0.0-0'}} Credits
|
||||
</div>
|
||||
|
||||
<button class="button" (click)="toggleBuy()" >Schiffe</button>
|
||||
<button class="button" (click)="toggleBuy()" >Route anlegen</button>
|
||||
</div>
|
||||
@ -3,97 +3,97 @@ import { PlanetInit } from "../model/planet.model";
|
||||
|
||||
export const PLANETCONFIGS: { x: number, y: number, texture: string, config: PlanetInit}[] = [
|
||||
{
|
||||
x: 600,
|
||||
y: 800,
|
||||
x: 900,
|
||||
y: 950,
|
||||
texture: 'terra-nova',
|
||||
config: {
|
||||
name: 'Terra Nova',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Wasser, amount: 15 },
|
||||
{ type: GoodType.Nahrung, amount: 8, productionBonus: 1.3 },
|
||||
{ type: GoodType.Wasser, amount: 10 },
|
||||
{ type: GoodType.Nahrung, amount: 10, productionBonus: 1.3 },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 3000,
|
||||
y: 900,
|
||||
x: 4000,
|
||||
y: 1500,
|
||||
texture: 'mechanica-prime',
|
||||
config: {
|
||||
name: 'Mechanica Prime',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Wasser, amount: 120 },
|
||||
{ type: GoodType.Nahrung, amount: 80 },
|
||||
{ type: GoodType.Metall, amount: 50, productionBonus: 1.5 },
|
||||
{ type: GoodType.Bauteile, amount: 20, productionBonus: 1.3 },
|
||||
{ type: GoodType.Wasser, amount: 10 },
|
||||
{ type: GoodType.Nahrung, amount: 10 },
|
||||
{ type: GoodType.Metall, amount: 10, productionBonus: 1.5 },
|
||||
{ type: GoodType.Bauteile, amount: 10, productionBonus: 1.3 },
|
||||
{ type: GoodType.Elektronik, amount: 10 }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 1800,
|
||||
y: 2800,
|
||||
y: 3800,
|
||||
texture: 'aqualis',
|
||||
config: {
|
||||
name: 'Aqualis',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Wasser, amount: 30, productionBonus: 2.0 },
|
||||
{ type: GoodType.Nahrung, amount: 15 },
|
||||
{ type: GoodType.Wasser, amount: 200, productionBonus: 2.0 },
|
||||
{ type: GoodType.Nahrung, amount: 10 },
|
||||
{ type: GoodType.Treibstoff, amount: 10 }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 3900,
|
||||
y: 2500,
|
||||
x: 4500,
|
||||
y: 5500,
|
||||
texture: 'planet',
|
||||
config: {
|
||||
name: 'Ferron',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Wasser, amount: 120 },
|
||||
{ type: GoodType.Nahrung, amount: 80 },
|
||||
{ type: GoodType.Erz, amount: 200, productionBonus: 1.8 },
|
||||
{ type: GoodType.Metall, amount: 40 },
|
||||
{ type: GoodType.Treibstoff, amount: 20 }
|
||||
{ type: GoodType.Wasser, amount: 10 },
|
||||
{ type: GoodType.Nahrung, amount: 10 },
|
||||
{ type: GoodType.Erz, amount: 10, productionBonus: 1.8 },
|
||||
{ type: GoodType.Metall, amount: 10 },
|
||||
{ type: GoodType.Treibstoff, amount: 10 }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 5400,
|
||||
y: 1400,
|
||||
x: 7400,
|
||||
y: 2400,
|
||||
texture: 'novus-reach',
|
||||
config: {
|
||||
name: 'Novus Reach',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Nahrung, amount: 90, productionBonus: 1.5 },
|
||||
{ type: GoodType.Wasser, amount: 50 },
|
||||
{ type: GoodType.Bauteile, amount: 15 }
|
||||
{ type: GoodType.Nahrung, amount: 10, productionBonus: 1.5 },
|
||||
{ type: GoodType.Wasser, amount: 10 },
|
||||
{ type: GoodType.Bauteile, amount: 10 }
|
||||
]
|
||||
}
|
||||
}
|
||||
,
|
||||
{
|
||||
x: 3700,
|
||||
y: 4600,
|
||||
texture: 'novus-reach',
|
||||
y: 7600,
|
||||
texture: 'volaris',
|
||||
config: {
|
||||
name: 'Novus Reach 2',
|
||||
name: 'Volaris',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Nahrung, amount: 90, productionBonus: 1.5 },
|
||||
{ type: GoodType.Wasser, amount: 50 },
|
||||
{ type: GoodType.Bauteile, amount: 15 }
|
||||
{ type: GoodType.Nahrung, amount: 10 },
|
||||
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.1 },
|
||||
{ type: GoodType.Bauteile, amount: 10 }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 6000,
|
||||
y: 3600,
|
||||
texture: 'terra-nova',
|
||||
x: 8000,
|
||||
y: 6600,
|
||||
texture: 'ignis-prime',
|
||||
config: {
|
||||
name: 'Terra Nova 2',
|
||||
name: 'Ignis Prime',
|
||||
initialGoods: [
|
||||
{ type: GoodType.Wasser, amount: 120 },
|
||||
{ type: GoodType.Nahrung, amount: 80, productionBonus: 1.3 },
|
||||
{ type: GoodType.Erz, amount: 100 }
|
||||
{ type: GoodType.Wasser, amount: 10 },
|
||||
{ type: GoodType.Nahrung, amount: 10 },
|
||||
{ type: GoodType.Treibstoff, amount: 10, productionBonus: 0.8 }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ export interface 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.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.Elektronik]: { baseProduction: 1, baseDemand: 0, storageLimit: 100, isRawResource: false },
|
||||
[GoodType.Bauteile]: { 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.001, storageLimit: 100, isRawResource: false }
|
||||
};
|
||||
@ -11,12 +11,12 @@ export class Planet {
|
||||
public image: string;
|
||||
|
||||
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;
|
||||
private populationGrowthRate = 0.002; // Basiswachstum pro Tick (%)
|
||||
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 dockedShips: Ship[] = [];
|
||||
@ -60,13 +60,56 @@ export class Planet {
|
||||
this.dockCapacity = 1;
|
||||
}, 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 {
|
||||
this.goods.forEach((good: Good, key: string) => {
|
||||
|
||||
|
||||
this.adjustPopulationDemands(good)
|
||||
|
||||
const lvlMultiplier = this.productionLevel.get(good.type) ?? 1;
|
||||
good.amount += good.productionRate * seconds * lvlMultiplier;
|
||||
good.amount -= good.demandRate * seconds;
|
||||
|
||||
// Min 0
|
||||
good.amount = Math.max(0, good.amount);
|
||||
@ -77,23 +120,11 @@ export class Planet {
|
||||
|
||||
this.updatePopulation(seconds)
|
||||
|
||||
|
||||
return;
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
@ -151,19 +181,9 @@ export class Planet {
|
||||
}
|
||||
|
||||
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}) {
|
||||
const offers = this.offeredGoods;
|
||||
if (offers.length == 0) { return 0; }
|
||||
@ -225,14 +245,18 @@ export class Planet {
|
||||
*/
|
||||
private calculateNaturalDemand(seconds: number): Map<GoodType, number> {
|
||||
const demand = new Map<GoodType, number>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for (let good of this.getAllGoods()) {
|
||||
demand.set(good.type ,this.population * good.demandRate * 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;
|
||||
}
|
||||
|
||||
@ -280,9 +304,7 @@ export class Planet {
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
@ -309,6 +331,36 @@ export class Planet {
|
||||
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 {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { MapScene } from "../scene/map.scene";
|
||||
import { GameService } from "../service/game.service";
|
||||
import { PlanetUi } from "../ui/planet.ui";
|
||||
import { TradeInstance } from "./planet.model";
|
||||
import { FlightMode, Ship } from "./ships/ship.model";
|
||||
|
||||
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
|
||||
|
||||
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.model = ship;
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ export interface ShipConfig {
|
||||
planetRoute: Planet[];
|
||||
buyCost: number;
|
||||
desciption: string;
|
||||
texture: string;
|
||||
}
|
||||
|
||||
export class Ship {
|
||||
@ -29,6 +30,10 @@ export class Ship {
|
||||
public cost = 0.5
|
||||
public buyCost = 0;
|
||||
public description: string = "";
|
||||
texture: string;
|
||||
|
||||
public totalCost: number = 0;
|
||||
public totalEarnings: number = 0;
|
||||
|
||||
private updateInterval = interval(1000).subscribe(() => {
|
||||
this.update();
|
||||
@ -44,7 +49,10 @@ export class Ship {
|
||||
this.cost = config.cost;
|
||||
this.buyCost = config.buyCost;
|
||||
this.description = config.desciption;
|
||||
this.texture = config.texture;
|
||||
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) {
|
||||
const good = planet.getGood(dgood.type);
|
||||
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);
|
||||
|
||||
}
|
||||
@ -101,7 +109,7 @@ export class Ship {
|
||||
|
||||
for (let demand of demands) {
|
||||
// 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; };
|
||||
demand.amount = Math.min(demand.amount, this.freeCargoSpace);
|
||||
demand.amount = Math.max(demand.amount, 0)
|
||||
@ -118,18 +126,21 @@ export class Ship {
|
||||
}
|
||||
|
||||
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++) {
|
||||
const existing = this.cargoSpace.find(c => c.type == cargo.type);
|
||||
if (existing) {
|
||||
existing.amount -= (cargo.amount / steps);
|
||||
to.amount += (cargo.amount / steps);
|
||||
this.gameService.money += 5;
|
||||
if (cargo) {
|
||||
cargo.amount -= transferPerStep;
|
||||
to.amount += transferPerStep;
|
||||
this.gameService.money += 18 * transferPerStep;
|
||||
this.totalEarnings += 18 * transferPerStep;
|
||||
}
|
||||
await this.waitForLoading(1)
|
||||
}
|
||||
|
||||
to.amount += cargo.amount;
|
||||
cargo.amount = 0;
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
@ -138,7 +149,7 @@ export class Ship {
|
||||
const steps = Math.ceil(cargo.amount)
|
||||
|
||||
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) {
|
||||
existing.amount += (cargo.amount / steps);
|
||||
} else {
|
||||
@ -166,12 +177,17 @@ export class Ship {
|
||||
|
||||
update() {
|
||||
this.gameService.money -= this.cost;
|
||||
this.totalCost += this.cost;
|
||||
}
|
||||
|
||||
|
||||
sell() {
|
||||
this.updateInterval.unsubscribe();
|
||||
}
|
||||
|
||||
get balance(): number {
|
||||
return this.totalEarnings - this.totalCost;
|
||||
}
|
||||
}
|
||||
|
||||
export enum FlightMode {
|
||||
|
||||
@ -21,10 +21,14 @@ export class MapScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
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('planet', 'sprites/planets/planet-1.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('novus-reach', 'sprites/planets/novus-reach.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++) {
|
||||
const x = 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
|
||||
|
||||
@ -15,7 +15,7 @@ export class GameService {
|
||||
|
||||
showTutorial = true;
|
||||
|
||||
public money = 12000;
|
||||
public money = 12500;
|
||||
|
||||
onShipCreate: EventEmitter<Ship> = new EventEmitter();
|
||||
onShipDestroy: EventEmitter<Ship> = new EventEmitter();
|
||||
|
||||