Commit Graph

958 Commits

Author SHA1 Message Date
gamer147
9813f82edc feat(mission): wire rank_achieved emit at RankBattleController
RankBattleController.FinishInternal now emits rank_achieved + tier-qualified
variant (via MissionEventKeys.Rank.AchievedAll) whenever
rankResult.TierAdvanced fires. Closes the last catalog wire-up gap in the
Tier 1+2 scope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:35:23 -04:00
gamer147
e9c13ae5f4 feat(rank-progress): RankProgressResult carries TierAdvanced signal
RankProgressResult gains a TierAdvanced bool (defaulted false so the 7-arg
positional-constructor sites don't break). Set true iff the grant is a win
AND RankTier.Name resolves both pre-grant and post-grant to distinct strings —
i.e. a promotion crossed a tier boundary.

Explicitly guards on isWin so a demotion (in principle possible after a loss
below a tier floor, though the current floor rules prevent it) never fires
the achievement backward.

Three new tests: no-cross win, beginner→d cross, loss stays false.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:32:45 -04:00
gamer147
8e41739bde feat(mission): wire class_level_up emits from every XP-granting controller
Every _xp.GrantAsync callsite now conditionally emits class_level_up:{class}
when the returned BattleXpGrantResult.LeveledUp is true. Six callsites total:

- PracticeController.Finish
- RankBattleController.FinishInternal
- FreeBattleController.Finish
- ArenaTwoPickBattleController.Finish (LeveledUp + ClassId routed via
  BattleFinishResultDto — controller-side signals, not on wire)
- ArenaColosseumBattleController (missed in the original wire-up survey;
  colosseum-battle XP now also fires the level-up event when applicable)
- StoryController.Finish (LeveledUp + ClassId routed via new
  StoryFinishOutcome wrapper, mirroring the TK2 RunFinishOutcome pattern)

Story's IStoryService.FinishAsync now returns StoryFinishOutcome instead of
FinishResponse directly. Test callsites updated to unwrap .Response.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:31:04 -04:00
gamer147
a2048a4d54 feat(battle-xp): BattleXpGrantResult carries LeveledUp signal
BattleXpGrantResult gains a LeveledUp bool set true iff at least one curve
threshold was crossed during the grant (comparing pre-Level to post-Level after
the level-up loop). Callers will gate class_level_up mission emits on this flag
rather than caching pre-state themselves.

No behavior change for existing callers — they read GetXp/TotalXp/Level today
and never check LeveledUp yet. Tests cover: unchanged level → false, single
threshold → true, multi-threshold within one grant → still true, unknown class
guardrail → false.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:23:34 -04:00
gamer147
42b0fd3d6e feat(mission): wire Tier 1 emits — rank/free/challenge match events
Adds IMissionProgressService to RankBattle, FreeBattle, ArenaTwoPickBattle, and
ArenaTwoPick controllers. Each finish handler emits the appropriate
MissionEventKeys builder result:

- RankBattle.Finish (win) → Ranked.WinAll(classId)
  Includes AI-rank routes; on a private server AI wins count the same.
- FreeBattle.Finish (win) → Free.WinAll() (aggregates only)
- ArenaTwoPickBattle.Finish → Challenge.MatchPlayAll or MatchWinAll
  Always fires challenge_play; wins additionally fire challenge_win and
  the ranked/arena/daily aggregates.
- ArenaTwoPick.Finish (5-win full clear) → Challenge.FullClearAll
  Gated on RunFinishOutcome.WasFullClear from the previous commit.

This closes six of the eight remaining catalog wire-up gaps; class_level_up
and rank_achieved follow in Tier 2 (need pre-state detection).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:22:02 -04:00
gamer147
94622a526d refactor(arena-tk2): FinishAsync returns RunFinishOutcome with WasFullClear signal
Introduces RunFinishOutcome — a semantic record that wraps the wire
FinishResponseDto with the controller-side WasFullClear signal used to fire
the challenge_full_clear mission event. The wire response isn't affected
(controller unwraps the .Response field before returning to the client);
RetireAsync stays returning FinishResponseDto because retire cannot full-clear.

Full-clear detection compares run.WinCount against ResolveMaxBattleCountAsync
rather than hard-coding 5, so a future TK2 rules-config change tracks
automatically. New test covers the full-clear signal explicitly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:17:23 -04:00
gamer147
f3c8b46469 feat(mission): add RankTier + Class/Ranked/Free/Challenge/ClassLevel/Rank builders
MissionEventKeys grows six new nested static classes covering the wire-up
gap for the counter families that Practice/Story left uncovered. Each mirrors
the established (Practice/Story) shape: named accessors for individual keys,
`{Family}.WinAll(...)` / `AchievedAll(...)` etc. as convenience arrays that
callers pass straight to RecordEventAsync.

RankTier lives at SVSim.Database/Enums/RankTier.cs since the concept (rank_id
→ tier bucket) is data-shaped, not emit-specific, and RankProgressService
will need it internally for tier-crossing detection. Ranked/ClassLevel/Rank
builders in MissionEventKeys compose Class.Name and RankTier.Name.

12 new registry tests cover the family shapes plus tier boundary edges
(rank 4/5, 24/25, 25/26).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 13:15:11 -04:00
gamer147
0f6c8d5e41 feat(bootstrap): validate seed event_type against MissionEventKeys registry
Achievement, mission, and BP monthly mission importers now throw at bootstrap
if any seed row references an event_type that doesn't start with a registered
top-level prefix. Prevents drift: a typo in the seed JSON (or a prefix removed
from code without updating the seed) fails loudly at startup instead of
silently producing counters no emitter writes to.

Verified all 12 top-level prefixes currently in the seed JSON pass validation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 12:59:48 -04:00
gamer147
652011ea74 refactor(controllers): migrate practice/story/item_purchase to MissionEventKeys
PracticeController.Finish now uses MissionEventKeys.Practice.WinAll — the
counter keys emitted match the catalog's named form (practice_win:elite:arisa
etc.), closing the numeric-vs-named mismatch that had all practice-elite
achievements silently at zero. Also removes the "bridging is a follow-up" TODO.

