Skip to content

Refactor and Optimize Stall Targeting System - #156

Merged
candour merged 3 commits into
mainfrom
refactor-stall-targeting-10273960744059109657
May 14, 2026
Merged

Refactor and Optimize Stall Targeting System#156
candour merged 3 commits into
mainfrom
refactor-stall-targeting-10273960744059109657

Conversation

@candour

@candour candour commented May 13, 2026

Copy link
Copy Markdown
Owner

Refactored the stall firing logic in MainViewModel.kt and Registry.kt for better efficiency and encapsulation.

Key improvements:

  1. Efficiency: Replaced $O(S \times E)$ targeting searches and Line-of-Sight (LoS) checks with a more efficient approach. By pre-sorting enemies by TargetMode (First, Strongest, Weakest) once per game tick, stalls now evaluate only the highest-priority candidates. LoS checks—which are computationally expensive—are now performed only until a valid target is found, significantly reducing the average workload per tick.
  2. Encapsulation: Targeting logic is now part of the StallBehavior interface. This allows for stall-specific targeting rules (like TRAY_RETURN_UNCLE's unique proximity requirements) to be self-contained rather than cluttered in the ViewModel.
  3. Simplicity: The handleStallFiring function in MainViewModel.kt has been significantly simplified. It now delegates the target selection and the firing action to the registry, handling different outcomes (projectiles, puddles, or capturing enemies) through a clean FireResult sealed class.
  4. Maintenance: Recorded the refactor in fixes.md (REF-008).

Verified with existing unit tests for stall stats, Tray Return Uncle logic, and general game state management.


PR created automatically by Jules for task 10273960744059109657 started by @candour

Summary by CodeRabbit

  • Refactor
    • Reorganized stall firing and target selection for more consistent, modular behavior and reduced redundant checks.
  • New Features
    • Added explicit "hold enemy" outcome for stalls, enabling stalls to grab and release enemies with configurable hold durations.
    • Updated the Tray Return Uncle stall to use the new hold behavior with a 2s hold time.

Review Change Stack

- Introduced pre-sorted enemy lists by TargetMode in MainViewModel to reduce targeting complexity.
- Refactored StallBehavior interface to encapsulate targeting logic (selectTarget).
- Optimized Line-of-Sight checks by evaluating candidates in priority order and short-circuiting.
- Moved Tray Return Uncle's specific grabbing logic into its own behavior class.
- Simplified handleStallFiring in MainViewModel by delegating to behaviors and FireResult.
- Updated fixes.md with REF-008.

Co-authored-by: candour <4670475+candour@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@candour has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 17 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dc99d8af-30a8-492b-8308-e4abc3494e12

📥 Commits

Reviewing files that changed from the base of the PR and between 4b24223 and 259aaae.

📒 Files selected for processing (2)
  • app/src/main/java/com/messark/hawker/registry/Registry.kt
  • fixes.md
📝 Walkthrough

Walkthrough

Stall firing architecture is refactored to delegate target selection and hold-release behavior to StallBehavior implementations. A new FireResult.HoldEnemy variant carries target ID and release time. StallBehavior gains a selectTarget(...) method; DefaultStallBehavior filters by range and grab status. TrayReturnUncleBehavior returns HoldEnemy outcomes. MainViewModel pre-sorts enemies by targeting mode, delegates selection to behaviors, reorganizes Bak Kut Teh boost scaling, and consolidates firing through unified FireResult handling.

Changes

Stall Firing Behavior Refactor

Layer / File(s) Summary
Stall firing contract: HoldEnemy and target selection
app/src/main/java/com/messark/hawker/registry/Registry.kt
FireResult adds a HoldEnemy variant carrying target ID and release time. StallBehavior gains a selectTarget(...) method to choose enemies based on targeting mode, range, obstructions, and grabbed-enemy state. DefaultStallBehavior.selectTarget filters candidates by range and grab status, using either allEnemies (for CLOSEST mode) or enemiesByMode lists, with optional line-of-sight blocking.
TrayReturnUncleBehavior: hold behavior and registry wiring
app/src/main/java/com/messark/hawker/registry/Registry.kt
TrayReturnUncleBehavior extends DefaultStallBehavior and overrides fire(...) to return FireResult.HoldEnemy with releaseTimeMs computed from stall's effectDurationMs. Registry entry for StallType.TRAY_RETURN_UNCLE is updated to use TrayReturnUncleBehavior and configure effectDurationMs as 2000L.
MainViewModel stall firing refactor
app/src/main/java/com/messark/hawker/MainViewModel.kt
Target selection refactored to pre-compute enemiesByMode map and delegate to stallDef.behavior.selectTarget(...). Boost scaling split into boost (damage) and effectBoost (duration/freeze) applied to boostedStall. Firing outcomes consolidated into unified FireResult handler: NewProjectile updates projectiles and resets stall state, NewPuddle updates puddles and timing, HoldEnemy records held enemy and release time. Previous special-case TRAY_RETURN_UNCLE branching replaced by HoldEnemy pathway.
Changelog entry
fixes.md
New REF-008 entry documents the stall firing refactor optimizing enemy targeting by moving targeting logic into behaviors to reduce line-of-sight check cost.

Possibly Related PRs

  • candour/towerpower#133: Refactors MainViewModel stall firing pipeline and handles TRAY_RETURN_UNCLE targeting/holding; overlaps with this PR's behavior-driven targeting changes.
  • candour/towerpower#82: Adds uniqueTargetIds adjacency tracking and hold/release handling for TRAY_RETURN_UNCLE, which relates to this PR's HoldEnemy outcome.
  • candour/towerpower#153: Introduces or extends the StallBehavior abstraction; this PR expands that model with selectTarget and FireResult.HoldEnemy.

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor and Optimize Stall Targeting System' directly and accurately captures the main objective of the pull request: refactoring the stall targeting logic to improve efficiency through pre-sorted enemy lists and better encapsulation via the StallBehavior interface.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-stall-targeting-10273960744059109657

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@github-actions

Copy link
Copy Markdown

Build Successful! 🚀

Download APK

Note: This link will be removed when the PR is closed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
fixes.md (1)

38-38: ⚡ Quick win

Consider documenting key architectural changes.

The entry focuses on performance optimization but omits significant architectural improvements introduced in this refactor:

  • FireResult.HoldEnemy variant for hold-and-release outcomes
  • StallBehavior.selectTarget(...) method delegation
  • TrayReturnUncleBehavior class extraction

While performance is the primary motivation, these structural changes improve encapsulation and maintainability. Documenting them provides a more complete picture of the refactor scope.

📝 Suggested expansion
-| REF-008 | 2025-06-12 | Refactored `handleStallFiring` and `StallBehavior` to optimize enemy targeting. Introduced pre-sorted enemy lists and encapsulated targeting logic in behaviors, reducing $O(S \times E)$ Line-of-Sight checks to $O(S \times \text{small constant})$. | Resolved |
+| REF-008 | 2025-06-12 | Refactored `handleStallFiring` and `StallBehavior` to optimize enemy targeting. Introduced pre-sorted enemy lists, `StallBehavior.selectTarget(...)` delegation, `FireResult.HoldEnemy` variant, and `TrayReturnUncleBehavior` class, reducing Line-of-Sight checks via early termination on priority-ordered candidates. | Resolved |
app/src/main/java/com/messark/hawker/MainViewModel.kt (1)

788-792: ⚡ Quick win

Consider defensive refactoring to future-proof enemiesByMode against enum expansion.

The TargetMode enum currently has exactly four members: FIRST, CLOSEST, STRONGEST, WEAKEST. The hardcoded map seeds three of them (FIRST, STRONGEST, WEAKEST), with CLOSEST intentionally handled separately. No current bug exists. However, if a new TargetMode value is added without updating the map, selectTarget() would silently return an empty list, causing stalls cycled to that mode to never fire.

A defensive hardening: drive the map construction from TargetMode.values() with an explicit switch and error() for unhandled cases, ensuring any future enum member triggers a compile-time or runtime failure rather than silent failure.

♻️ Suggested defensive construction
-        val enemiesByMode = mapOf(
-            TargetMode.FIRST to state.enemies.sortedByDescending { it.currentPathIndex },
-            TargetMode.STRONGEST to state.enemies.sortedByDescending { it.health },
-            TargetMode.WEAKEST to state.enemies.sortedBy { it.health }
-        )
+        val enemiesByMode: Map<TargetMode, List<Enemy>> = TargetMode.values()
+            .filter { it != TargetMode.CLOSEST }
+            .associateWith { mode ->
+                when (mode) {
+                    TargetMode.FIRST -> state.enemies.sortedByDescending { it.currentPathIndex }
+                    TargetMode.STRONGEST -> state.enemies.sortedByDescending { it.health }
+                    TargetMode.WEAKEST -> state.enemies.sortedBy { it.health }
+                    else -> error("Unhandled TargetMode for pre-sort: $mode")
+                }
+            }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e366e83-fe87-4541-84b7-378d4587275c

📥 Commits

Reviewing files that changed from the base of the PR and between a708c55 and e8289a6.

📒 Files selected for processing (3)
  • app/src/main/java/com/messark/hawker/MainViewModel.kt
  • app/src/main/java/com/messark/hawker/registry/Registry.kt
  • fixes.md

Comment thread app/src/main/java/com/messark/hawker/registry/Registry.kt Outdated
Comment thread fixes.md
@candour
candour enabled auto-merge (squash) May 13, 2026 23:33
@candour

candour commented May 13, 2026

Copy link
Copy Markdown
Owner Author

The TargetMode enum currently has exactly four members: FIRST, CLOSEST, STRONGEST, WEAKEST. The hardcoded map seeds three of them (FIRST, STRONGEST, WEAKEST), with CLOSEST intentionally handled separately. No current bug exists. However, if a new TargetMode value is added without updating the map, selectTarget() would silently return an empty list, causing stalls cycled to that mode to never fire.

@candour

candour commented May 13, 2026

Copy link
Copy Markdown
Owner Author

When stall.targetMode != CLOSEST and the caller hasn't populated enemiesByMode[stall.targetMode], candidates collapses to emptyList() and the stall silently never fires (no log, no fallback). Today the caller in MainViewModel.handleStallFiring hardcodes only FIRST, STRONGEST, and WEAKEST (lines 788-792), so any additional TargetMode value (e.g., LAST) would be silently dead even though cycleTargetMode will happily route users to it.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@candour
candour disabled auto-merge May 13, 2026 23:37
@google-labs-jules

Copy link
Copy Markdown
Contributor

The TargetMode enum currently has exactly four members: FIRST, CLOSEST, STRONGEST, WEAKEST. The hardcoded map seeds three of them (FIRST, STRONGEST, WEAKEST), with CLOSEST intentionally handled separately. No current bug exists. However, if a new TargetMode value is added without updating the map, selectTarget() would silently return an empty list, causing stalls cycled to that mode to never fire.

Good point. I've updated selectTarget in Registry.kt to use a when block. This will force a compile-time check if a new TargetMode is added, preventing the silent failure you mentioned.

- Updated `DefaultStallBehavior.selectTarget` to use a `when` block for `TargetMode`. This ensures that any future additions to the `TargetMode` enum will require an explicit update to the targeting logic, avoiding silent failures.

Co-authored-by: candour <4670475+candour@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

Build Successful! 🚀

Download APK

Note: This link will be removed when the PR is closed.

1 similar comment
@github-actions

Copy link
Copy Markdown

Build Successful! 🚀

Download APK

Note: This link will be removed when the PR is closed.

@candour

candour commented May 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resolve

@candour
candour enabled auto-merge (squash) May 14, 2026 00:51
@candour
candour merged commit a458b18 into main May 14, 2026
3 checks passed
@candour
candour deleted the refactor-stall-targeting-10273960744059109657 branch May 14, 2026 00:52
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Comments resolved and changes approved.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant