Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions resources/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
"d_troops": "Defending troops",
"a_troops": "Attacking troops",
"gold": "Gold",
"productivity": "Productivity",
"ports": "Ports",
"cities": "Cities",
"hospitals": "Hospitals",
Expand Down Expand Up @@ -467,5 +468,21 @@
},
"heads_up_message": {
"choose_spawn": "Choose a starting location"
},
"buttons": {
"focus": "Focus",
"accept": "Accept",
"reject": "Reject",
"propose_renewal": "Propose to renew",
"i_want_to_renew": "I want to renew"
},
"alliance": {
"requested": "{name} requests an alliance!",
"renewed": "Alliance successfully renewed.",
"request_accepted": "{name} accepted your alliance request",
"request_rejected": "{name} rejected your alliance request",
"about_to_expire": "Your alliance with {name} is about to expire",
"expired": "Your alliance with {name} expired",
"no_alliance_to_extend": "No alliance to extend."
}
}
16 changes: 16 additions & 0 deletions resources/lang/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -409,5 +409,21 @@
"stop_trade": "Stop handel",
"yes": "Ja",
"no": "Nee"
},
"buttons": {
"focus": "Focus",
"accept": "Accepteren",
"reject": "Weigeren",
"propose_renewal": "Verlenging voorstellen",
"i_want_to_renew": "Ik wil verlengen"
},
"alliance": {
"requested": "{name} vraagt om een alliantie!",
"renewed": "Alliantie succesvol verlengd.",
"request_accepted": "{name} heeft je alliantieverzoek geaccepteerd",
"request_rejected": "{name} heeft je alliantieverzoek geweigerd",
"about_to_expire": "Je alliantie met {name} staat op het punt te verlopen",
"expired": "Je alliantie met {name} is verlopen",
"no_alliance_to_extend": "Er is geen alliantie om te verlengen."
}
}
39 changes: 16 additions & 23 deletions src/client/NewsModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,33 +51,26 @@ export class NewsModal extends LitElement {
<div class="news-container">
<div class="news-content">
<p>
This test version introduces a new building:
<strong>Hospitals</strong>. Each hospital restores some of
your troop losses from both offensive and defensive combat.
The restored troops are displayed next to your population
growth count in your control panel.
This test version introduces a new mechanic:
<strong>Investment</strong>.
</p>
<p>
The first hospital provides a
<strong>10% reduction</strong> in combat casualties. Each
additional hospital reduces losses by
<strong>75% of the previous reduction</strong>.
A new <strong>Investment Slider</strong> lets you dedicate a
portion of your nation's gold to productivity growth. Gold
spent on investment is subtracted before any other expenses.
</p>
<ul>
<li>1st hospital: 10% reduction</li>
<li>2nd hospital: 7.5% additional reduction</li>
<li>3rd hospital: 5.6% additional reduction</li>
<li>... and so on</li>
</ul>
<p>These effects stack cumulatively.</p>
<p>
For a full list of changes, join the
<a
href="https://discord.com/channels/1379151032369676338/1379156389699649566"
target="_blank"
>
Discord </a
>.
The more you invest, the faster your
<strong>worker productivity</strong> increases—boosting your
long-term gold income. Productivity grows gradually and
compounds over time, meaning consistent investment can lead to
a powerful economic advantage.
</p>
<p>
Nuclear strikes now <strong>reduce productivity</strong>
proportionally to the number of tiles you lose. This creates
longer-term economic damage beyond just troop and worker
losses.
</p>
</div>
</div>
Expand Down
32 changes: 32 additions & 0 deletions src/client/Transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export class SendAllianceReplyIntentEvent implements GameEvent {
) {}
}

export class SendAllianceExtensionIntentEvent implements GameEvent {
constructor(public readonly recipient: PlayerView) {}
}

export class SendSpawnIntentEvent implements GameEvent {
constructor(public readonly cell: Cell) {}
}
Expand Down Expand Up @@ -141,6 +145,9 @@ export class CancelBoatIntentEvent implements GameEvent {
export class SendSetTargetTroopRatioEvent implements GameEvent {
constructor(public readonly ratio: number) {}
}
export class SendSetInvestmentRateEvent implements GameEvent {
constructor(public readonly rate: number) {}
}

export class SendWinnerEvent implements GameEvent {
constructor(
Expand Down Expand Up @@ -193,6 +200,9 @@ export class Transport {
this.eventBus.on(SendBreakAllianceIntentEvent, (e) =>
this.onBreakAllianceRequestUIEvent(e),
);
this.eventBus.on(SendAllianceExtensionIntentEvent, (e) =>
this.onSendAllianceExtensionIntent(e),
);
this.eventBus.on(SendSpawnIntentEvent, (e) =>
this.onSendSpawnIntentEvent(e),
);
Expand All @@ -217,6 +227,10 @@ export class Transport {
this.eventBus.on(SendSetTargetTroopRatioEvent, (e) =>
this.onSendSetTargetTroopRatioEvent(e),
);
this.eventBus.on(SendSetInvestmentRateEvent, (e) =>
this.onSendSetInvestmentRateEvent(e),
);

this.eventBus.on(BuildUnitIntentEvent, (e) => this.onBuildUnitIntent(e));

this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e));
Expand Down Expand Up @@ -408,6 +422,16 @@ export class Transport {
});
}

private onSendAllianceExtensionIntent(
event: SendAllianceExtensionIntentEvent,
) {
this.sendIntent({
type: "allianceExtension",
clientID: this.lobbyConfig.clientID,
recipient: event.recipient.id(),
});
}

private onSendSpawnIntentEvent(event: SendSpawnIntentEvent) {
this.sendIntent({
type: "spawn",
Expand Down Expand Up @@ -503,6 +527,14 @@ export class Transport {
});
}

private onSendSetInvestmentRateEvent(event: SendSetInvestmentRateEvent) {
this.sendIntent({
type: "investment_rate",
clientID: this.lobbyConfig.clientID,
rate: event.rate,
});
}

private onBuildUnitIntent(event: BuildUnitIntentEvent) {
this.sendIntent({
type: "build_unit",
Expand Down
1 change: 1 addition & 0 deletions src/client/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export function getMessageTypeClasses(type: MessageType): string {
case MessageType.SAM_MISS:
case MessageType.ALLIANCE_EXPIRED:
case MessageType.NAVAL_INVASION_INBOUND:
case MessageType.WARN:
return severityColors["warn"];
case MessageType.CHAT:
case MessageType.ALLIANCE_REQUEST:
Expand Down
5 changes: 4 additions & 1 deletion src/client/graphics/GameRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export function createRenderer(
): GameRenderer {
const transformHandler = new TransformHandler(game, eventBus, canvas);

const uiState = { attackRatio: 20 };
const uiState: UIState = {
attackRatio: 0.2, // 20% as a float
investmentRate: 0.5, // 50% default investment rate
};

//hide when the game renders
const startingModal = document.querySelector(
Expand Down
1 change: 1 addition & 0 deletions src/client/graphics/UIState.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface UIState {
attackRatio: number;
investmentRate: number;
}
60 changes: 59 additions & 1 deletion src/client/graphics/layers/ControlPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { EventBus } from "../../../core/EventBus";
import { Gold } from "../../../core/game/Game";
import { GameView } from "../../../core/game/GameView";
import { AttackRatioEvent } from "../../InputHandler";
import { SendSetTargetTroopRatioEvent } from "../../Transport";
import {
SendSetInvestmentRateEvent,
SendSetTargetTroopRatioEvent,
} from "../../Transport";
import { renderNumber, renderTroops } from "../../Utils";
import { UIState } from "../UIState";
import { Layer } from "./Layer";
Expand All @@ -25,6 +28,9 @@ export class ControlPanel extends LitElement implements Layer {
@state()
private currentTroopRatio = 0.6;

@state()
private investmentRate: number = 0.5; // default to 50%

@state()
private _population: number;

Expand Down Expand Up @@ -52,6 +58,12 @@ export class ControlPanel extends LitElement implements Layer {
@state()
private _gold: Gold;

@state()
private _productivity: number;

@state()
private _productivityGrowth: number;

@state()
private _goldPerSecond: Gold;

Expand All @@ -68,6 +80,10 @@ export class ControlPanel extends LitElement implements Layer {
this.targetTroopRatio = Number(
localStorage.getItem("settings.troopRatio") ?? "0.6",
);
this.investmentRate = Number(
localStorage.getItem("settings.investmentRate") ?? "0.5",
);
this.uiState.investmentRate = this.investmentRate;
this.init_ = true;
this.uiState.attackRatio = this.attackRatio;
this.currentTroopRatio = this.targetTroopRatio;
Expand Down Expand Up @@ -102,6 +118,7 @@ export class ControlPanel extends LitElement implements Layer {
this.eventBus.emit(
new SendSetTargetTroopRatioEvent(this.targetTroopRatio),
);
this.eventBus.emit(new SendSetInvestmentRateEvent(this.investmentRate));
this.init_ = false;
}

Expand All @@ -126,6 +143,8 @@ export class ControlPanel extends LitElement implements Layer {
this._maxPopulation = this.game.config().maxPopulation(player);
this._hospitalReturns = player.hospitalReturns() * 10;
this._gold = player.gold();
this._productivity = player.productivity();
this._productivityGrowth = player.productivityGrowthPerMinute();
this._troops = player.troops();
this._workers = player.workers();
this.popRate = this.game.config().populationIncreaseRate(player) * 10;
Expand All @@ -138,6 +157,9 @@ export class ControlPanel extends LitElement implements Layer {
onAttackRatioChange(newRatio: number) {
this.uiState.attackRatio = newRatio;
}
onInvestmentRateChange(newRate: number) {
this.eventBus.emit(new SendSetInvestmentRateEvent(newRate));
}

renderLayer(context: CanvasRenderingContext2D) {
// Render any necessary canvas elements
Expand Down Expand Up @@ -310,6 +332,42 @@ export class ControlPanel extends LitElement implements Layer {
/>
</div>
</div>
<div class="relative mt-4 lg:mb-4">
<label class="block text-white mb-1" translate="no">
Production Investment Rate:
${(this.investmentRate * 100).toFixed(0)}%
</label>
<div
class="text-white text-right text-xs opacity-60 mt-1"
translate="no"
>
Prod: ${Math.round(this._productivity * 100)}%
(${this._productivityGrowth >= 0 ? "+" : ""}${(
this._productivityGrowth * 100
).toFixed(1)}%/min)
</div>
<div class="relative h-8">
<div
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded"
></div>
<div
class="absolute left-0 top-3 h-2 bg-green-400/60 rounded transition-all duration-300"
style="width: ${this.investmentRate * 100}%"
></div>
<input
type="range"
min="0"
max="100"
.value=${(this.investmentRate * 100).toString()}
@input=${(e: Event) => {
this.investmentRate =
parseInt((e.target as HTMLInputElement).value) / 100;
this.onInvestmentRateChange(this.investmentRate);
}}
class="absolute left-0 right-0 top-2 m-0 h-4 cursor-pointer"
/>
</div>
</div>
</div>
`;
}
Expand Down
Loading
Loading