balancing

This commit is contained in:
Bastian Wagner 2025-05-21 23:25:40 +02:00
parent c0ec36ba82
commit 798e61318f
43 changed files with 481 additions and 147 deletions

View File

@ -3,9 +3,9 @@ version: '3.8'
services: services:
shattered_kingdom_client: shattered_kingdom_client:
image: bastianwagner/stellar_lines_client:latest image: bastianwagner/stellar-lines:latest
pull_policy: always pull_policy: always
container_name: stellar_lines_client container_name: stellar-lines
ports: ports:
- "3900:80" - "3900:80"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 909 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@ -14,4 +14,6 @@
@if (gameService.showTutorial) { @if (gameService.showTutorial) {
<app-welcome (click)="stopPropagation($event)" (mousedown)="stopPropagation($event)" /> <app-welcome (click)="stopPropagation($event)" (mousedown)="stopPropagation($event)" />
} }
<app-trade-route cdkDrag cdkDragBoundary="html" (click)="stopPropagation($event)"/>

View File

@ -13,12 +13,13 @@ import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de'; import localeDeExtra from '@angular/common/locales/extra/de';
import { WelcomeComponent } from './components/tutorial/welcome/welcome.component'; import { WelcomeComponent } from './components/tutorial/welcome/welcome.component';
import { TradeRouteComponent } from './components/dialog/trade-route/trade-route.component';
registerLocaleData(localeDe, 'de-DE', localeDeExtra); registerLocaleData(localeDe, 'de-DE', localeDeExtra);
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [MatDialogModule, ShipDialogComponent, PlanetDialogComponent, DragDropModule, StatusBarComponent, BuyComponent, WelcomeComponent], imports: [MatDialogModule, ShipDialogComponent, PlanetDialogComponent, DragDropModule, StatusBarComponent, BuyComponent, WelcomeComponent, TradeRouteComponent],
providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }], providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.scss' styleUrl: './app.component.scss'

View File

@ -1,6 +1,6 @@
<div class="ui-panel"> <div class="ui-panel">
<div class="ui-title" cdkDragHandle> <div class="ui-title" cdkDragHandle>
<div class="image"><img [src]="'sprites/planets/sm/'+ planet.image +'.png'" alt=""></div> <div class="image"><img [src]="'sprites/planets/'+ planet.image +'.png'" alt=""></div>
<div>{{ planet.name }}</div> <div>{{ planet.name }}</div>
</div> </div>

View File

@ -0,0 +1,54 @@
<div class="ui-panel">
<div class="ui-title" cdkDragHandle>
<div>Handelsrouten</div>
</div>
<div class="ui-body">
@if (availableRoutes.length > 0) {
<div class="ui-section" style="display: flex; flex-direction: column; gap: 4px;">
<h4>Routen:</h4>
<div class="flex">
@for (route of availableRoutes; track $index) {
<button (click)="selectRoute(route)" class="button" [class.active-button]="selectedRoute == route">Route {{ $index }}</button>
}
</div>
<div>oder: </div>
<button class="button" (click)="newRoute()">Neue Route erstellen</button>
</div>
}
<div class="ui-section">
<h4 matTooltip="Füge Planeten durch anklicken zur Route hinzu">Verfügbare Planeten:</h4>
<div style="max-height: 100px; overflow: auto;">
@for(planet of availablePlanets;track planet.name) {
<button class="button" (click)="addPlanet(planet)" >{{ planet.name }}</button>
}
</div>
<div>
<h4 matTooltip="Die Planeten werden von oben nach unten der Reihe nach angeflogen.">Route:</h4>
<div class="ui-section">
<div cdkDropList (cdkDropListDropped)="drop($event)" class="ui-section" >
@for(planet of selectedPlanets; track $index) {
<div class="flex">
<div cdkDrag>{{ planet.name }}</div>
<button class="button" (click)="remove(planet)" >X</button>
</div>
}
</div>
</div>
</div>
</div>
</div>
<button class="button" (click)="save()">Speichern</button>
</div>

View File

@ -0,0 +1,17 @@
:host {
position: absolute;
display: flex;
flex-direction: column;
position: absolute;
top: 24px;
left: 24px;
width: 600px;
// height: 240px;
// background-color: var(--background-color);
// color: var(--primary-color);
z-index: 1;
}
h4 {
margin: 0;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TradeRouteComponent } from './trade-route.component';
describe('TradeRouteComponent', () => {
let component: TradeRouteComponent;
let fixture: ComponentFixture<TradeRouteComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TradeRouteComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TradeRouteComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,64 @@
import { CdkDrag, CdkDragDrop, CdkDropList, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
import { CommonModule } from '@angular/common';
import { Component, inject } from '@angular/core';
import { GameService } from '../../../service/game.service';
import { Planet } from '../../../model/planet.model';
import { TradeRoute } from '../../../model/routes/trade-route.model';
import { MatTooltipModule } from '@angular/material/tooltip';
@Component({
selector: 'app-trade-route',
imports: [CommonModule, DragDropModule, CdkDropList, CdkDrag, MatTooltipModule],
templateUrl: './trade-route.component.html',
styleUrl: './trade-route.component.scss'
})
export class TradeRouteComponent {
private gameService: GameService = inject(GameService);
selectedPlanets: Planet[] = [];
selectedRoute: TradeRoute = new TradeRoute([]);
constructor() {}
get availablePlanets(): Planet[] {
return this.gameService.planets.filter(p => p.hasHarbour || true)
}
get availableRoutes(): TradeRoute[] {
return this.gameService.tradeRoutes;
}
addPlanet(planet: Planet) {
this.selectedPlanets.push(planet);
console.log(this.selectedPlanets)
}
remove(planet: Planet) {
this.selectedPlanets = this.selectedPlanets.filter(p => p != planet)
}
drop(event: CdkDragDrop<string[]>) {
moveItemInArray(this.selectedPlanets, event.previousIndex, event.currentIndex);
}
selectRoute(route: TradeRoute) {
this.selectedRoute = route;
this.selectedPlanets = route.getPlanets();
}
save() {
this.selectedRoute.setPlanets(this.selectedPlanets);
this.gameService.tradeRoutes.push(this.selectedRoute);
this.newRoute();
}
newRoute() {
this.selectRoute(new TradeRoute([]))
}
}

View File

@ -12,6 +12,10 @@
</select> </select>
</div> </div>
<div>🚀 Neues Schiff: {{ selectedShip.name }}</div> <div>🚀 Neues Schiff: {{ selectedShip.name }}</div>
<mat-progress-bar
class="example-margin"
[value]="shipBuildProgress">
</mat-progress-bar>
<div style="margin-top: 12px; font-size: 12px;">{{ selectedShip.desciption }}</div> <div style="margin-top: 12px; font-size: 12px;">{{ selectedShip.desciption }}</div>
<ul> <ul>
<li class="flex"> <li class="flex">
@ -85,7 +89,7 @@
<div class="ui-section flex"> <div class="ui-section flex">
<button class="button" <button class="button"
[disabled]="selectedPlanets.length < 2 || !canAffordShip()" [disabled]="selectedPlanets.length < 2 || !canAffordShip() || shipBuildProgress > 0"
(click)="buyShip()"> (click)="buyShip()">
Schiff kaufen Schiff kaufen
</button> </button>

View File

@ -6,10 +6,11 @@ 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'; import { FormsModule } from '@angular/forms';
import { SHIP_DATA } from '../../../model/ships/ship.type'; import { SHIP_DATA } from '../../../model/ships/ship.type';
import {MatProgressBarModule} from '@angular/material/progress-bar';
@Component({ @Component({
selector: 'app-buy', selector: 'app-buy',
imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, FormsModule], imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, FormsModule, MatProgressBarModule],
templateUrl: './buy.component.html', templateUrl: './buy.component.html',
styleUrl: './buy.component.scss' styleUrl: './buy.component.scss'
}) })
@ -50,13 +51,16 @@ export class BuyComponent {
return this.gameService.money >= this.config.buyCost; return this.gameService.money >= this.config.buyCost;
} }
get shipBuildProgress(): number {
return this.gameService.planets.find(p => p.spaceShipLauncher != undefined)?.spaceShipLauncher?.buildProcess ?? 0;
}
buyShip() { buyShip() {
if (!this.canAffordShip()) return; if (!this.canAffordShip()) return;
this.gameService.createShip({ this.gameService.createShip({
...this.selectedShip, ...this.selectedShip,
planetRoute: this.selectedPlanets planetRoute: this.selectedPlanets
}) })
this.close();
} }
close() { close() {

View File

@ -1,97 +1,135 @@
import { GoodType } from "../model/goods/good-type.enum"; import { GoodType } from "../model/goods/good-type.enum";
import { PlanetInit } from "../model/planet.model"; 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 }[] = [
{ // Kernzone
x: 900, { x: 10000, y: 10000, texture: 'terra-nova', config: { name: 'Terra Nova', initialGoods: [
y: 950, { type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
texture: 'terra-nova', { type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 },
config: { { type: GoodType.Erz, amount: 10, productionBonus: 0.1 },
name: 'Terra Nova', ]}},
initialGoods: [ { x: 12500, y: 10200, texture: 'mechanica-prime', config: { name: 'Mechanica Prime', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.18 }, { type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.3 }, { type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 }, { type: GoodType.Erz, amount: 10, productionBonus: 1.7 },
] ]}},
} { x: 9700, y: 12500, texture: 'volaris', config: { name: 'Volaris', initialGoods: [
}, { type: GoodType.Wasser, amount: 10, productionBonus: 0.3 },
{ { type: GoodType.Nahrung, amount: 10, productionBonus: 2.0 },
x: 4000, { type: GoodType.Erz, amount: 10, productionBonus: 0 },
y: 1500, ]}},
texture: 'mechanica-prime', { x: 7500, y: 9700, texture: 'ferron', config: { name: 'Ferron', initialGoods: [
config: { { type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
name: 'Mechanica Prime', { type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
initialGoods: [ { type: GoodType.Erz, amount: 10, productionBonus: 2.0 },
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.07 }, ]}},
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.05 }, { x: 12000, y: 7600, texture: 'solaris', config: { name: 'Solaris', initialGoods: [
{ type: GoodType.Erz, amount: 10, productionBonus: 1.5 }, { type: GoodType.Wasser, amount: 10, productionBonus: 0.4 },
] { type: GoodType.Nahrung, amount: 10, productionBonus: 2.4 },
} { type: GoodType.Erz, amount: 10, productionBonus: 0 },
}, ]}},
{ { x: 8000, y: 12500, texture: 'arboris', config: { name: 'Arboris', initialGoods: [
x: 1800, { type: GoodType.Wasser, amount: 10, productionBonus: 1.6 },
y: 3800, { type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
texture: 'aqualis', { type: GoodType.Erz, amount: 10, productionBonus: 0.2 },
config: { ]}},
name: 'Aqualis',
initialGoods: [ // Mittelzone
{ type: GoodType.Wasser, amount: 10, productionBonus: 2.5 }, { x: 19000, y: 10000, texture: 'aqualis', config: { name: 'Aqualis', initialGoods: [
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.06 }, { type: GoodType.Wasser, amount: 10, productionBonus: 2.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.2 } { type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
] { type: GoodType.Erz, amount: 10, productionBonus: 0 },
} ]}},
}, { x: 16500, y: 16000, texture: 'dryad', config: { name: 'Dryad', initialGoods: [
{ { type: GoodType.Wasser, amount: 10, productionBonus: 2.0 },
x: 4500, { type: GoodType.Nahrung, amount: 10, productionBonus: 0.1 },
y: 5500, { type: GoodType.Erz, amount: 10, productionBonus: 0 },
texture: 'planet', ]}},
config: { { x: 15000, y: 8500, texture: 'ignis-prime', config: { name: 'Ignis Prime', initialGoods: [
name: 'Ferron', { type: GoodType.Wasser, amount: 10, productionBonus: 1.5 },
initialGoods: [ { type: GoodType.Nahrung, amount: 10, productionBonus: 0.4 },
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.02 }, { type: GoodType.Erz, amount: 10, productionBonus: 0.3 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.03 }, ]}},
{ type: GoodType.Erz, amount: 10, productionBonus: 2.2 } { x: 11000, y: 19500, texture: 'novus-reach', config: { name: 'Novus Reach', initialGoods: [
] { type: GoodType.Wasser, amount: 10, productionBonus: 1.8 },
} { type: GoodType.Nahrung, amount: 10, productionBonus: 0.3 },
}, { type: GoodType.Erz, amount: 10, productionBonus: 0.5 },
{ ]}},
x: 7400, { x: 5000, y: 15000, texture: 'borealis', config: { name: 'Borealis', initialGoods: [
y: 2400, { type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
texture: 'novus-reach', { type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
config: { { type: GoodType.Erz, amount: 10, productionBonus: 1.5 },
name: 'Novus Reach', ]}},
initialGoods: [ { x: 3000, y: 10000, texture: 'flamara', config: { name: 'Flamara', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.6 }, { type: GoodType.Wasser, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.07 }, { type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.1 } { type: GoodType.Erz, amount: 10, productionBonus: 0 },
] ]}},
} { x: 14000, y: 4000, texture: 'celestia', config: { name: 'Celestia', initialGoods: [
} { type: GoodType.Wasser, amount: 10, productionBonus: 2.0 },
, { type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
{ { type: GoodType.Erz, amount: 10, productionBonus: 0.2 },
x: 3700, ]}},
y: 7600, { x: 9000, y: 3000, texture: 'maridia', config: { name: 'Maridia', initialGoods: [
texture: 'volaris', { type: GoodType.Wasser, amount: 10, productionBonus: 1.8 },
config: { { type: GoodType.Nahrung, amount: 10, productionBonus: 0.4 },
name: 'Volaris', { type: GoodType.Erz, amount: 10, productionBonus: 0 },
initialGoods: [ ]}},
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.25 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 }, // Außenzone
{ type: GoodType.Erz, amount: 10, productionBonus: 0 } { x: 26000, y: 10000, texture: 'crystallia', config: { name: 'Crystallia', initialGoods: [
] { type: GoodType.Wasser, amount: 10, productionBonus: 0.3 },
} { type: GoodType.Nahrung, amount: 10, productionBonus: 2.0 },
}, { type: GoodType.Erz, amount: 10, productionBonus: 0 },
{ ]}},
x: 8000, { x: 22000, y: 19000, texture: 'draco', config: { name: 'Draco', initialGoods: [
y: 6600, { type: GoodType.Wasser, amount: 10, productionBonus: 0.05 },
texture: 'ignis-prime', { type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
config: { { type: GoodType.Erz, amount: 10, productionBonus: 2.5 },
name: 'Ignis Prime', ]}},
initialGoods: [ { x: 18000, y: 26000, texture: 'titanus', config: { name: 'Titanus', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.3 }, { type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.06 }, { type: GoodType.Nahrung, amount: 10, productionBonus: 0.3 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.1 } { type: GoodType.Erz, amount: 10, productionBonus: 2.5 },
] ]}},
} { x: 10000, y: 27000, texture: 'zephyra', config: { name: 'Zephyra', initialGoods: [
}, { type: GoodType.Wasser, amount: 10, productionBonus: 0.3 },
] { type: GoodType.Nahrung, amount: 10, productionBonus: 2.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 1000, y: 22000, texture: 'aurora', config: { name: 'Aurora', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.6 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.0 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.2 },
]}},
{ x: 500, y: 14000, texture: 'luminis', config: { name: 'Luminis', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 2.3 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.4 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 2000, y: 5000, texture: 'eclipse', config: { name: 'Eclipse', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.0 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 7000, y: 500, texture: 'umbra', config: { name: 'Umbra', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 2.2 },
]}},
{ x: 14000, y: 1000, texture: 'ignis-minor', config: { name: 'Ignis Minor', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.3 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.3 },
]}},
{ x: 21000, y: 4000, texture: 'nautilus', config: { name: 'Nautilus', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 2.3 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 25000, y: 7000, texture: 'vespera', config: { name: 'Vespera', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
];

View File

@ -0,0 +1,54 @@
import { inject } from "@angular/core";
import { MapScene } from "../../scene/map.scene";
import { GameService } from "../../service/game.service";
import { Planet } from "../planet.model";
import { Ship } from "../ships/ship.model";
import { ShipUi } from "../ship";
export class SpaceshipLauncher {
durationInSeconds = 10;
x: number;
y: number;
private planet: Planet;
private gameService: GameService;
public buildProcess: number = 0;
constructor(config: {planet: Planet, x: number, y: number, gameService: GameService}) {
this.planet = config.planet;
this.gameService = config.gameService;
this.x = config.x;
this.y = config.y;
}
async buildShip(scene: MapScene, ship: Ship) {
await this.waitForBuilding();
const ui = new ShipUi(scene, this.x, this.y, this.gameService, ship);
ui.activateRoute();
scene.physics.world.enable(ui);
scene.ships.push(ui);
this.buildProcess = 0;
}
private async waitForBuilding() {
this.buildProcess = 0;
return new Promise(async resolve=> {
for (let i = 0; i < this.durationInSeconds * 100; i++) {
this.buildProcess += 0.10;
await this.timeout(10);
}
resolve(null)
})
}
private async timeout(ms: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve(null);
}, ms)
})
}
}