StoryController.Finish uses MissionEventKeys.Story.ChapterFinishAll for the
same shape. ItemPurchaseController.CounterKey routes through
MissionEventKeys.ItemPurchase so the "item_purchase:" prefix has one owner.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 12:59:34 -04:00
gamer147
fa89c32110 refactor(calendar): expose AllTime via GameCalendarPeriods, not the concrete service
Move the "all-time" constant out of GameCalendarService (concrete) into a new
static class GameCalendarPeriods. Consumers using IGameCalendarService no
longer have to reach through the concrete class to name the lifetime bucket.

Migrates 5 callsites (mission progress/assembler, admin, item purchase, tests).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 12:59:24 -04:00
gamer147
e1b38d5649 feat(mission): add MissionEventKeys registry with Practice numeric→named mapping
Central registry of every string that can appear as ViewerEventCounter.EventKey
or catalog EventType. All 12 top-level prefixes from the achievement/mission
seed JSON are constants; Practice.WinAll and Story.ChapterFinishAll are
hierarchical builders that emit multi-level counter keys per event.

Practice.WinAll translates the wire (difficulty:int, enemy_class_id:int) tuple
into the catalog-facing named form (elite/elite2/elite3 : arisa/erika/...).
Wire difficulty 4/6/7 map to elite/elite2/elite3 (verified via
practicetext.json + practice-opponents.json + practice_ai_setting.csv). Class
1-8 map to the leader names in CardClass ordering. Wire values outside the
known set skip that hierarchical level but still advance the top-level counter.

MissionEventKeys.IsRegistered validates that a string starts with a registered
top-level prefix — for seed-importer drift checks in the next commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 12:59:15 -04:00
gamer147
6e6856db4f refactor(calendar): delete JstPeriod, update doc comments
All JstPeriod callsites have moved to IGameCalendarService — delete the
static class and its tests. Updates the doc comments on Viewer.cs
(LastLoginBonusClaimedAt) and ViewerEventCounter.cs to reference the
new UTC / config-driven boundary instead of the old "JST 02:00".

