Skip to content

Feat: Transport boats now show arrival ETA#3381

Open
camclark wants to merge 11 commits intoopenfrontio:mainfrom
camclark:feature/boat-eta-countdown
Open

Feat: Transport boats now show arrival ETA#3381
camclark wants to merge 11 commits intoopenfrontio:mainfrom
camclark:feature/boat-eta-countdown

Conversation

@camclark
Copy link

@camclark camclark commented Mar 8, 2026

Description:

Adds a progress bar to transport boats in the attacks display panel, showing journey completion for both outgoing and incoming boats. A countdown overlay appears for the final 5 seconds before arrival.
Resolves #1793

CleanShot 2026-03-11 at 23 02 11

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

cool_clarky

@CLAassistant
Copy link

CLAassistant commented Mar 8, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

Walkthrough

Adds a client-side ETA estimator and per-boat countdowns/progress bars in AttacksDisplay, introduces a seconds-abbrev localization key, and adds tests for the estimator. Changes are client-side UI and tests only.

Changes

Cohort / File(s) Summary
Localization
resources/lang/en.json
Adds events_display.seconds_abbrev = "{seconds}s" for abbreviated seconds display.
Client UI: Boat ETA & Progress
src/client/graphics/layers/AttacksDisplay.ts
Adds exported estimateBoatEtaSeconds(remainingTicks, turnIntervalMs): number, private getBoatEtaSeconds and getBoatProgress, and renderProgressBar; integrates ETA/progress rendering into outgoing/incoming boat lists.
Tests
tests/client/graphics/layers/BoatEta.test.ts
New unit tests for estimateBoatEtaSeconds and getBoatEtaSeconds, covering normal, edge, and invalid-input scenarios with a mocked AttacksDisplay/game.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

⛵ Seconds tick beside each keel,
Small numbers tell us where they'll heel,
A neat green bar, a red one too,
Countdown whispers, "land on cue",
Little timers make the maps feel real.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR implements a progress bar with 5-second countdown overlay instead of the requested per-second numeric countdown format ('Boat: 1.91K 10s X'). While both show arrival information, the implementation diverges from the explicit specification in #1793. Clarify whether the progress bar with countdown overlay satisfies the requirements of #1793 or if the numeric countdown format was intentionally changed during implementation review and approved.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat: Transport boats now show arrival ETA' directly and clearly describes the main feature: adding estimated time of arrival display for boats.
Description check ✅ Passed The description explains the feature adds a progress bar and countdown overlay to transport boats, references the resolved issue #1793, includes required confirmations, and provides contact information.
Out of Scope Changes check ✅ Passed All changes are scoped to displaying boat arrival information: a localization entry, ETA calculation functions, progress bar rendering, and comprehensive tests. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/client/graphics/layers/AttacksDisplay.ts (1)

27-33: Extract estimateBoatEtaSeconds into a plain helper module.