View File

@ -9,11 +9,11 @@ export interface GoodConfig {
} }
export const GOODS_DATA: Record<GoodType, GoodConfig> = { export const GOODS_DATA: Record<GoodType, GoodConfig> = {
[GoodType.Wasser]: { baseProduction: 1, baseDemand: 0.0012, storageLimit: 500, isRawResource: true }, [GoodType.Wasser]: { baseProduction: 1, baseDemand: 0.0052, storageLimit: 500, isRawResource: true },
[GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 300, isRawResource: true }, [GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.0041, storageLimit: 300, isRawResource: true },
[GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0.0005, storageLimit: 200, isRawResource: true }, [GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0.0015, storageLimit: 200, isRawResource: true },
[GoodType.Metall]: { baseProduction: 0.7, baseDemand: 0.003, storageLimit: 300, isRawResource: false, dependsOn: [GoodType.Erz, GoodType.Wasser] }, [GoodType.Metall]: { baseProduction: 0.7, baseDemand: 0.0003, storageLimit: 300, isRawResource: false, dependsOn: [GoodType.Erz, GoodType.Wasser] },
[GoodType.Treibstoff]: { baseProduction: 0.5, baseDemand: 0.002, storageLimit: 150, isRawResource: false, dependsOn: [GoodType.Wasser] }, [GoodType.Treibstoff]: { baseProduction: 0.5, baseDemand: 0.0002, storageLimit: 150, isRawResource: false, dependsOn: [GoodType.Wasser] },
[GoodType.Elektronik]: { baseProduction: 0.3, baseDemand: 0.001, storageLimit: 100, isRawResource: false, dependsOn: [GoodType.Bauteile, GoodType.Treibstoff] }, [GoodType.Elektronik]: { baseProduction: 0.3, baseDemand: 0.0001, storageLimit: 100, isRawResource: false, dependsOn: [GoodType.Bauteile, GoodType.Treibstoff] },
[GoodType.Bauteile]: { baseProduction: 0.5, baseDemand: 0.0015, storageLimit: 100, isRawResource: false, dependsOn: [GoodType.Metall] } [GoodType.Bauteile]: { baseProduction: 0.5, baseDemand: 0.00015, storageLimit: 100, isRawResource: false, dependsOn: [GoodType.Metall] }
}; };