GameConfigurationJsonb config-section snapshot test picks up "GameCalendar".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 12:00:18 -04:00
gamer147
448153d592 refactor(admin,item-purchase): migrate to IGameCalendarService
Swaps 6 JstPeriod.* callsites across AdminController (mission counter
resolution for the /admin catalog) and ItemPurchaseController (monthly
reset bucket key + all-time period).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:52:53 -04:00
gamer147
2b0cd516f0 refactor(mission): migrate MissionProgressService + MissionAssembler to IGameCalendarService
Swaps 6 JstPeriod.* callsites in mission code to IGameCalendarService.
Also drops the manual `jstNow = now.ToOffset(+9).AddHours(-2)` shift in
MissionAssembler — BP monthly missions now key off UTC calendar month, and
the start/end wire timestamps become UTC-midnight month boundaries. This
matches the plan's all-UTC discipline; wire fidelity for BP monthlies
is not shipped yet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:51:27 -04:00
gamer147
70ec063c0f refactor(login-bonus): migrate LoginBonusService to IGameCalendarService
IsDue() collapses from the JST DayKey comparison boilerplate to a single
_calendar.ResetReady(viewer.LastLoginBonusClaimedAt) call.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:49:54 -04:00
gamer147
a6b2c47b98 refactor(pack): migrate PackController to IGameCalendarService
Swaps all four DateTime.UtcNow.Date same-day checks in PackController for
_calendar.ResetReady() — daily-single open, /pack/info daily-single gate,
FreePacks /pack/info visibility, FreePacks /pack/open quota. Removes the
TODO(daily-reset) comments; ToDto promoted from static to instance so it
can see _calendar.

Also fills in GameCalendarConfig.ShippedDefaults() (required by the
[ConfigSection] discovery in SVSimDbContext.EnsureSeedDataAsync — bootstrap
throws without it).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:49:17 -04:00
gamer147
90d420d97a feat(calendar): add IGameCalendarService — UTC reset-boundary primitive
New config-driven service that replaces JstPeriod. All timestamps are UTC;
one config value (GameCalendarConfig.DailyResetUtcHour, default 0 = midnight
UTC) drives both ResetReady checks and DayKey/WeekKey/MonthKey bucket keys.
Because both derive from the same MostRecentBoundary helper, they can never
disagree.

Prod parity is a config flip: set DailyResetUtcHour=17 in GameConfigs to get
the 02:00 JST boundary. No JST-flavored math anywhere in the code.

JstPeriod callsite migration + deletion follow in subsequent commits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:46:12 -04:00
gamer147
81fe842c15 fix(pack): daily half-off single spends crystals and gates re-purchase
The DAILY child gacha (type_detail=3, is_daily_single=true) is the once-per-day
half-off crystal single-pack. Two bugs:

1. /pack/open Daily case was calling TrySpendAsync(Rupee) — decompiled client
   (GachaUI.cs:1046 CheckBuyPackWithCrystal, :1187 SetClystalConfirmDialog)
   confirms it's a crystal purchase. Freeplay mode masked the mismatch in
   testing. Swapped to Crystal + insufficient_crystals error.

2. /pack/info kept emitting is_daily_single=true after the viewer's first
   claim, so the client re-showed the half-off button and a second click
   400'd with daily_free_already_claimed. Added a per-parent-pack gate that
   flips is_daily_single false when LastDailyFreeAt.Date == today. The child
   entry itself stays so the CRYSTAL_MULTI full-price button still activates.

TODO(daily-reset) comments now reference docs/plans/daily-reset-service.md
(in the outer repo) — the current UTC-midnight comparison will migrate to
the JST 02:00 reset boundary via that plan.

