Skip to content

Refactor Stall and Enemy Registries - #67

Merged
candour merged 3 commits into
mainfrom
refactor-stall-enemy-registry-10904571682745949244
Apr 21, 2026
Merged

Refactor Stall and Enemy Registries#67
candour merged 3 commits into
mainfrom
refactor-stall-enemy-registry-10904571682745949244

Conversation

@candour

@candour candour commented Apr 21, 2026

Copy link
Copy Markdown
Owner

Refactored the codebase to centralize stall and enemy definitions, stats, and behavioral logic into a new registry system. This improves maintainability by eliminating scattered hardcoded data and logic. Key improvements include modularizing firing mechanics, damage modifiers, and upgrade calculations, as well as fixing a critical firing rate bug for the Satay stall.


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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added visual feedback for disabled buttons with grayscale effect
    • Implemented affordability indicators for stalls with visual styling
    • Integrated haptic feedback support
  • UI Improvements

    • Redesigned start wave button with improved appearance
    • Enhanced cost display with conditional coloring based on affordability
    • Upgraded button sprite visuals and layouts
  • Refactor

    • Centralized game data configuration for better maintainability

Consolidated scattered data and behavioral logic for stalls and enemies into StallRegistry and EnemyRegistry.
- Moved base stats, costs, and descriptions to registries.
- Relocated behavioral logic (firing, damage modifiers, special behaviors) to registry definitions.
- Centralized sprite coordinate mapping.
- Simplified MainViewModel, UI components, and tests by delegating to registries.
- Fixed a bug where Satay's fire rate was ignored due to rotation updates.
- Ensured weapons.png asset is preserved.

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.

@github-actions

Copy link
Copy Markdown

Build Successful! 🚀

Download APK

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

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces centralized registry infrastructure for stalls and enemies, migrating from hardcoded configurations and scattered constant maps to registry-based lookups. A new Registry.kt module defines StallDefinition and EnemyDefinition classes that encapsulate behavior and configuration, with StallRegistry and EnemyRegistry singletons providing access. MainViewModel delegates combat logic, tutorial data, and stall behavior to these registries. UI components are updated to thread haptic callbacks and affordability states, while SpriteButton gains an enabled parameter for disabled visuals.

Changes