tests/client/graphics/layers/BoatEta.test.ts now has to import the whole Lit element module just to test a pure function, which also pulls in custom-element registration and asset imports. A small utility file would keep this test simpler and side-effect free.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/AttacksDisplay.ts` around lines 27 - 33, Move the
pure function estimateBoatEtaSeconds out of the Lit element file into a small
plain helper module that exports the same named function; in AttacksDisplay.ts
replace the local implementation with an import of estimateBoatEtaSeconds from
the new helper (or re-export it) so external callers keep working, and update
tests to import the helper module directly instead of importing the whole Lit
element module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 363-370: In getBoatEtaSeconds, replace the Manhattan-distance
based ETA with one computed from the boat's actual remaining route length:
instead of calling this.game.manhattanDist(boat.tile(), targetTile), obtain the
boat's remaining path/route length (e.g., via a method on UnitView like
boat.remainingRouteLength() or boat.route()/boat.path() that returns remaining
steps) or ask the game's pathfinder for the current path length from boat.tile()
to boat.targetTile(), then pass that step count into estimateBoatEtaSeconds so
ETA reflects route steps around land/chokepoints (update references in
getBoatEtaSeconds and remove the manhattanDist usage).

---

Nitpick comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 27-33: Move the pure function estimateBoatEtaSeconds out of the
Lit element file into a small plain helper module that exports the same named
function; in AttacksDisplay.ts replace the local implementation with an import
of estimateBoatEtaSeconds from the new helper (or re-export it) so external
callers keep working, and update tests to import the helper module directly
instead of importing the whole Lit element module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80c72ee4-2eb1-4bf8-83a9-ec856cdd2177

📥 Commits

Reviewing files that changed from the base of the PR and between a4b3d13 and 9a8e486.

📒 Files selected for processing (3)
  • resources/lang/en.json
  • src/client/graphics/layers/AttacksDisplay.ts
  • tests/client/graphics/layers/BoatEta.test.ts

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Mar 8, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/graphics/layers/AttacksDisplay.ts (1)

387-425: ⚠️ Potential issue | 🟠 Major

Drive the ETA refresh from a 1s wall-clock tick.

This renders the new countdown, but the layer still does not opt into a 1-second wall-clock schedule. As a result, both outgoing and incoming boat ETAs will follow the default layer tick cadence instead of the PR requirement of updating once per second.

Suggested fix
 export class AttacksDisplay extends LitElement implements Layer {
   public eventBus: EventBus;
   public game: GameView;
   public uiState: UIState;
@@
   tick() {
     this.active = true;
@@
     this.requestUpdate();
   }
+
+  getTickIntervalMs(): number {
+    return 1000;
+  }
 
   shouldTransform(): boolean {
     return false;
   }

Based on learnings, UI/layer updates should be wall-clock throttled independently of simulation speed, and layers should expose getTickIntervalMs() for that.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/AttacksDisplay.ts` around lines 387 - 425, The
layer currently renders ETA countdowns but doesn't opt into a 1s wall-clock
schedule; add/override getTickIntervalMs() on the AttacksDisplay class to return
1000 (ms) so the renderer uses a 1-second wall-clock tick and the values from
getBoatEtaSeconds(boat) refresh once per second for both outgoingBoats and
incoming boats; ensure this method exists on the class (same class that defines
renderButton, getBoatEtaSeconds, outgoingBoats mapping) so the layer
infrastructure will throttle updates independently of simulation speed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 27-35: The estimateBoatEtaSeconds function validates distance but
not turnIntervalMs, so invalid or non-positive intervals can produce bad ETAs;
update estimateBoatEtaSeconds to also return null when turnIntervalMs is not a
finite number or is <= 0 (e.g., use Number.isFinite(turnIntervalMs) &&
turnIntervalMs > 0), then compute secondsPerTick and the ceil as before;
reference the function estimateBoatEtaSeconds and the parameter turnIntervalMs
when making this change.

---

Outside diff comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 387-425: The layer currently renders ETA countdowns but doesn't
opt into a 1s wall-clock schedule; add/override getTickIntervalMs() on the
AttacksDisplay class to return 1000 (ms) so the renderer uses a 1-second
wall-clock tick and the values from getBoatEtaSeconds(boat) refresh once per
second for both outgoingBoats and incoming boats; ensure this method exists on
the class (same class that defines renderButton, getBoatEtaSeconds,
outgoingBoats mapping) so the layer infrastructure will throttle updates
independently of simulation speed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aed723fc-04da-435d-8114-962bc361a05a

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8e486 and 9d57c79.

📒 Files selected for processing (1)
  • src/client/graphics/layers/AttacksDisplay.ts

@camclark
Copy link
Author

camclark commented Mar 8, 2026

Regarding the getTickIntervalMs() suggestion to add a 1s wall-clock tick:

AttacksDisplay currently ticks at the default game tick rate (~100ms), which is consistent with EventsDisplay — the betrayal debuff timer (the closest analogue, also a seconds countdown) doesn't set getTickIntervalMs either. Even the game clock in GameRightSidebar uses 250ms, not 1000ms.

Adding a 1s interval would actually make the ETA update less frequently than the betrayal timer, which would be inconsistent. Leaving it as-is.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
Comment on lines +32 to +33
if (!Number.isFinite(distance) || distance < 0) return null;
if (!Number.isFinite(turnIntervalMs) || turnIntervalMs <= 0) return null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need these checks? I don't think this should ever happen.

Maybe just throw an error instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — changed to throw errors with descriptive messages since these values come from game internals and should never be invalid. Updated tests to expect throws. 66455bf

} from "./Leaderboard";
import swordIcon from "/images/SwordIcon.svg?url";

/** Estimates boat arrival time in seconds from Manhattan distance and server tick interval. */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

manhattan distance is a bad heuristic. could be way off if boat is going around a peninsula. Instead we should use actual distance

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Now using actual motion plan path. ada2bef

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/client/graphics/layers/BoatEta.test.ts (1)

1-56: Good test coverage for the ETA calculator.

The test suite covers standard cases, ceiling behavior, zero/large distances, and all the error cases for invalid inputs. The test names are clear and easy to read.

One optional addition: you might add tests for -Infinity distance and +Infinity turnIntervalMs to fully cover all non-finite edge cases. These should also throw, per the Number.isFinite checks.

,

Optional: Additional edge case tests
  test("throws for Infinity distance", () => {
    expect(() => estimateBoatEtaSeconds(Infinity, 100)).toThrow(
      "Invalid distance",
    );
  });

+  test("throws for -Infinity distance", () => {
+    expect(() => estimateBoatEtaSeconds(-Infinity, 100)).toThrow(
+      "Invalid distance",
+    );
+  });
+
   test("throws for zero turnIntervalMs", () => {
     expect(() => estimateBoatEtaSeconds(100, 0)).toThrow(
       "Invalid turnIntervalMs",
     );
   });

+  test("throws for Infinity turnIntervalMs", () => {
+    expect(() => estimateBoatEtaSeconds(100, Infinity)).toThrow(
+      "Invalid turnIntervalMs",
+    );
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/client/graphics/layers/BoatEta.test.ts` around lines 1 - 56, Add tests
that assert estimateBoatEtaSeconds throws for non-finite inputs: specifically
add one test calling estimateBoatEtaSeconds(-Infinity, 100) expecting an
"Invalid distance" throw, and another calling estimateBoatEtaSeconds(100,
Infinity) expecting an "Invalid turnIntervalMs" throw; place these alongside the
existing NaN/Infinity tests so the suite fully covers Number.isFinite edge cases
for the estimateBoatEtaSeconds function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/client/graphics/layers/BoatEta.test.ts`:
- Around line 1-56: Add tests that assert estimateBoatEtaSeconds throws for
non-finite inputs: specifically add one test calling
estimateBoatEtaSeconds(-Infinity, 100) expecting an "Invalid distance" throw,
and another calling estimateBoatEtaSeconds(100, Infinity) expecting an "Invalid
turnIntervalMs" throw; place these alongside the existing NaN/Infinity tests so
the suite fully covers Number.isFinite edge cases for the estimateBoatEtaSeconds
function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c758b1cc-eb90-4e8f-ac41-115a2373d37f

📥 Commits

Reviewing files that changed from the base of the PR and between 056a48a and 66455bf.

📒 Files selected for processing (2)
  • src/client/graphics/layers/AttacksDisplay.ts
  • tests/client/graphics/layers/BoatEta.test.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
@camclark camclark force-pushed the feature/boat-eta-countdown branch from a280a40 to 533a6e4 Compare March 8, 2026 10:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 372-379: The ETA calculation undercounts time because remaining
path steps (computed as remaining) are passed to estimateBoatEtaSeconds as if
they were ticks; multiply remaining by plan.ticksPerStep before calling
estimateBoatEtaSeconds so the function receives remaining ticks, e.g., replace
the call using remaining with (remaining * plan.ticksPerStep); refer to elapsed,
remaining, plan.ticksPerStep, estimateBoatEtaSeconds and
TransportShipExecution.ticksPerMove to locate and fix the logic in
AttacksDisplay.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 102e3958-091f-452f-a346-f3e073f58496

📥 Commits

Reviewing files that changed from the base of the PR and between 66455bf and ada2bef.

📒 Files selected for processing (1)
  • src/client/graphics/layers/AttacksDisplay.ts

@camclark camclark force-pushed the feature/boat-eta-countdown branch from 6caf9fa to 7c79c9b Compare March 8, 2026 10:56
@camclark
Copy link
Author

camclark commented Mar 8, 2026

@coderabbitai resume

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

✅ Actions performed

Reviews resumed.

@camclark
Copy link
Author

camclark commented Mar 8, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/client/graphics/layers/BoatEta.test.ts (1)

4-72: Add one regression test for ticksPerStep > 1.

This suite only proves estimateBoatEtaSeconds(). It does not protect the motion-plan conversion in AttacksDisplay.getBoatEtaSeconds(), so Line 378 could regress back to passing steps instead of ticks and these tests would still pass. Please add one case that exercises a plan with ticksPerStep > 1, or extract that conversion into a small pure helper and test it directly.

Based on learnings, AttacksDisplay.getBoatEtaSeconds() must convert motion-plan remaining steps into remaining ticks with plan.ticksPerStep before converting to seconds.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/client/graphics/layers/BoatEta.test.ts` around lines 4 - 72, Add a
regression test that ensures motion-plan steps are converted to ticks using
plan.ticksPerStep before converting to seconds: in BoatEta.test.ts add one case
that simulates a plan with ticksPerStep > 1 (or call
AttacksDisplay.getBoatEtaSeconds with a mock plan) and asserts the result equals
estimateBoatEtaSeconds(remainingSteps * plan.ticksPerStep, turnIntervalMs).
Reference estimateBoatEtaSeconds and AttacksDisplay.getBoatEtaSeconds (or the
extracted helper) and ensure the test uses ticksPerStep > 1 so it would fail if
steps were passed directly instead of ticks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/client/graphics/layers/BoatEta.test.ts`:
- Around line 4-72: Add a regression test that ensures motion-plan steps are
converted to ticks using plan.ticksPerStep before converting to seconds: in
BoatEta.test.ts add one case that simulates a plan with ticksPerStep > 1 (or
call AttacksDisplay.getBoatEtaSeconds with a mock plan) and asserts the result
equals estimateBoatEtaSeconds(remainingSteps * plan.ticksPerStep,
turnIntervalMs). Reference estimateBoatEtaSeconds and
AttacksDisplay.getBoatEtaSeconds (or the extracted helper) and ensure the test
uses ticksPerStep > 1 so it would fail if steps were passed directly instead of
ticks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b5761aa-3cbf-467e-9cb7-8ae896094f7d

📥 Commits

Reviewing files that changed from the base of the PR and between ada2bef and 7c79c9b.

📒 Files selected for processing (3)
  • resources/lang/en.json
  • src/client/graphics/layers/AttacksDisplay.ts
  • tests/client/graphics/layers/BoatEta.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • resources/lang/en.json

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 9, 2026
@camclark camclark requested a review from evanpelle March 9, 2026 23:15
Comment on lines +407 to +413
${etaSeconds !== null
? html`<span class="text-xs"
>${translateText("events_display.seconds_abbrev", {
seconds: etaSeconds,
})}</span
>`
: ""}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seconds makes it a bit too busy, I think instead of showing seconds, just have a bar that decreases until the boat arrives.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CleanShot 2026-03-11 at 23 02 11

I do really think the last X seconds are important to brace for impact, so I've implemented what you've asked and then added a countdown on the last 5 seconds.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/client/graphics/layers/AttacksDisplay.ts`:
- Around line 381-389: getBoatProgress currently computes progress as
elapsedSteps/totalSteps which ignores plan.ticksPerStep; change it to compute
progress based on ticks: calculate totalTicks = (plan.path.length - 1) *
plan.ticksPerStep, elapsedTicks = this.game.ticks() - plan.startTick (clamped
>=0), then return Math.min(1, Math.max(0, elapsedTicks / totalTicks)); keep the
early-null/zero guards (when no plan or totalTicks <= 0) and use the same
symbols (getBoatProgress, game.motionPlans(), plan.path, plan.ticksPerStep,
plan.startTick, this.game.ticks()) to locate and update the logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b4169071-f37f-44f3-b713-25a4065e7e2a

📥 Commits

Reviewing files that changed from the base of the PR and between d01294d and fcd0aec.

📒 Files selected for processing (2)
  • resources/lang/en.json
  • src/client/graphics/layers/AttacksDisplay.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • resources/lang/en.json

Comment on lines +381 to +389
private getBoatProgress(boat: UnitView): number | null {
const plan = this.game.motionPlans().get(boat.id());
if (!plan) return null;
const totalSteps = plan.path.length - 1;
if (totalSteps <= 0) return 1;
const elapsed = Math.floor(
(this.game.ticks() - plan.startTick) / plan.ticksPerStep,
);
return Math.min(1, Math.max(0, elapsed / totalSteps));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Compute progress from ticks, not just completed path steps.

getBoatEtaSeconds() was fixed to account for plan.ticksPerStep, but getBoatProgress() still uses elapsedSteps / totalSteps. For transport boats with ticksPerStep > 1, the bar will stay flat for most of each move and then jump at tile boundaries, so the new “journey completion” UI lags behind real travel time. Use elapsed/total ticks here as well.

♻️ Proposed fix
   private getBoatProgress(boat: UnitView): number | null {
     const plan = this.game.motionPlans().get(boat.id());
     if (!plan) return null;
     const totalSteps = plan.path.length - 1;
     if (totalSteps <= 0) return 1;
-    const elapsed = Math.floor(
-      (this.game.ticks() - plan.startTick) / plan.ticksPerStep,
-    );
-    return Math.min(1, Math.max(0, elapsed / totalSteps));
+    const totalTicks = totalSteps * plan.ticksPerStep;
+    const elapsedTicks = this.game.ticks() - plan.startTick;
+    return Math.min(1, Math.max(0, elapsedTicks / totalTicks));
   }

Based on learnings: transport-boat motion plans in this PR can use ticksPerStep > 1, so boat timing UI should be derived from ticks, not only path steps.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/client/graphics/layers/AttacksDisplay.ts` around lines 381 - 389,
getBoatProgress currently computes progress as elapsedSteps/totalSteps which
ignores plan.ticksPerStep; change it to compute progress based on ticks:
calculate totalTicks = (plan.path.length - 1) * plan.ticksPerStep, elapsedTicks
= this.game.ticks() - plan.startTick (clamped >=0), then return Math.min(1,
Math.max(0, elapsedTicks / totalTicks)); keep the early-null/zero guards (when
no plan or totalTicks <= 0) and use the same symbols (getBoatProgress,
game.motionPlans(), plan.path, plan.ticksPerStep, plan.startTick,
this.game.ticks()) to locate and update the logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

Display real-time countdown for boats in transit

3 participants