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:
shattered_kingdom_client:
image: bastianwagner/stellar_lines_client:latest
image: bastianwagner/stellar-lines:latest
pull_policy: always
container_name: stellar_lines_client
container_name: stellar-lines
ports:
- "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

@ -15,3 +15,5 @@
@if (gameService.showTutorial) {
<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 localeDeExtra from '@angular/common/locales/extra/de';
import { WelcomeComponent } from './components/tutorial/welcome/welcome.component';
import { TradeRouteComponent } from './components/dialog/trade-route/trade-route.component';
registerLocaleData(localeDe, 'de-DE', localeDeExtra);
@Component({
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' }],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'

View File

@ -1,6 +1,6 @@
<div class="ui-panel">
<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>

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>
</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>
<ul>
<li class="flex">
@ -85,7 +89,7 @@
<div class="ui-section flex">
<button class="button"
[disabled]="selectedPlanets.length < 2 || !canAffordShip()"
[disabled]="selectedPlanets.length < 2 || !canAffordShip() || shipBuildProgress > 0"
(click)="buyShip()">
Schiff kaufen
</button>

View File

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

View File

@ -1,97 +1,135 @@
import { GoodType } from "../model/goods/good-type.enum";
import { PlanetInit } from "../model/planet.model";
export const PLANETCONFIGS: { x: number, y: number, texture: string, config: PlanetInit}[] = [
{
x: 900,
y: 950,
texture: 'terra-nova',
config: {
name: 'Terra Nova',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.18 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.3 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]
}
},
{
x: 4000,
y: 1500,
texture: 'mechanica-prime',
config: {
name: 'Mechanica Prime',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.07 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.05 },
{ type: GoodType.Erz, amount: 10, productionBonus: 1.5 },
]
}
},
{
x: 1800,
y: 3800,
texture: 'aqualis',
config: {
name: 'Aqualis',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 2.5 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.06 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.2 }
]
}
},
{
x: 4500,
y: 5500,
texture: 'planet',
config: {
name: 'Ferron',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.02 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.03 },
{ type: GoodType.Erz, amount: 10, productionBonus: 2.2 }
]
}
},
{
x: 7400,
y: 2400,
texture: 'novus-reach',
config: {
name: 'Novus Reach',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.6 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.07 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.1 }
]
}
}
,
{
x: 3700,
y: 7600,
texture: 'volaris',
config: {
name: 'Volaris',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.25 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 }
]
}
},
{
x: 8000,
y: 6600,
texture: 'ignis-prime',
config: {
name: 'Ignis Prime',
initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.3 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.06 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.1 }
]
}
},
]
export const PLANETCONFIGS: { x: number, y: number, texture: string, config: PlanetInit }[] = [
// Kernzone
{ x: 10000, y: 10000, texture: 'terra-nova', config: { name: 'Terra Nova', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.1 },
]}},
{ x: 12500, y: 10200, texture: 'mechanica-prime', config: { name: 'Mechanica Prime', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
{ 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 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 7500, y: 9700, texture: 'ferron', config: { name: 'Ferron', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Erz, amount: 10, productionBonus: 2.0 },
]}},
{ x: 12000, y: 7600, texture: 'solaris', config: { name: 'Solaris', initialGoods: [
{ 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: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.6 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.2 },
]}},
// Mittelzone
{ x: 19000, y: 10000, texture: 'aqualis', config: { name: 'Aqualis', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 2.5 },
{ 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 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
{ x: 15000, y: 8500, texture: 'ignis-prime', config: { name: 'Ignis Prime', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.4 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0.3 },
]}},
{ 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: 5000, y: 15000, texture: 'borealis', config: { name: 'Borealis', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.2 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 1.5 },
]}},
{ x: 3000, y: 10000, texture: 'flamara', config: { name: 'Flamara', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.5 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 2.2 },
{ 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: 9000, y: 3000, texture: 'maridia', config: { name: 'Maridia', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 1.8 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.4 },
{ type: GoodType.Erz, amount: 10, productionBonus: 0 },
]}},
// Außenzone
{ 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: 22000, y: 19000, texture: 'draco', config: { name: 'Draco', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.05 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.5 },
{ type: GoodType.Erz, amount: 10, productionBonus: 2.5 },
]}},
{ x: 18000, y: 26000, texture: 'titanus', config: { name: 'Titanus', initialGoods: [
{ type: GoodType.Wasser, amount: 10, productionBonus: 0.1 },
{ type: GoodType.Nahrung, amount: 10, productionBonus: 0.3 },
{ 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> = {
[GoodType.Wasser]: { baseProduction: 1, baseDemand: 0.0012, storageLimit: 500, isRawResource: true },
[GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.001, storageLimit: 300, isRawResource: true },
[GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0.0005, storageLimit: 200, isRawResource: true },
[GoodType.Metall]: { baseProduction: 0.7, baseDemand: 0.003, 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.Elektronik]: { baseProduction: 0.3, baseDemand: 0.001, 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.Wasser]: { baseProduction: 1, baseDemand: 0.0052, storageLimit: 500, isRawResource: true },
[GoodType.Nahrung]: { baseProduction: 1, baseDemand: 0.0041, storageLimit: 300, isRawResource: true },
[GoodType.Erz]: { baseProduction: 1.0, baseDemand: 0.0015, storageLimit: 200, isRawResource: true },
[GoodType.Metall]: { baseProduction: 0.7, baseDemand: 0.0003, storageLimit: 300, isRawResource: false, dependsOn: [GoodType.Erz, GoodType.Wasser] },
[GoodType.Treibstoff]: { baseProduction: 0.5, baseDemand: 0.0002, storageLimit: 150, isRawResource: false, dependsOn: [GoodType.Wasser] },
[GoodType.Elektronik]: { baseProduction: 0.3, baseDemand: 0.0001, storageLimit: 100, isRawResource: false, dependsOn: [GoodType.Bauteile, GoodType.Treibstoff] },
[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 { GameService } from "../service/game.service";
import { Factory } from "./factories/factory.model";
import { SpaceshipLauncher } from "./factories/spaceship-launcher.model";
export class Planet {
public population: number = 100;
@ -30,12 +31,26 @@ export class Planet {
private gameService: GameService;
hasHarbour = false;
spaceShipLauncher: SpaceshipLauncher | undefined;
private config: {
x: number;
y: number;
texture: string;
config: PlanetInit;
};
constructor(config: PlanetInit, gameService: GameService) {
this.name = config.name;
this.image = config.name.toLowerCase().split(' ').join('-')
constructor(data: {
x: number;
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.config = data;
/** set all empty */
Object.values(GoodType).forEach(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];
this.goods.set(good.type, {
type: good.type,
@ -58,7 +73,6 @@ export class Planet {
productionStorage: base.storageLimit
})
});
console.log(this.getAllGoods())
this.updatePopulation(0)
setTimeout(() => {
@ -194,7 +208,7 @@ export class Planet {
}
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,
target: this.name,
})
@ -238,7 +252,8 @@ export class Planet {
});
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;
} else {
this.isGrowing = false;
@ -351,6 +366,18 @@ export class Planet {
buildHarbour() {
this.hasHarbour = true;
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() {
@ -392,6 +419,7 @@ export class Planet {
public getStatus(): void {
return;
console.log(`[${this.name}]: ${this.isGrowing ? '🚀' : '⬇️'}`);
console.log('Population:', Math.round(this.population));
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 { Ship } from "../ships/ship.model";
export class TradeRoute {
private route: ITradePlanet[] = [];
private _ships: Ship[] = [];
private target!: ITradePlanet;
constructor(route: Planet[]) {
this.setPlanets(route);
}
setPlanets(route: Planet[]) {
const r: any[] = route.map(r => { return {target: r, next: null }})
if (route.length < 2) { return;}
@ -17,6 +23,10 @@ export class TradeRoute {
this.route = r;
}
getPlanets() {
return this.route.map(r => r.target);
}
get nextPlanetName(): string {
return this.target?.target.name ?? '';
}

View File

@ -19,8 +19,8 @@ export interface 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.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.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.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: 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: 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 dragStart = new Phaser.Math.Vector2();
private ship!: ShipUi; // Das Raumschiff
private ships: ShipUi[] = [];
public ships: ShipUi[] = [];
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('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');
this.load.image('ferron', 'sprites/planets/ferron.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');
// 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() {
@ -40,17 +47,17 @@ export class MapScene extends Phaser.Scene {
this.createPlaceHolderGraphic();
// this.camera.setBackgroundColor('0x99ccff')
const worldSize = 30000;
// Weltgröße groß setzen, z.B. 5000x5000 Pixel
this.camera.setBounds(0, 0, 10000, 10000);
this.physics.world.setBounds(0, 0, 10000, 10000);
this.cameras.main.setBounds(0, 0, 10000, 10000);
this.camera.setBounds(0, 0, worldSize, worldSize);
this.physics.world.setBounds(0, 0, worldSize, worldSize);
this.cameras.main.setBounds(0, 0, worldSize, worldSize);
/* Sterne */
for (let i = 0; i< 1000; i++) {
const x = Phaser.Math.Between(0, 10000);
const y = Phaser.Math.Between(0, 10000);
const x = Phaser.Math.Between(0, worldSize);
const y = Phaser.Math.Between(0, worldSize);
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);
}
const minScale = Math.max(window.innerWidth / 10000, window.innerHeight / 10000)
const minScale = Math.max(window.innerWidth / worldSize, window.innerHeight / worldSize)
// Begrenze Zoom
this.camera.setZoom(Phaser.Math.Clamp(this.camera.zoom, minScale, 1));
});
this.camera.setZoom(0.4)
// this.camera.setPosition(1000, 1000)
this.camera.scrollX = 10000;
this.camera.scrollY = 10000;
this.gameService.onShipCreate.subscribe(ship => {
const ui = new ShipUi(this, 100, 100, this.gameService, ship);
ui.activateRoute();
this.physics.world.enable(ui);
this.ships.push(ui);
const factory = this.gameService.planets.find(p => p.spaceShipLauncher)?.spaceShipLauncher;
if (!factory) { return; }
factory.buildShip(this, ship)
})
this.gameService.onShipDestroy.subscribe(ship => {
@ -130,7 +139,7 @@ export class MapScene extends Phaser.Scene {
}
private buildPlanets() {
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 => {
this.ship.moveTo(planet);
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)}`);
});
}
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 { Planet } from "../model/planet.model";
import { Ship, ShipConfig } from "../model/ships/ship.model";
import { TradeRoute } from "../model/routes/trade-route.model";
@Injectable({
providedIn: 'root',
@ -10,6 +11,7 @@ export class GameService {
public showShipInfo: Ship | undefined;
public showBuyShip = false;
public tradeRoutes: TradeRoute[] = [];
public ships: Ship[] = [];
public planets: Planet[] = [];
@ -31,7 +33,7 @@ export class GameService {
}
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 { PlanetStatus } from "./planet-status.ui";
import { GameService } from "../service/game.service";
import { MapScene } from "../scene/map.scene";
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 model: Planet;
private status: PlanetStatus | undefined;
private harbourImage!: any;
private harbourImage!: Phaser.GameObjects.Image;
private spaceshipLauncherImage!: Phaser.GameObjects.Image;
private gameService: GameService;
private updateInterval = interval(1000)
constructor(scene: Phaser.Scene, x: number, y: number, texture: string, config: any, gameService: GameService) {
super(scene, x, y, texture);
this.gameService = gameService;
// 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.setDisplaySize(164, 256);
this.harbourImage.setDisplaySize(256, 256);
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)
@ -71,7 +83,8 @@ export class PlanetUi extends Phaser.Physics.Arcade.Sprite {
}
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;
}
.active-button {
background-color: $color-success;
}
.ui-section {
padding: 16px;