-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: allow trade ships to trade with own ports at reduced gold #3785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,14 +21,17 @@ export class TradeShipExecution implements Execution { | |
| private tilesTraveled = 0; | ||
| private motionPlanId = 1; | ||
| private motionPlanDst: TileRef | null = null; | ||
| private readonly isSelfTrade: boolean; | ||
|
|
||
| private static _staggerCounter = 0; | ||
|
|
||
| constructor( | ||
| private origOwner: Player, | ||
| private srcPort: Unit, | ||
| private _dstPort: Unit, | ||
| ) {} | ||
| ) { | ||
| this.isSelfTrade = srcPort.owner() === _dstPort.owner(); | ||
| } | ||
|
|
||
| init(mg: Game, ticks: number): void { | ||
| this.mg = mg; | ||
|
|
@@ -71,20 +74,19 @@ export class TradeShipExecution implements Execution { | |
| this.wasCaptured = true; | ||
| } | ||
|
|
||
| // If a player captures another player's port while trading we should delete | ||
| // the ship. | ||
| if (dstPortOwner.id() === this.srcPort.owner().id()) { | ||
| this.tradeShip.delete(false); | ||
| this.active = false; | ||
| return; | ||
| } | ||
|
|
||
| if ( | ||
| !this.wasCaptured && | ||
| (!this._dstPort.isActive() || !tradeShipOwner.canTrade(dstPortOwner)) | ||
| ) { | ||
| this.tradeShip.delete(false); | ||
| this.active = false; | ||
| const dstActive = this._dstPort.isActive(); | ||
| const shouldCancel = | ||
| // Non-self-trade: cancel if destination port was captured back, or | ||
| // (not yet captured) if trade is no longer viable | ||
| (!this.isSelfTrade && | ||
| (dstPortOwner.id() === this.srcPort.owner().id() || | ||
| (!this.wasCaptured && | ||
| (!dstActive || !tradeShipOwner.canTrade(dstPortOwner))))) || | ||
| // Self-trade: cancel if destination port is no longer active or was captured | ||
| (this.isSelfTrade && | ||
| (!dstActive || dstPortOwner !== this.srcPort.owner())); | ||
| if (shouldCancel) { | ||
| this.cancelTrade(); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -106,8 +108,7 @@ export class TradeShipExecution implements Execution { | |
| this.mg.hasWaterComponent(port.tile(), myComponent), | ||
| ); | ||
| if (nearestPort === null) { | ||
| this.tradeShip.delete(false); | ||
| this.active = false; | ||
| this.cancelTrade(); | ||
| return; | ||
| } else { | ||
| this._dstPort = nearestPort; | ||
|
|
@@ -165,10 +166,15 @@ export class TradeShipExecution implements Execution { | |
| } | ||
| } | ||
|
|
||
| private cancelTrade() { | ||
| this.tradeShip!.delete(false); | ||
| this.active = false; | ||
| } | ||
|
|
||
| private complete() { | ||
| this.active = false; | ||
| this.tradeShip!.delete(false); | ||
| const gold = this.mg | ||
| let gold = this.mg | ||
| .config() | ||
| .tradeShipGold(this.tilesTraveled, this.tradeShip!.owner()); | ||
|
|
||
|
|
@@ -188,6 +194,25 @@ export class TradeShipExecution implements Execution { | |
| this.mg | ||
| .stats() | ||
| .boatCapturedTrade(this.tradeShip!.owner(), this.origOwner, gold); | ||
| } else if (this.isSelfTrade) { | ||
| // Self-trade: reduced gold, credited once | ||
| const multiplier = this.mg.config().tradeShipSelfGoldMultiplier(); | ||
| gold = BigInt(Math.floor(Number(gold) * multiplier)); | ||
| this.srcPort.owner().addGold(gold, this._dstPort.tile()); | ||
| this.mg.displayMessage( | ||
| "events_display.received_gold_from_trade", | ||
| MessageType.RECEIVED_GOLD_FROM_TRADE, | ||
| this.srcPort.owner().id(), | ||
| gold, | ||
| { | ||
| gold: renderNumber(gold), | ||
| name: this.srcPort.owner().displayName(), | ||
| }, | ||
| ); | ||
| // Record stats | ||
| this.mg | ||
| .stats() | ||
| .boatArriveTrade(this.srcPort.owner(), this._dstPort.owner(), gold); | ||
|
Comment on lines
+197
to
+215
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-trade message shows the player their own name. The cross-player branch below shows the source the destination player's name and vice versa. Here the recipient is 🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eh, This should be fine.. Too many different chat labels might get confusing for the player
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(⳹´・ω・)ノ 🧠 Learnings used |
||
| } else { | ||
| this.srcPort.owner().addGold(gold); | ||
| this._dstPort.owner().addGold(gold, this._dstPort.tile()); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Captured self-trade ship cannot reroute when destination is also lost.
For non-self trades, the
(!this.wasCaptured && (!dstActive || !canTrade))guard lets a captured ship fall through to the rerouting block at lines 95–118, so the new owner's ship can pick another friendly port instead of being deleted. The self-trade branch has no!this.wasCapturedguard, so if the trade ship gets captured by player C and the destination port is also lost (deactivated, or taken by yet another player D), this block deletes the captured ship instead of letting the captured-ship logic find one of C's ports.Mirror the non-self path so capture takes precedence over the self-trade ownership check.
🛡️ Suggested fix
This also flattens the condition into three clear cases, which is easier to read than the combined boolean expression.
🤖 Prompt for AI Agents