Cohort / File(s) Summary
Registry Infrastructure
app/src/main/java/com/messark/hawkerrush/registry/Registry.kt
New 416-line module defining FireResult (sealed class for firing outcomes), StallDefinition with upgrade calculations and behavior delegation methods, EnemyDefinition with puddle slow multipliers and special-behavior updates, and StallRegistry/EnemyRegistry singleton accessors. Centralizes stall firing, damage modifiers, freeze duration, speed boosts, and enemy HP/behavior logic.
Core Model & ViewModel Migration
app/src/main/java/com/messark/hawkerrush/MainViewModel.kt, app/src/main/java/com/messark/hawkerrush/model/GameModels.kt
MainViewModel refactored to source available stalls, tutorials, enemy stats, and combat behavior from registries instead of inline definitions or TutorialContent lookups. Stall firing, projectile creation, damage/freeze/speed modifiers, and visual effects now delegate to StallDefinition. Stall.getUpgradeBenefit signature changed from (category: String, level: Int, baseStall: Stall) to (category: String, level: Int), delegating to registry.
Tutorial & Constants Cleanup
app/src/main/java/com/messark/hawkerrush/model/TutorialModels.kt, app/src/main/java/com/messark/hawkerrush/ui/constants/SpriteConstants.kt
Removed TutorialContent singleton (76 lines) that exposed ENEMY_TUTORIALS and STALL_TUTORIALS maps. SpriteConstants removed STALL_RECTS and ENEMY_ROW_INDICES maps (replaced by registry), added button sprite rects (BTN_UPGRADE_RECT, BTN_START_RECT, BTN_SELL_RECT, BTN_TARGET_RECT).
SpriteButton & Haptic Integration
app/src/main/java/com/messark/hawkerrush/MainActivity.kt, app/src/main/java/com/messark/hawkerrush/ui/components/GameControlPanel.kt
SpriteButton composable now accepts enabled: Boolean = true parameter; when disabled, button sprite does not switch to pressed state and click interaction is disabled; grayscale ColorFilter applied visually. GameControlPanel accepts onTriggerHaptic callback, wires it to tutorial trigger and replaced Material Button with SpriteButton for wave-start CTA.
UI Component Refactoring
app/src/main/java/com/messark/hawkerrush/ui/components/StallConsole.kt, app/src/main/java/com/messark/hawkerrush/ui/components/StallSlot.kt, app/src/main/java/com/messark/hawkerrush/ui/components/GameBoard.kt, app/src/main/java/com/messark/hawkerrush/ui/components/TutorialOverlay.kt
StallConsole accepts onTriggerHaptic, migrates Material Button widgets to SpriteButton with labeled overlays, removes baseStall from getUpgradeBenefit calls, adds upgrade cost color spans. StallSlot gains canAfford: Boolean parameter, conditionally desaturates sprite and changes text colors based on affordability. GameBoard and TutorialOverlay switched sprite/row lookups from SpriteConstants maps to EnemyRegistry/StallRegistry definitions.
Test Update
app/src/test/java/com/messark/hawkerrush/MilestoneBoostTest.kt
Updated getUpgradeBenefit test calls to use new signature (category: String, level: Int) without baseStall parameter.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 Stall and Enemy Registries' directly and precisely describes the main change—consolidating stall and enemy definitions into a centralized registry system, which is the primary objective of this substantial refactor.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-stall-enemy-registry-10904571682745949244
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch refactor-stall-enemy-registry-10904571682745949244

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

@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.

🧹 Nitpick comments (7)
app/src/main/java/com/messark/hawkerrush/ui/components/StallSlot.kt (1)

53-55: Avoid allocating ColorMatrix/ColorFilter inside the draw path.

This creates new objects during rendering. Cache the disabled filter once per composition.

