diff --git a/README.md b/README.md index 23fec8e..44bba95 100644 --- a/README.md +++ b/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 \ No newline at end of file diff --git a/public/sprites/planets/ignis-prime.png b/public/sprites/planets/ignis-prime.png new file mode 100644 index 0000000..76adc62 Binary files /dev/null and b/public/sprites/planets/ignis-prime.png differ diff --git a/public/sprites/planets/sm/ignis-prime.png b/public/sprites/planets/sm/ignis-prime.png new file mode 100644 index 0000000..8d3af56 Binary files /dev/null and b/public/sprites/planets/sm/ignis-prime.png differ diff --git a/public/sprites/planets/sm/terra-nova-2.png b/public/sprites/planets/sm/terra-nova-2.png new file mode 100644 index 0000000..e20e5f0 Binary files /dev/null and b/public/sprites/planets/sm/terra-nova-2.png differ diff --git a/public/sprites/planets/sm/volaris.png b/public/sprites/planets/sm/volaris.png new file mode 100644 index 0000000..e237ba9 Binary files /dev/null and b/public/sprites/planets/sm/volaris.png differ diff --git a/public/sprites/planets/terra-nova-2.png b/public/sprites/planets/terra-nova-2.png new file mode 100644 index 0000000..d0b2cc1 Binary files /dev/null and b/public/sprites/planets/terra-nova-2.png differ diff --git a/public/sprites/planets/volaris.png b/public/sprites/planets/volaris.png new file mode 100644 index 0000000..ae3a999 Binary files /dev/null and b/public/sprites/planets/volaris.png differ diff --git a/public/sprites/ships/ChatGPT Image 26. Apr. 2025, 15_10_18-Photoroom.png b/public/sprites/ships/ChatGPT Image 26. Apr. 2025, 15_10_18-Photoroom.png new file mode 100644 index 0000000..966858e Binary files /dev/null and b/public/sprites/ships/ChatGPT Image 26. Apr. 2025, 15_10_18-Photoroom.png differ diff --git a/public/sprites/ships/bulk-freighter.png b/public/sprites/ships/bulk-freighter.png new file mode 100644 index 0000000..eacfd03 Binary files /dev/null and b/public/sprites/ships/bulk-freighter.png differ diff --git a/public/sprites/ships/colony-carrier.png b/public/sprites/ships/colony-carrier.png new file mode 100644 index 0000000..d7a79a6 Binary files /dev/null and b/public/sprites/ships/colony-carrier.png differ diff --git a/public/sprites/ships/industrial-tanker.png b/public/sprites/ships/industrial-tanker.png new file mode 100644 index 0000000..251a729 Binary files /dev/null and b/public/sprites/ships/industrial-tanker.png differ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1761833..28a4c47 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -55,7 +55,7 @@ export class AppComponent { physics: { default: 'arcade', arcade: { - debug: true, + debug: false, timeScale: 1 } } diff --git a/src/app/components/dialog/planet-dialog/planet-dialog.component.html b/src/app/components/dialog/planet-dialog/planet-dialog.component.html index 302e5f0..b6b86b1 100644 --- a/src/app/components/dialog/planet-dialog/planet-dialog.component.html +++ b/src/app/components/dialog/planet-dialog/planet-dialog.component.html @@ -8,7 +8,7 @@
@if (planet.hasHarbour) { -
👥 Bevölkerung: {{ population }} 🚀
+
👥 Bevölkerung: {{ population }} {{ planet.isGrowing ? '🚀' : '⬇️' }}
Landebuchten: {{ planet.dockCapacity }}
@@ -16,7 +16,12 @@
🏭 Produktion:
@@ -41,6 +46,15 @@ + + @if (goodsInTransit.length > 0) {
📦 In Lieferung:
@@ -59,7 +73,11 @@
🏭 Produktion:
@@ -70,7 +88,10 @@ @if (planet.hasHarbour) { + >Raumhafen aufwerten + + } @else { + \ No newline at end of file diff --git a/src/app/data/planets.data.ts b/src/app/data/planets.data.ts index 369fad2..d337026 100644 --- a/src/app/data/planets.data.ts +++ b/src/app/data/planets.data.ts @@ -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 } ] } } diff --git a/src/app/model/goods/good-config.ts b/src/app/model/goods/good-config.ts index c277342..25ef79c 100644 --- a/src/app/model/goods/good-config.ts +++ b/src/app/model/goods/good-config.ts @@ -8,11 +8,11 @@ export interface GoodConfig { } export const GOODS_DATA: Record = { - [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 } }; \ No newline at end of file diff --git a/src/app/model/planet.model.ts b/src/app/model/planet.model.ts index ad0a405..4c6e315 100644 --- a/src/app/model/planet.model.ts +++ b/src/app/model/planet.model.ts @@ -11,12 +11,12 @@ export class Planet { public image: string; private goods: Map = 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 = 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 { const demand = new Map(); - - 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 { diff --git a/src/app/model/ship.ts b/src/app/model/ship.ts index 1e8b54f..1b16c89 100644 --- a/src/app/model/ship.ts +++ b/src/app/model/ship.ts @@ -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; diff --git a/src/app/model/ships/ship.model.ts b/src/app/model/ships/ship.model.ts index 7d203b9..017f731 100644 --- a/src/app/model/ships/ship.model.ts +++ b/src/app/model/ships/ship.model.ts @@ -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 { diff --git a/src/app/scene/map.scene.ts b/src/app/scene/map.scene.ts index 23620c9..07c95a6 100644 --- a/src/app/scene/map.scene.ts +++ b/src/app/scene/map.scene.ts @@ -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 diff --git a/src/app/service/game.service.ts b/src/app/service/game.service.ts index 957106a..44e668a 100644 --- a/src/app/service/game.service.ts +++ b/src/app/service/game.service.ts @@ -15,7 +15,7 @@ export class GameService { showTutorial = true; - public money = 12000; + public money = 12500; onShipCreate: EventEmitter = new EventEmitter(); onShipDestroy: EventEmitter = new EventEmitter();