Updated the existing daily test to use cost=50 and assert
Crystals: 200 -> 150 + Rupees untouched (previously used cost=0 so the
currency choice was undetectable). Added two /pack/info gate tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:23:51 -04:00
gamer147
4ce1cd172a feat(rank): AI-start SelfInfo carries viewer rank progress
/ai_*_rank_battle/start now reads ViewerRankProgress for the target format
and populates SelfInfo.rank / battlePoint / isMasterRank / masterPoint so
the pre-battle screen matches the viewer's real state. Read-only path uses
IRankProgressService.GetAsync (added in this commit).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 10:25:29 -04:00
gamer147
7ff8a7bd92 feat(rank): wire RankProgressService into RankBattleController.Finish
Replaces the phase-3 zero stub. Real progression math now lands in the
response body (rank / after_battle_point / after_master_point /
battle_point / master_point). Format is authored by URL (not derived from
BattleContext) so /finish still works when hit without a prior
/do_matching — mirrors the client's URL-per-format Wizard/RankBattleFinishTask
dispatch (decompile lines 12-35).

3 existing tests updated with real progression assertions; 3 new tests
cover D-tier floor loss, format separation, and AI-variant persistence.
Full solution suite: 1583/1583 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 10:21:12 -04:00
gamer147
f18cfb58b8 feat(rank): viewer repo LoadForRankProgressAsync
Split-query load of Classes (for class XP) + RankProgress (for rank grant)
so RankBattleController.Finish uses a single viewer read. Test factory's
ReferenceDataImporter.ImportAllAsync already seeds RankInfo — no test
helper needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 10:16:10 -04:00
gamer147
4e87957306 feat(rank): RankProgressService — pure +100/-50 tier-floored math
Reads RankInfoEntry.LowerLimitPoint from ranks.csv (via IGlobalsRepository)
for main-tier floors — no hardcoded thresholds. Master (Point=50000) and
Grand Master (MP-based, floor 5000) branch handles the post-Master path.
GetAsync is a read-only snapshot used later by AI-start's SelfInfo.

13 unit tests cover first-win, loss floors (0, Beginner→D, D→D0), win
crossing 50000, MP awards at Master, GM promotion at MP≥5000, GM floor
protecting against Master demotion, format separation, Crossover throw.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 10:15:34 -04:00
gamer147
9f97837211 feat(rank): add ViewerRankProgress owned entity + migration
One row per (viewer, format) tracking accumulated Point + MasterPoint.
Unique index on (ViewerId, Format) enforces one row per format per viewer
(mirrors owned-collection precedent per project_owned_collection_unique_index).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 10:13:50 -04:00
gamer147
94ed6734c7 tune(battle-xp): raise defaults to XpPerWin=200 / XpPerLoss=50
Doubles the shipped defaults across all modes without touching per-mode
overrides. Curve interaction against classexp.csv (L1=50, L2=150):
- Win (200 XP): crosses both L1 + L2 → land at L3, Exp=0
- Loss (50 XP): exactly meets L1 → land at L2, Exp=0

Updates every affected test assertion (Practice, Rank, Free, TK2,
Colosseum, Story) for the new level/exp math.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:34:01 -04:00
gamer147
8199a95356 feat(battle-xp): wire story first-clear /finish through IBattleXpService
- StoryConfig.ClassXpPerClear retired; equivalent knob moves to
  BattleXpConfig.StoryXpPerClear (falls back to XpPerWin when null).
- StoryService.FinishAsync now grants class XP via IBattleXpService with
  BattleXpMode.Story on the firstClear && isPlayShape branch. Persists
  post-grant Level/Exp to Viewer.Classes.
- Wire fields (get_class_experience, class_level) stringified as before.
- StoryConfig kept as an empty [ConfigSection] for future story-specific
  knobs. StoryServiceTests updated to reflect the new default path
  (XpPerWin=100 crosses classexp.csv L1=50 → L2 with 50 carry).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:06:47 -04:00
gamer147
a03bff3ea7 feat(battle-xp): wire colosseum /finish through IBattleXpService, extend response DTO
- ColosseumBattleFinishResponseDto gains get_class_experience,
  class_experience, class_level. Client-safe: same BattleFinishResponsProcessing
  handles all three keys as ints (decomp verified).