♻️ Suggested refactor
 fun StallSlot(
@@
 ) {
     val spriteRect = StallRegistry.get(stall.stallType).spriteRect
+    val disabledColorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
@@
                 drawImage(
@@
-                    colorFilter = if (!canAfford) {
-                        ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
-                    } else null
+                    colorFilter = if (!canAfford) disabledColorFilter else null
                 )
app/src/main/java/com/messark/hawkerrush/ui/components/GameControlPanel.kt (1)

11-12: Unused import: ButtonDefaults.

The Button composable was replaced with SpriteButton, but the ButtonDefaults import remains. This import is no longer needed.

🧹 Remove unused import
 import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.Text
app/src/main/java/com/messark/hawkerrush/MainViewModel.kt (2)

361-376: Consider simplifying by using behaviorUpdatedEnemy directly.

The code extracts isStopped, stopDurationMs, and lastStopMs from behaviorUpdatedEnemy but then doesn't use behaviorUpdatedEnemy itself. Since updateSpecialBehavior returns the updated enemy, you could use it more directly to reduce redundancy.


400-402: Redundant parameter in getPuddleSlowMultiplier call.

enemyDef is retrieved via EnemyRegistry.get(enemy.type), so enemyDef.type already equals enemy.type. The getPuddleSlowMultiplier method takes the enemy type as a parameter, but it should instead use its own type field internally. See related comment on Registry.kt.

app/src/main/java/com/messark/hawkerrush/registry/Registry.kt (3)

189-199: Use explicit locale for consistent number formatting.

String.format uses the implicit default locale, which may cause inconsistent decimal separators (e.g., "1.5" vs "1,5") across different user locales. For UI consistency in a game, use an explicit locale.

🌐 Use explicit Locale for formatting
+import java.util.Locale
+
 // In getUpgradeBenefit function:
             "Range" -> {
                 // ...
-                "+${String.format("%.1f", currentRange - baseStall.range)}"
+                "+${String.format(Locale.US, "%.1f", currentRange - baseStall.range)}"
             }
             "Radius" -> {
                 // ...
-                "+${String.format("%.1f", currentRadius - baseStall.aoeRadius)}"
+                "+${String.format(Locale.US, "%.1f", currentRadius - baseStall.aoeRadius)}"
             }

235-241: Parameter is redundant; method should use instance type field.

getPuddleSlowMultiplier takes enemyType as a parameter, but it's called on an EnemyDefinition that was already retrieved for that specific enemy type. The method should use this.type instead, consistent with updateSpecialBehavior which uses the instance field.

♻️ Remove redundant parameter
-    fun getPuddleSlowMultiplier(enemyType: EnemyType): Float {
-        return when (enemyType) {
+    fun getPuddleSlowMultiplier(): Float {
+        return when (type) {
             EnemyType.DELIVERY_RIDER -> 0.2f
             EnemyType.AUNTIE -> 0.8f
             else -> 0.6f
         }
     }

Then update the call site in MainViewModel.kt:

-                    speedMultiplier = enemyDef.getPuddleSlowMultiplier(enemy.type)
+                    speedMultiplier = enemyDef.getPuddleSlowMultiplier()

371-372: All enum values are covered; consider adding validation to prevent future regressions.

Both StallRegistry.get() and EnemyRegistry.get() currently use non-null assertions (!!), and verification confirms all enum values have entries: StallType has 5 values (TEH_TARIK, SATAY, CHICKEN_RICE, DURIAN, ICE_KACHANG) all mapped at lines 288–354, and EnemyType has 4 values (SALARYMAN, TOURIST, AUNTIE, DELIVERY_RIDER) all mapped at lines 377–404. The code is safe today, but consider adding init-time validation to catch missing entries early if new enum values are added.

Also applies to: 415-416


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d1f7d3eb-061c-455f-b6ec-a80426fc57cf

📥 Commits

Reviewing files that changed from the base of the PR and between ff3d04b and f52aead.

⛔ Files ignored due to path filters (1)
  • app/src/main/res/drawable-nodpi/weapons.png is excluded by !**/*.png
📒 Files selected for processing (12)
  • app/src/main/java/com/messark/hawkerrush/MainActivity.kt
  • app/src/main/java/com/messark/hawkerrush/MainViewModel.kt
  • app/src/main/java/com/messark/hawkerrush/model/GameModels.kt
  • app/src/main/java/com/messark/hawkerrush/model/TutorialModels.kt
  • app/src/main/java/com/messark/hawkerrush/registry/Registry.kt
  • app/src/main/java/com/messark/hawkerrush/ui/components/GameBoard.kt
  • app/src/main/java/com/messark/hawkerrush/ui/components/GameControlPanel.kt
  • app/src/main/java/com/messark/hawkerrush/ui/components/StallConsole.kt
  • app/src/main/java/com/messark/hawkerrush/ui/components/StallSlot.kt
  • app/src/main/java/com/messark/hawkerrush/ui/components/TutorialOverlay.kt
  • app/src/main/java/com/messark/hawkerrush/ui/constants/SpriteConstants.kt
  • app/src/test/java/com/messark/hawkerrush/MilestoneBoostTest.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/messark/hawkerrush/model/TutorialModels.kt

Consolidated scattered data and behavioral logic for stalls and enemies into StallRegistry and EnemyRegistry.
- Moved base stats, costs, and descriptions to registries.
- Relocated behavioral logic (firing, damage modifiers, special behaviors) to registry definitions.
- Centralized sprite coordinate mapping.
- Simplified MainViewModel, UI components, and tests by delegating to registries.
- Fixed a bug where Satay's fire rate was ignored due to rotation updates.
- Rebasing on main to ensure no merge conflicts.

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.

@github-actions

Copy link
Copy Markdown

Build Successful! 🚀

Download APK

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

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