Skip to content

Overworld ais#1370

Open
ifroshty wants to merge 21 commits into
mainfrom
overworld-ais-1347
Open

Overworld ais#1370
ifroshty wants to merge 21 commits into
mainfrom
overworld-ais-1347

Conversation

@ifroshty

@ifroshty ifroshty commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Pull Request

Closes #1347 (Overworld AIs) and #1348 (quest retry delays).

Summary

Adds Overworld AIs — staff-placed AI NPCs that stand on the world map and that players interact with by walking onto their tile (a prompt opens) or clicking them. A placement is either FRIENDLY (a quest-giver / dialogue / delivery NPC) or HOSTILE (an enemy you fight), can be pinned to a fixed tile or roam daily, and FRIENDLY NPCs carry a weighted quest pool. This makes missions location-driven: a player can accept a mission from an NPC, travel to and defeat a placed enemy, and deliver an item back — including branching dialogue scenes shown right at the NPC.

It also adds quest retry delays (#1348): a quest can now repeat on a daily / weekly / monthly cadence with maxCompletes re-interpreted as a per-period cap (tracked via periodCompletes / periodStartAt), instead of only a lifetime cap. This underpins overworld mission cooldowns. A companion per-mission attempt cooldown (attemptDelay) caps how often a player can roll for a mission (see below).

What was implemented

Overworld AI placements (#1347)

  • New tables OverworldAiPlacement + OverworldAiPlacementQuest. A placement binds an AI template to a sector tile with: interactionType (FRIENDLY/HOSTILE), sectorType (specific/random/from_list), locationType (specific/random), an isActive flag, a positionVersion, and (FRIENDLY) a weighted quest pool (per-quest grant chance summing ≤ 100%).
  • Admin UI at /manual/ai/[aiid]/placements to create/edit/delete placements, configure the quest pool, and copy placement ids. Reserved sectors (Wake Island, War-Torn Battleground) are excluded.
  • Placements render on the overworld (travel.getSectorDataplacementToSectorUserSector.tsx / libs/threejs/sector.ts): FRIENDLY shows a quest/talk marker, HOSTILE shows the attack marker.
  • NPC sprite rendering (feat: render better): overworld NPCs now draw their actual character art standing on the tile — preferring the full avatar (their avatarLight is often an un-generated placeholder) via a shared pickSpriteAvatar helper, and rendered as a free-standing character (no circular portrait crop / map-pin) while keeping the talk/attack bubble.
  • Arrival prompt (feat: modal when interacting): walking onto an NPC's tile auto-opens a confirm modal — FRIENDLY → "Request a mission?", HOSTILE → "Attack?" — so players don't have to click the small sprite (QoL, especially on mobile). Clicking the NPC still works; the prompt shows once per arrival and re-arms after the player leaves the tile.
  • Daily reposition cron GET /api/daily-overworld-ai (added to vercel.json): re-randomizes non-fixed placements once per UTC day and bumps positionVersion; clients holding a stale version are told to refresh. Guarded by CRON_SECRET + a daily timer lock.

Friendly NPC quest-giving

  • Clicking a FRIENDLY NPC grants a quest from its weighted pool via a deterministic band-walk (a roll past the summed chances grants "nothing this time"), filtered by per-quest availability and the new period cap.
  • Daily roll cap OVERWORLD_QUEST_ROLLS_PER_DAY (= MISSIONS_PER_DAY = 20) consumed via CAS so failed rolls still cost a slot; one active NPC mission at a time via an atomic activeNpcQuestId claim (with stale-claim self-heal). New activeNpcQuestId + dailyOverworldQuestRolls columns on UserData; the daily-counters cron resets the roll count.

Overworld NPC objectives (location-driven missions)

  • defeat_opponents bound to a placement (overworldPlacementId): walk onto the NPC's tile, click it, and a PvE OVERWORLD battle starts against the placement's AI; winning completes the objective via the existing post-battle tracker path. The opponent is derived from the placement at quest save (single source of truth — the author never picks the AI twice). Overworld fights use the PvE loadout (OVERWORLD added to QuestBattleTypes).
  • deliver_item / dialog bound to a FRIENDLY placement: clicking resolves them (deliver requires the player to own the items; dialog presents branch choices).
  • Dialogue scenes: the overworld dialog modal renders the objective's description (rich text via parseHtml) plus the scene (sceneBackground + sceneCharacters, fetched with gameAsset.getSceneAssets) above the choice buttons — matching the Logbook scene. Branch selection advances the quest (selectedNextObjectiveId).
  • Quest editor binding: objectives gain an overworldPlacementId dropdown of existing placements, scoped by taskdefeat_opponents narrows to the selected Opponent AI's placements; deliver_item/dialog show FRIENDLY placements only — and opponentAIs is auto-derived and hidden once a placement is bound.
  • Referential safety: a placement that a quest objective binds cannot be deleted or deactivated (exact JSON membership check on quest content).
  • Shared startOverworldBattle helper used by both the plain HOSTILE fight and the quest-linked fight.

Quest retry delays (#1348)

  • Quests gain retryDelay (none / daily / weekly / monthly). When set, maxCompletes becomes a per-period cap: the quest can be completed up to N times each calendar period, then unlocks at the next period boundary (none keeps lifetime-cap semantics; maxCompletes ≥ 1 is required when a real delay is set).
  • Tracked via periodCompletes + periodStartAt on the user-quest record. Period boundaries computed in utils/time.ts (periodStart); the cap is enforced by periodCapReached / periodCompletionSet in libs/quest.ts and folded into isAvailableUserQuests, so period-capped quests automatically drop out of availability (including the overworld NPC pool). Mission cooldowns / NPC mission logic build on this.

Per-mission attempt cooldown (attemptDelay)

  • A second, orthogonal delay axis to Modify the delay Requirement #1348: where retryDelay / maxCompletes cap completions, attemptDelay (none / daily / weekly / monthly) caps attempts — how often a player can roll for a mission from an overworld NPC per calendar period, where a 99% "nothing" miss still spends the attempt. The two compose; attemptDelay defaults to none (existing quests unaffected).
  • New Quest.attemptDelay column + UserQuestAttempt table (userId, questId, lastAttemptAt, unique (userId, questId)). A pure attemptCapReached helper (sibling of periodCapReached) reuses the same periodStart boundaries, so it self-resets with no cron.
  • Enforced only in the overworld interact path: an attempt-capped quest drops out of the eligible pool — a capped sole-pool quest returns "nothing available" and costs no daily roll — and after the daily-roll CAS succeeds, an atomic onDuplicateKeyUpdate upsert records the attempt for every eligible cooldown quest, so a grant and a miss both spend it.
  • Exposed as an Attempt Delay dropdown in the quest editor for all quest types (including mission / errand), unlike the completion fields. Enables ultra-rare roaming missions, e.g. attemptDelay = daily + pool chance = 1 → one 1% roll per day.

Why

Overworld AIs turn the world map into an interaction surface: NPCs that hand out missions, enemies you hunt at a known location, and delivery/dialogue targets — enabling multi-step, place-based missions instead of menu-only quests. Quest retry delays give content designers repeatable content with sane cadences (daily/weekly/monthly) and per-period caps, which the overworld mission economy relies on; the attempt cooldown lets designers make a mission genuinely rare to even roll, not just to complete.

Screenshots

AI Overworld Button
image

Overworld Placement UI
image

Friendly Quest Placement UI
image

Friendly Overworld UI selections
image

Selecting Opponent AI who a placement was created and selecting the id from dropdown
image

Overworld Friendly NPC
image

NPC Dialog on acceptance
image

Breaking changes

Additive schema + migrations; no breaking API changes.

  • Migrations required (drizzle 00170022): new tables OverworldAiPlacement, OverworldAiPlacementQuest, UserQuestAttempt; UserData gains activeNpcQuestId, dailyOverworldQuestRolls; Quest gains retryDelay and attemptDelay; the user-quest record gains periodCompletes, periodStartAt; objective content gains overworldPlacementId (JSON, no column).
  • New cron route /api/daily-overworld-ai plus a vercel.json cron entry; requires CRON_SECRET to be set in the environment.
  • No existing endpoints changed shape in a breaking way (new fields are optional/defaulted).

Testing

  • Unit tests added and passing: tests/libs/overworldAi.{test,derive,filter,scope,loadout,objective,roll,serializer}.test.ts, tests/validators/overworldAi.test.ts, tests/libs/quest.{attemptcap,boundfail,eligibility,periodcap,periodcount}.test.ts, tests/utils/time.periodStart.test.ts, tests/validators/questCooldown.test.ts.
  • bunx tsc --noEmit → clean. bunx biome lint → clean on changed files (one pre-existing noNonNullAssertion warning in resolveOverworldPosition and one pre-existing useOptionalChain warning in unrelated Sector.tsx code are not introduced here).
  • Note: the combat-engine test suite (tests/libs/combat/*) requires a populated .env to load and is environmental, not exercised by this change.

Notes / follow-ups

  • Flee re-engagement (parked): fleeing an overworld-AI fight returns the player to the NPC's tile; making "leave without re-engaging" smooth is a separate follow-up.
  • Per-mission cooldown UI: the new Attempt Delay control (per-period attempt cap) is exposed for all quest types, including mission. The completion cooldown (retryDelay / maxCompletes) is still only exposed for QuestTypesWithMaxAttempts (event/story/battlepyramid/starter/raid), so a mission-type quest can't set a per-quest completion cap in the UI yet — missions remain bounded by the global MISSIONS_PER_DAY cap. The period-cap engine itself works for any quest type.
  • Defeat completion granularity: a defeat_opponents objective completes when its AI is defeated in any battle context, not only at the bound placement — bind to an AI that isn't reused elsewhere if you need a location-specific kill.
  • UserQuestAttempt growth: one row per (player, attempt-capped quest), upserted and never pruned — same shape as questHistory; no cleanup cron needed at current scale.
  • Roaming (random/from_list) placements move daily; keep a placement specific if a mission needs a predictable location.

License

By making this pull request, I confirm that I have the right to waive copyright and related rights to my contribution, and agree that all copyright and related rights in my contributions are waived, and I acknowledge that the Studie-Tech ApS organization has the copyright to use and modify my contribution for perpetuity.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added overworld AI NPC placements with placement-bound objectives, weighted quest selection, and NPC dialog branching.
    • Added staff and player workflows to manage placements and trigger overworld NPC interactions.
  • Enhancements
    • Expanded OVERWORLD battle type support across gameplay and UI.
    • Added per-period quest attempt tracking/capping, plus improved overworld NPC talk/attack behavior and rendering.
    • Daily automation now refreshes NPC placement coordinates and resets daily overworld quest rolls.
  • Tests
    • Added/expanded coverage for placement binding/actionability, dialog routing, weighted selection, and period-cap behavior.

@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
the-ninja-ai Error Error Jun 25, 2026 11:09pm
tnr Error Error Jun 25, 2026 11:09pm

Request Review

@github-actions github-actions Bot added the size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). label Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

Walkthrough

This PR introduces a complete overworld AI NPC placement system with period-capped quest completion tracking. It adds new DB schema (OverworldAiPlacement, OverworldAiPlacementQuest, period-tracking columns), a Zod placement validator, position/binding utilities, enhanced quest tracking with placement-bound objectives that auto-fail or freeze, a new overworldAiRouter TRPC router (CRUD + HOSTILE/FRIENDLY interaction branches), a daily position-refresh cron, Three.js NPC sprite rendering with click/dialog flow, travel sector integration, and an admin management UI.

Changes

Overworld AI NPC System

Layer / File(s) Summary
DB schema, migrations, and overworld constants
app/drizzle/constants.ts, app/drizzle/schema.ts, app/drizzle/migrations/0017*.sql, app/drizzle/migrations/0018*.sql, app/drizzle/migrations/0019*.sql, app/drizzle/migrations/0020*.sql, app/drizzle/migrations/0021*.sql, app/drizzle/migrations/meta/_journal.json, app/src/libs/combat/util.ts, app/src/app/colosseum/page.tsx
Extends BattleTypes/PveBattleTypes/QuestBattleTypes with OVERWORLD, adds three enum constant sets (OverworldInteractionTypes, OverworldSectorTypes, OverworldLocationTypes) and config cap (OVERWORLD_QUEST_ROLLS_PER_DAY), creates OverworldAiPlacement/OverworldAiPlacementQuest tables with indexes and relations, adds dailyOverworldQuestRolls and activeNpcQuestId to UserData, adds periodCompletes/periodStartAt to QuestHistory, and registers five migration journal entries.
Placement validator and position resolution utilities
app/src/validators/objectives.ts, app/src/validators/overworldAi.ts, app/src/libs/overworldAi.ts, app/src/utils/time.ts, app/tests/libs/overworldAi.*.ts, app/tests/validators/overworldAi.test.ts, app/tests/utils/time.periodStart.test.ts
Adds OverworldPlacementSchema with cross-field validation (non-empty sectorList, no duplicate questIds, chance sum ≤ 100), overworldPlacementId to objective fields, isPlaceableSector predicate, resolveOverworldPosition with sector/coordinate selection logic, placementToSectorUser serializer, findActionableBoundObjective objective lookup, pickWeightedQuest weighted selection, placement filtering helpers, and periodStart utility for retry-period boundaries. Includes full Vitest coverage.
Quest binding and period-cap logic with shared helpers
app/src/libs/quest.ts, app/src/server/api/routers/quests.ts, app/tests/libs/quest.boundfail.test.ts, app/tests/libs/quest.eligibility.test.ts, app/tests/libs/quest.periodcap.test.ts, app/tests/libs/quest.periodcount.test.ts, app/tests/validators/questCooldown.test.ts
Adds isBoundPlacementDeleted/isBoundPlacementFrozen predicates and three-way placement-binding logic (auto-fail/freeze/pass) in getNewTrackers; introduces periodCapReached/periodCompletionSet per-period completion helpers; extends getNewTrackers/isAvailableUserQuests with optional placement-status and period-cap parameters; extracts assignQuestToUser (shared quest-start orchestrator), commitQuestObjectiveRewards (reward-claim flow), fetchBoundPlacementStatus (placement query); refactors startQuest/checkRewards/checkLocationQuest to use these. Includes test coverage for bound placement behavior, period capping, and quest-type concurrency blocking.
overworldAi TRPC router with CRUD and interaction
app/src/server/api/routers/overworldAi.ts, app/src/server/api/root.ts
Introduces overworldAiRouter with getPlacementsForAi (list by AI user), getAllPlacementNames (formatted labels), upsertPlacement (position compute + row upsert + quest child sync), deletePlacement (cascade delete), and interactWithOverworldAi (validate active/positionVersion/actor tile, branch on HOSTILE battle vs FRIENDLY quest/dialog with daily roll consumption and NPC quest slot claiming). Registers router in appRouter.
Daily overworld AI position refresh cron
app/src/app/api/daily-overworld-ai/route.ts, app/src/app/api/daily-counters/route.ts, app/vercel.json
Adds daily-locked API route that re-resolves positions for non-specific placements and updates sector/longitude/latitude/positionVersion in parallel; includes error rollback. Schedules cron at 0/5 0 * * *. Updates daily-counters to reset dailyOverworldQuestRolls to 0 on daily increment.
Travel router sector data integration
app/src/server/api/routers/travel.ts
Extends getSectorData to load active overworldAiPlacement records for current sector (including aiTemplate fields), filters by AI template, converts each to SectorUser via placementToSectorUser, and returns overworldAis array in response.
Three.js NPC sprite creation and visibility handling
app/src/libs/threejs/types.ts, app/src/libs/threejs/sector.ts
Extends SectorUser with optional NPC fields (isNpc, npcPlacementId, npcInteractionType, npcPositionVersion); updates createUserSprite to render talk/attack interaction meshes for NPCs with placement metadata; fixes drawUsers caching to use npcPlacementId ?? userId (prevent NPC-player mesh collision), bypasses hideStudentAndGenin filtering for NPCs; adds early-return in intersectUsers for NPC groups (skip player-only tooltip logic).
Sector.tsx NPC dialog and interaction flow
app/src/layout/Sector.tsx
Adds npcDialog state for dialog branches, pendingNpcInteractRef for placement/version context, memoizes fetchedUsers from data.users + data.overworldAis; introduces interactNpc mutation (handle battle/dialog/sector invalidation); extends scene click-handler for talk meshes (open dialog if adjacent, move-toward if distant) and attack mesh (placement-based interaction); adds NPC Dialog modal with branch-select callbacks; updates scene effect dependencies.
Admin placement management and navigation
app/src/app/manual/ai/[aiid]/placements/page.tsx, app/src/app/manual/ai/edit/[aiid]/page.tsx, app/src/layout/EditContent.tsx
Adds ManualAiPlacements page with PlacementsManager (React Hook Form + Zod, conditional form sections for sector/location/quest pool, upsert/delete mutations, existing placements list with edit/delete/copy-id actions). Adds placement names dropdown in EditContent with sentinel "none" option. Adds "Overworld Placements" navigation button to AI edit page.
Period start and NPC quest concurrency helpers
app/src/utils/time.ts, app/src/server/utils/concurrency.ts
Adds periodStart utility to compute UTC start dates for retry delays (daily, weekly, monthly). Adds claimActiveNpcQuest and clearActiveNpcQuest concurrency helpers for atomic single-user NPC quest slot management.

Sequence Diagram(s)

sequenceDiagram
    participant Player as Player (Sector)
    participant getSectorData as travelRouter.getSectorData
    participant OWAiRouter as overworldAiRouter
    participant getNewTrackers as quest.getNewTrackers
    participant DB as Database

    Player->>getSectorData: getSectorData(sector)
    getSectorData->>DB: query overworldAiPlacement (active, sector)
    DB-->>getSectorData: placements + aiTemplate
    getSectorData->>getSectorData: placementToSectorUser(placement, template)
    getSectorData-->>Player: { users, overworldAis, ... }

    Player->>Player: click NPC talk/attack mesh
    Player->>OWAiRouter: interactWithOverworldAi(placementId, positionVersion, ...)
    OWAiRouter->>DB: validate placement + actor (parallel)
    OWAiRouter->>OWAiRouter: check active + positionVersion + actor tile

    alt HOSTILE interaction
        OWAiRouter->>OWAiRouter: initiateBattle(OVERWORLD)
        OWAiRouter-->>Player: battle started
    else FRIENDLY + bound objective
        OWAiRouter->>getNewTrackers: findActionableBoundObjective
        OWAiRouter->>OWAiRouter: commitQuestObjectiveRewards
        OWAiRouter-->>Player: dialog branches / reward claimed
    else FRIENDLY + quest giver
        OWAiRouter->>DB: CAS increment dailyOverworldQuestRolls
        OWAiRouter->>OWAiRouter: filter eligible quest pool
        OWAiRouter->>OWAiRouter: pickWeightedQuest (deterministic roll)
        OWAiRouter->>OWAiRouter: assignQuestToUser
        OWAiRouter-->>Player: quest assigned or no quest
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • studie-tech/TheNinjaRPG#1372: Both PRs extend getNewTrackers in app/src/libs/quest.ts with new objective gating logic — main PR adds bound-placement auto-fail/freeze + period caps, retrieved PR adds battle-context kill objective gating.
  • studie-tech/TheNinjaRPG#765: Both PRs modify getNewTrackers to extend tracker logic, though for different purposes (main PR adds overworld placement binding, retrieved PR adds crafting/gathering/hunting trackers).
  • studie-tech/TheNinjaRPG#777: Both PRs modify quest progression/availability logic in app/src/libs/quest.ts and isAvailableUserQuests, including daily reset and quest eligibility gating.

Suggested labels

Completed

Suggested reviewers

  • MathiasGruber
  • theeneon

Poem

🐇 A ninja hops across the overworld wide,
NPCs await with quests locked inside!
"Talk or fight?" asks the sprite on each tile,
Daily rolls reset after a while—
With placements and dialogs aligned in a row,
The quest world is alive with each period's flow! 🗺️

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes quest retry delay work for #1348, which is unrelated to the only linked issue (#1347). Split the retry-delay changes into a separate PR or add #1348 as a linked issue if they are intended to ship together.
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.
Title check ❓ Inconclusive The title is related but too vague to describe the main change. Rename it to something specific like "Add overworld AI placements and quest retry delays".
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The overworld AI work meets #1347 by adding map placements, interactions, fixed/random positioning, and quest/item delivery flows.
Description check ✅ Passed The PR description follows the required template and includes implementation, rationale, screenshots, breaking changes, testing, and license sections.

✏️ 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 overworld-ais-1347

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.

@ifroshty ifroshty marked this pull request as ready for review June 19, 2026 20:13
@ifroshty ifroshty requested a review from MathiasGruber as a code owner June 19, 2026 20:13
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
app/src/server/api/routers/overworldAi.ts Adds the overworld AI tRPC router for placement management, NPC interaction, quest grants, and overworld battle starts.
app/src/layout/Sector.tsx Adds overworld NPC rendering, arrival prompts, click handling, dialog display, and profile refresh after NPC interactions.
app/src/libs/quest.ts Adds placement-aware objective resolution, dialog branch validation, and per-period quest availability checks.

Reviews (21): Last reviewed commit: "chore: regenerate migrations after rebas..." | Re-trigger Greptile

Comment thread app/src/layout/Sector.tsx
Comment thread app/src/server/api/routers/overworldAi.ts Outdated
Comment thread app/src/server/api/routers/overworldAi.ts
coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

Comment thread app/src/server/api/routers/overworldAi.ts
Comment thread app/src/server/api/routers/overworldAi.ts

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

Caution

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

⚠️ Outside diff range comments (1)
app/tests/libs/quest.periodcap.test.ts (1)

20-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Strengthen the null-period test so it actually validates null handling.

This case uses periodCompletes: 0, so it stays uncapped even without exercising the null reset behavior. Use a value above maxCompletes (e.g., periodCompletes: 5) to ensure periodStartAt: null is what makes it pass.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/tests/libs/quest.periodcap.test.ts` around lines 20 - 21, The test for
null periodStartAt in periodCapReached does not properly validate the null reset
behavior. Currently it uses periodCompletes: 0 which keeps the period uncapped
regardless of the null check, so the test would pass even if null handling was
broken. Change the periodCompletes value to something greater than maxCompletes
(such as 5) so that the test actually exercises the null handling logic and
confirms that a null periodStartAt prevents capping even when periodCompletes
exceeds the maxCompletes threshold.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/tests/libs/quest.periodcap.test.ts`:
- Around line 20-21: The test for null periodStartAt in periodCapReached does
not properly validate the null reset behavior. Currently it uses
periodCompletes: 0 which keeps the period uncapped regardless of the null check,
so the test would pass even if null handling was broken. Change the
periodCompletes value to something greater than maxCompletes (such as 5) so that
the test actually exercises the null handling logic and confirms that a null
periodStartAt prevents capping even when periodCompletes exceeds the
maxCompletes threshold.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d3476956-fcde-4da5-a458-553c6b4f6d32

📥 Commits

Reviewing files that changed from the base of the PR and between 8331b16 and bc14005.

📒 Files selected for processing (2)
  • app/src/libs/quest.ts
  • app/tests/libs/quest.periodcap.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/libs/quest.ts

@ifroshty

Copy link
Copy Markdown
Contributor Author

@greptileai

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/drizzle/migrations/0022_long_grim_reaper.sql`:
- Line 10: The Quest migration currently adds attemptDelay with a default of
none, which drops existing retry cadence data from retryDelay. Update the
migration to preserve current values by renaming or altering the existing column
in place, or by adding the new column, backfilling it from retryDelay, and then
dropping the old column in the same migration. Use the Quest table change and
attemptDelay/retryDelay column names as the reference points.

In `@app/src/hooks/quest.ts`:
- Around line 241-246: The `attemptDelay` field in `useQuest` is being added
unconditionally, but it should only be included for quest types that support
capped attempts. Update the `formData.push` logic in `useQuest` to gate the
`attemptDelay` entry behind `QuestTypesWithMaxAttempts.includes(questType)`,
matching the surrounding attempt-related fields so unsupported quest types
cannot configure it.
🪄 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: 01ba2814-abcc-443d-939b-a7c43dbe1c82

📥 Commits

Reviewing files that changed from the base of the PR and between 9c6e33e and 3c7508a.

📒 Files selected for processing (9)
  • app/drizzle/migrations/0022_long_grim_reaper.sql
  • app/drizzle/migrations/meta/0022_snapshot.json
  • app/drizzle/migrations/meta/_journal.json
  • app/drizzle/schema.ts
  • app/src/hooks/quest.ts
  • app/src/libs/quest.ts
  • app/src/server/api/routers/overworldAi.ts
  • app/src/validators/objectives.ts
  • app/tests/libs/quest.attemptcap.test.ts
✅ Files skipped from review due to trivial changes (1)
  • app/tests/libs/quest.attemptcap.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/validators/objectives.ts
  • app/drizzle/migrations/meta/_journal.json
  • app/src/server/api/routers/overworldAi.ts
  • app/src/libs/quest.ts

Comment thread app/drizzle/migrations/0022_long_grim_reaper.sql Outdated
Comment thread app/src/hooks/quest.ts
@MathiasGruber

Copy link
Copy Markdown
Collaborator

Rebased this branch onto the latest main to clear the merge conflicts.

Conflicts resolved:

  • app/src/libs/quest.tsmain had added the war-context kill-objective gating (CombatQuestContext type + killObjectiveCountsForQuest) while this branch added the placement-binding helpers (isBoundPlacementDeleted / isBoundPlacementFrozen). Both additive feature sets were kept. Both also added a parameter to getNewTrackers; the merged signature is (user, tasks, combatContext?, boundPlacementStatus?). The one call site that passed boundPlacementStatus positionally (quests.ts) was updated to pass undefined for combatContext first, and all other call sites were verified consistent with the new signature.

  • Migrations — this branch's hand-numbered 0017–0022 migrations collided with main's 0017_uneven_sister_grimm. The branch migrations were deleted and a single migration was regenerated from the schema via drizzle-kit generate, producing 0018_violet_turbo on top of main's migration set (creates OverworldAiPlacement, OverworldAiPlacementQuest, UserQuestAttempt; adds the OVERWORLD battle type, Quest.attemptDelay, QuestHistory.periodCompletes/periodStartAt, and UserData.activeNpcQuestId/dailyOverworldQuestRolls).

The merged quest and overworld-AI unit test suites pass locally. The branch is now conflict-free against main.

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

Labels

50 SS Completed size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overworld AIs

3 participants