View File

@ -5,6 +5,7 @@ import { Good } from "./goods/good.interface";
import { Ship } from "./ships/ship.model"; import { Ship } from "./ships/ship.model";
import { GameService } from "../service/game.service"; import { GameService } from "../service/game.service";
import { Factory } from "./factories/factory.model"; import { Factory } from "./factories/factory.model";
import { SpaceshipLauncher } from "./factories/spaceship-launcher.model";
export class Planet { export class Planet {
public population: number = 100; public population: number = 100;
@ -30,12 +31,26 @@ export class Planet {
private gameService: GameService; private gameService: GameService;
hasHarbour = false; hasHarbour = false;
spaceShipLauncher: SpaceshipLauncher | undefined;
private config: {
x: number;
y: number;
texture: string;
config: PlanetInit;
};
constructor(config: PlanetInit, gameService: GameService) { constructor(data: {
this.name = config.name; x: number;
this.image = config.name.toLowerCase().split(' ').join('-') y: number;
texture: string;
config: PlanetInit;
}, gameService: GameService) {
this.name = data.config.name;
this.image = data.config.name.toLowerCase().split(' ').join('-')
this.gameService = gameService; this.gameService = gameService;
this.config = data;
/** set all empty */ /** set all empty */
Object.values(GoodType).forEach(c => { Object.values(GoodType).forEach(c => {
this.goods.set(c, { this.goods.set(c, {
@ -48,7 +63,7 @@ export class Planet {
}) })
config.initialGoods.forEach(good => { data.config.initialGoods.forEach(good => {
const base = GOODS_DATA[good.type]; const base = GOODS_DATA[good.type];
this.goods.set(good.type, { this.goods.set(good.type, {
type: good.type, type: good.type,
@ -58,7 +73,6 @@ export class Planet {
productionStorage: base.storageLimit productionStorage: base.storageLimit
}) })
}); });
console.log(this.getAllGoods())
this.updatePopulation(0) this.updatePopulation(0)
setTimeout(() => { setTimeout(() => {
@ -194,7 +208,7 @@ export class Planet {
} }
result.push({ result.push({
amount: demand * 5 - good.amount - inTransit, // Request 3x buffer amount amount: demand * 2 - good.amount - inTransit, // Request 3x buffer amount
type: good.type, type: good.type,
target: this.name, target: this.name,
}) })
@ -238,7 +252,8 @@ export class Planet {
}); });
if (allSupplied) { if (allSupplied) {
this.population += this.population * this.populationGrowthRate * seconds; const grow = Phaser.Math.Between(this.populationGrowthRate * 0.8 * 100000, this.populationGrowthRate * 1.2 * 100000) / 100000;
this.population += this.population * grow * seconds;
this.isGrowing = true; this.isGrowing = true;
} else { } else {
this.isGrowing = false; this.isGrowing = false;
@ -351,6 +366,18 @@ export class Planet {
buildHarbour() { buildHarbour() {
this.hasHarbour = true; this.hasHarbour = true;
this.gameService.money -= 30000; this.gameService.money -= 30000;
if (!this.gameService.planets.some(p => p.spaceShipLauncher)) {
this.buildSpaceshipLauncher();
}
}
buildSpaceshipLauncher() {
this.spaceShipLauncher = new SpaceshipLauncher({
planet: this,
x: this.config.x,
y: this.config.y,
gameService: this.gameService
});
} }
upgradeProduction() { upgradeProduction() {
@ -392,6 +419,7 @@ export class Planet {
public getStatus(): void { public getStatus(): void {
return;
console.log(`[${this.name}]: ${this.isGrowing ? '🚀' : '⬇️'}`); console.log(`[${this.name}]: ${this.isGrowing ? '🚀' : '⬇️'}`);
console.log('Population:', Math.round(this.population)); console.log('Population:', Math.round(this.population));
console.log('Goods:\n', Array.from(this.goods.entries()).map(([type, good]) => console.log('Goods:\n', Array.from(this.goods.entries()).map(([type, good]) =>

View File

@ -1,11 +1,17 @@
import { Planet, TradeInstance } from "../planet.model" import { Planet, TradeInstance } from "../planet.model"
import { Ship } from "../ships/ship.model";
export class TradeRoute { export class TradeRoute {
private route: ITradePlanet[] = []; private route: ITradePlanet[] = [];
private _ships: Ship[] = [];
private target!: ITradePlanet; private target!: ITradePlanet;
constructor(route: Planet[]) { constructor(route: Planet[]) {
this.setPlanets(route);
}
setPlanets(route: Planet[]) {
const r: any[] = route.map(r => { return {target: r, next: null }}) const r: any[] = route.map(r => { return {target: r, next: null }})
if (route.length < 2) { return;} if (route.length < 2) { return;}
@ -17,6 +23,10 @@ export class TradeRoute {
this.route = r; this.route = r;
} }
getPlanets() {
return this.route.map(r => r.target);
}
get nextPlanetName(): string { get nextPlanetName(): string {
return this.target?.target.name ?? ''; return this.target?.target.name ?? '';
} }

View File

@ -19,8 +19,8 @@ export interface ShipType {
export const SHIP_DATA: Record<ShipClass, ShipType> = { export const SHIP_DATA: Record<ShipClass, ShipType> = {
[ShipClass.light]: { name: 'Pioneer-01', cargoSize: 25, loadingSpeed: 15, maxSpeed: 350, acceleration: 300, class: ShipClass.light, buyCost: 3000, cost: 0.6, texture: 'swift-hauler', desciption: 'Ein kleines, schnelles Schiff. Es hat wenig Platz für Ladung, lädt aber schnell und ist sehr wendig.' }, [ShipClass.light]: { name: 'Pioneer-01', cargoSize: 25, loadingSpeed: 15, maxSpeed: 350, acceleration: 300, class: ShipClass.light, buyCost: 2000, cost: 0.6, texture: 'swift-hauler', desciption: 'Ein kleines, schnelles Schiff. Es hat wenig Platz für Ladung, lädt aber schnell und ist sehr wendig.' },
[ShipClass.medium]: { name: 'Colony Carrier', cargoSize: 80, loadingSpeed: 20, maxSpeed: 250, acceleration: 90, class: ShipClass.medium, buyCost: 8000, cost: 1.6, texture: 'colony-carrier', desciption: 'Eine größere Version von der Pioneer-01. Der größere Frachtraum geht zu Lasten der Beschleunigung und Maximalgeschwindigkeit.' }, [ShipClass.medium]: { name: 'Colony Carrier', cargoSize: 80, loadingSpeed: 20, maxSpeed: 250, acceleration: 90, class: ShipClass.medium, buyCost: 5000, cost: 1.6, texture: 'colony-carrier', desciption: 'Eine größere Version von der Pioneer-01. Der größere Frachtraum geht zu Lasten der Beschleunigung und Maximalgeschwindigkeit.' },
[ShipClass.heavy]: { name: 'Industrial Tanker', cargoSize: 400, loadingSpeed: 11, maxSpeed: 180, acceleration: 20, class: ShipClass.heavy, buyCost: 18000, cost: 3.8, texture: 'industrial-tanker', desciption: 'Ein großes behäbiges Schiff. Es wird durchaus schnell, beschleunigt aber sehr langsam und erreicht die Spitzengeschwindigkeit nur selten.' } [ShipClass.heavy]: { name: 'Industrial Tanker', cargoSize: 400, loadingSpeed: 11, maxSpeed: 180, acceleration: 20, class: ShipClass.heavy, buyCost: 12000, cost: 3.8, texture: 'industrial-tanker', desciption: 'Ein großes behäbiges Schiff. Es wird durchaus schnell, beschleunigt aber sehr langsam und erreicht die Spitzengeschwindigkeit nur selten.' }
} }

View File

@ -10,7 +10,7 @@ export class MapScene extends Phaser.Scene {
private isDragging = false; private isDragging = false;
private dragStart = new Phaser.Math.Vector2(); private dragStart = new Phaser.Math.Vector2();
private ship!: ShipUi; // Das Raumschiff private ship!: ShipUi; // Das Raumschiff
private ships: ShipUi[] = []; public ships: ShipUi[] = [];
private planets: PlanetUi[] = []; private planets: PlanetUi[] = [];
@ -24,15 +24,22 @@ export class MapScene extends Phaser.Scene {
this.load.image('swift-hauler', '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('colony-carrier', 'sprites/ships/colony-carrier.png');
this.load.image('industrial-tanker', 'sprites/ships/industrial-tanker.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('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('volaris', 'sprites/planets/volaris.png');
this.load.image('ignis-prime', 'sprites/planets/ignis-prime.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');
this.load.image('ferron', 'sprites/planets/ferron.png'); // this.load.image('ferron', 'sprites/planets/ferron.png');
this.load.image('harbour', 'sprites/buildings/harbour.png');
this.load.image('spaceship-launch-place', 'sprites/buildings/spaceship-launch-place.png');
this.loadPlanetSprites()
} }
create() { create() {
@ -40,17 +47,17 @@ export class MapScene extends Phaser.Scene {
this.createPlaceHolderGraphic(); this.createPlaceHolderGraphic();
// this.camera.setBackgroundColor('0x99ccff') // this.camera.setBackgroundColor('0x99ccff')
const worldSize = 30000;
// Weltgröße groß setzen, z.B. 5000x5000 Pixel // Weltgröße groß setzen, z.B. 5000x5000 Pixel
this.camera.setBounds(0, 0, 10000, 10000); this.camera.setBounds(0, 0, worldSize, worldSize);
this.physics.world.setBounds(0, 0, 10000, 10000); this.physics.world.setBounds(0, 0, worldSize, worldSize);
this.cameras.main.setBounds(0, 0, 10000, 10000); this.cameras.main.setBounds(0, 0, worldSize, worldSize);
/* Sterne */ /* Sterne */
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, worldSize);
const y = Phaser.Math.Between(0, 10000); const y = Phaser.Math.Between(0, worldSize);
this.add.circle(x, y, Phaser.Math.Between(2, 10), 0x88ccff) this.add.circle(x, y, Phaser.Math.Between(2, 10), 0x88ccff)
} }
@ -90,20 +97,22 @@ export class MapScene extends Phaser.Scene {
this.camera.setZoom(this.camera.zoom * factor); this.camera.setZoom(this.camera.zoom * factor);
} }
const minScale = Math.max(window.innerWidth / 10000, window.innerHeight / 10000) const minScale = Math.max(window.innerWidth / worldSize, window.innerHeight / worldSize)
// Begrenze Zoom // Begrenze Zoom
this.camera.setZoom(Phaser.Math.Clamp(this.camera.zoom, minScale, 1)); this.camera.setZoom(Phaser.Math.Clamp(this.camera.zoom, minScale, 1));
}); });
this.camera.setZoom(0.4) this.camera.setZoom(0.4)
// this.camera.setPosition(1000, 1000)
this.camera.scrollX = 10000;
this.camera.scrollY = 10000;
this.gameService.onShipCreate.subscribe(ship => { this.gameService.onShipCreate.subscribe(ship => {
const ui = new ShipUi(this, 100, 100, this.gameService, ship); const factory = this.gameService.planets.find(p => p.spaceShipLauncher)?.spaceShipLauncher;
ui.activateRoute(); if (!factory) { return; }
this.physics.world.enable(ui); factory.buildShip(this, ship)
this.ships.push(ui);
}) })
this.gameService.onShipDestroy.subscribe(ship => { this.gameService.onShipDestroy.subscribe(ship => {
@ -130,7 +139,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, this.gameService); const planet = new PlanetUi(this, config.x, config.y, config.texture, 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;
@ -171,4 +180,11 @@ export class MapScene extends Phaser.Scene {
mouseText.setText(`X: ${pointer.worldX.toFixed(0)}\nY: ${pointer.worldY.toFixed(0)}`); mouseText.setText(`X: ${pointer.worldX.toFixed(0)}\nY: ${pointer.worldY.toFixed(0)}`);
}); });
} }
loadPlanetSprites() {
const planets = PLANETCONFIGS;
for (let planet of planets) {
this.load.image(planet.texture, 'sprites/planets/' + planet.texture + '.png');
}
}
} }

View File

@ -1,6 +1,7 @@
import { EventEmitter, Injectable } from "@angular/core"; import { EventEmitter, Injectable } from "@angular/core";
import { Planet } from "../model/planet.model"; import { Planet } from "../model/planet.model";
import { Ship, ShipConfig } from "../model/ships/ship.model"; import { Ship, ShipConfig } from "../model/ships/ship.model";
import { TradeRoute } from "../model/routes/trade-route.model";
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -9,7 +10,8 @@ export class GameService {
public showPlanetInfo: Planet | undefined; public showPlanetInfo: Planet | undefined;
public showShipInfo: Ship | undefined; public showShipInfo: Ship | undefined;
public showBuyShip = false; public showBuyShip = false;
public tradeRoutes: TradeRoute[] = [];
public ships: Ship[] = []; public ships: Ship[] = [];
public planets: Planet[] = []; public planets: Planet[] = [];
@ -31,7 +33,7 @@ export class GameService {
} }
get canDrag(): boolean { get canDrag(): boolean {
return this.showShipInfo == undefined && this.showPlanetInfo == undefined; return this.showShipInfo == undefined && this.showPlanetInfo == undefined && !this.showBuyShip;
} }

View File

@ -3,6 +3,7 @@ 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"; import { GameService } from "../service/game.service";
import { MapScene } from "../scene/map.scene";
export class PlanetUi extends Phaser.Physics.Arcade.Sprite { export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
@ -10,12 +11,16 @@ export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
public rightClick: EventEmitter<PlanetUi> = new EventEmitter(); public rightClick: EventEmitter<PlanetUi> = new EventEmitter();
public model: Planet; public model: Planet;
private status: PlanetStatus | undefined; private status: PlanetStatus | undefined;
private harbourImage!: any; private harbourImage!: Phaser.GameObjects.Image;
private spaceshipLauncherImage!: Phaser.GameObjects.Image;
private gameService: GameService;
private updateInterval = interval(1000) private updateInterval = interval(1000)
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, config: any, gameService: GameService) { 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.gameService = gameService;
// this.setDisplaySize(200, 200); // this.setDisplaySize(200, 200);
@ -43,10 +48,17 @@ export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
this.harbourImage = this.scene.add.image(this.getWorldPoint().x, this.getWorldPoint().y, 'harbour') this.harbourImage = this.scene.add.image(this.getWorldPoint().x, this.getWorldPoint().y, 'harbour')
this.harbourImage.setDisplaySize(164, 256); this.harbourImage.setDisplaySize(256, 256);
this.harbourImage.setVisible(false); this.harbourImage.setVisible(false);
console.log(this.height) this.spaceshipLauncherImage = this.scene.add.image(this.getWorldPoint().x + 260, this.getWorldPoint().y, 'spaceship-launch-place')
this.spaceshipLauncherImage.setScale(0.1)
this.spaceshipLauncherImage.setVisible(false);
this.spaceshipLauncherImage.setInteractive({ useHandCursor: true });
this.spaceshipLauncherImage.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
this.gameService.showBuyShip = true;
});
// this.setScale(0.5) // this.setScale(0.5)
@ -71,7 +83,8 @@ export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
} }
this.status?.update(); this.status?.update();
this.harbourImage.setVisible(this.model.hasHarbour) this.harbourImage.setVisible(this.model.hasHarbour);
this.spaceshipLauncherImage.setVisible(this.model.spaceShipLauncher != undefined);
} }

View File

@ -100,6 +100,10 @@ $font-size-base: 14px;
@include button; @include button;
} }
.active-button {
background-color: $color-success;
}
.ui-section { .ui-section {
padding: 16px; padding: 16px;