- ArenaColosseumBattleController.FinishInternal grants win/loss XP via
  IBattleXpService with BattleXpMode.Colosseum for both /colosseum_battle/finish
  and /colosseum_rank_battle/finish URLs.
- Existing win test extended: XP wire assertions + persistence check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:02:25 -04:00
gamer147
91ec14fc7e feat(battle-xp): wire free-battle /finish through IBattleXpService
- FreeBattleController.Finish (rotation + unlimited URLs) now grants
  win/loss XP via IBattleXpService with BattleXpMode.Free.
- Preserves the strict field subset (no rank/master fields).
- Tests updated: win asserts XP+level-up; new loss test on rotation URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:00:29 -04:00
gamer147
1e9693e56d feat(battle-xp): wire rank + AI-rank /finish through IBattleXpService
- RankBattleController.Finish (covers all 4 URLs: rotation, unlimited,
  ai_rotation, ai_unlimited) now grants win/loss XP via IBattleXpService
  with BattleXpMode.Rank. Uses req.ClassId from wire.
- Tests updated: AI-variant win asserts new XP values; consistency-loss
  asserts loss XP. Added persistence test on /unlimited_rank_battle/finish.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:59:01 -04:00
gamer147
c3da6079b2 feat(battle-xp): wire /practice/finish through IBattleXpService
- Practice finish now grants win/loss XP via IBattleXpService.GrantAsync
  with BattleXpMode.Practice. Response fields (get_class_experience,
  class_experience, class_level) reflect post-grant totals.
- Uses req.ClassId from wire; no BattleContext lookup needed.
- Test asserts default XpPerWin=100 crosses classexp.csv L1=50 threshold
  → Level=2 + Exp=50 carry, persists to Viewer.Classes.
- Added loss-XP test asserting default XpPerLoss=25 stays under threshold.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:56:59 -04:00
gamer147
f85f221bfa refactor(battle-xp): retire ArenaTwoPickConfig.ClassXpPerBattle for shared BattleXpService
- ArenaTwoPickService now delegates class-XP grants to IBattleXpService
  with BattleXpMode.ArenaTwoPick. Inline GrantClassXp/ResolveClassLevel
  helpers deleted.
- ClassXpPerBattle field removed from ArenaTwoPickConfig; TK2 XP now
  configurable via ArenaTwoPickXpPerWin/XpPerLoss overrides or the global
  BattleXpConfig defaults (100/25).
- Updated 5 test files (ArenaTwoPickService callers) for the new
  constructor arg. Added loss-XP assertion to Finish tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:54:52 -04:00
gamer147
81771bb0a0 feat(battle-xp): IBattleXpService with per-mode overrides and auto level-up
- IBattleXpService.GrantAsync mutates viewer.Classes in place; caller SaveChanges
- Resolves win/loss amount from per-mode override or global XpPerWin/XpPerLoss
- Story mode ignores isWin, uses StoryXpPerClear ?? XpPerWin
- Level-up loop consults curve[row.Level].NecessaryExp; carries overflow
- Saturates at max curve level (excess piles in Exp)
- Unknown classId → no-op returning (0, 0, 1), logs Warning
- 10 unit tests (Moq-backed curve stub); DI registered next to InventoryService
- GameConfigurationJsonbTests baseline updated (19 sections)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:51:43 -04:00
gamer147
4541378885 feat(battle-xp): config section + viewer loader for class XP grants
Adds BattleXpConfig ([ConfigSection("BattleXp")]) with global
XpPerWin/XpPerLoss plus per-mode nullable overrides. Adds BattleXpMode
enum and IViewerRepository.LoadForBattleXpGrantAsync — focused tracked
load with viewer.Classes + Class nav ref included, for the service
about to be introduced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:44:11 -04:00
gamer147
57df329923 content: wire Serilog into SVSim.ContentServer
Calls UseSvSimSerilog("svsim-content") right after CreateBuilder and
wraps Main in try/catch/finally. No environment gate needed —
ContentServer has no test-suite path. The existing manual request-log
middleware (uses ILogger<Program>) now flows through Serilog
automatically; swapping it for UseSerilogRequestLogging() is a
follow-up per the spec.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 02:08:44 -04:00
gamer147
9a47ac8d90 app: wire Serilog into SVSim.EmulatedEntrypoint
Registers Serilog via SVSim.Hosting.UseSvSimSerilog("svsim-api") right
after CreateBuilder and wraps Main in try/catch/finally so a fatal
startup exception logs before CloseAndFlush.

Also updates the SVSim.Hosting extension to gate the file sink on entry
assembly rather than the ASP.NET environment name. WebApplicationFactory
sets the environment via a hook that resolves after both CreateBuilder
and the UseSerilog callback see their environment view, so
IsEnvironment("Testing") is unreliable in tests. The entry assembly
under `dotnet test` is always "testhost", which is a reliable signal.

appsettings.json grows a Serilog section mirroring the existing
Logging:LogLevel overrides; the Logging block itself is left in place
per the spec's follow-up cleanup note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 02:07:41 -04:00
gamer147
a7843adfe6 hosting: add SVSim.Hosting with Serilog extension
Adds new SVSim.Hosting project carrying Serilog + Serilog.AspNetCore +
Serilog.Settings.Configuration + enricher package refs, and a single
IHostBuilder extension method UseSvSimSerilog(appName) that installs a
colored console sink and a plain-text file sink rolling daily with a
100 MB per-file cap and 7-file retention. ReadFrom.Configuration is
called last so appsettings can override minimum levels per category.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 01:55:58 -04:00
gamer147
87891f9896 cull(engine-cleanup): pass-9 cascade round 2 after UILabel stub 2026-07-03 23:46:27 -04:00
gamer147
773844165b cull(engine-cleanup): pass-9 cascade round 1 after UILabel stub 2026-07-03 23:45:28 -04:00
gamer147
ed6f6f1d68 cull(engine-cleanup): pass-9 — stub UILabel to live surface 2026-07-03 23:44:22 -04:00
gamer147
737f375ea7 cull(engine-cleanup): pass-9 cascade round 3 after NGUIText stub 2026-07-03 23:39:49 -04:00
gamer147
39ae2b0c0a cull(engine-cleanup): pass-9 cascade round 2 after NGUIText stub 2026-07-03 23:38:51 -04:00
gamer147
08c6bca58c cull(engine-cleanup): pass-9 cascade round 1 after NGUIText stub 2026-07-03 23:37:54 -04:00
gamer147
9183625ac2 cull(engine-cleanup): pass-9 — stub NGUIText to live surface 2026-07-03 23:36:53 -04:00
gamer147
699e78755b cull(engine-cleanup): pass-9 cascade round 6 after UIPanel stub 2026-07-03 23:34:56 -04:00
gamer147
c6319696f3 cull(engine-cleanup): pass-9 cascade round 5 after UIPanel stub 2026-07-03 23:33:54 -04:00
gamer147
01f9635d50 cull(engine-cleanup): pass-9 cascade round 4 after UIPanel stub 2026-07-03 23:32:57 -04:00
gamer147
0103050fe0 cull(engine-cleanup): pass-9 cascade round 3 after UIPanel stub 2026-07-03 23:31:57 -04:00
gamer147
18844b2233 cull(engine-cleanup): pass-9 cascade round 2 after UIPanel stub 2026-07-03 23:30:59 -04:00
gamer147
36bd90c2b3 cull(engine-cleanup): pass-9 cascade round 1 after UIPanel stub 2026-07-03 23:29:52 -04:00
gamer147
853b060f02 cull(engine-cleanup): pass-9 — stub UIPanel to live surface 2026-07-03 23:28:50 -04:00
gamer147
b54e79f1fb cull(engine-cleanup): pass-9 cascade round 3 after DialogBase stub 2026-07-03 23:25:54 -04:00