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>
This commit is contained in:
@@ -410,6 +410,8 @@ public class ArenaTwoPickService : IArenaTwoPickService
|
||||
BeforeSpotPoint = before,
|
||||
AddSpotPoint = aCfg.SpotPointsPerBattle,
|
||||
AfterSpotPoint = after,
|
||||
LeveledUp = xp.LeveledUp,
|
||||
ClassId = run.ClassId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@ public interface IStoryService
|
||||
Task<InfoResponse> GetInfoAsync(StoryApiType apiType, int sectionId, int? charaId, long viewerId);
|
||||
Task<GetDeckListResponse> GetDeckListAsync(StoryApiType apiType, int storyId, long viewerId);
|
||||
Task<StartResponse> StartAsync(StoryApiType apiType, int[] storyIds, long viewerId);
|
||||
Task<FinishResponse> FinishAsync(StoryApiType apiType, FinishRequest req, long viewerId);
|
||||
Task<StoryFinishOutcome> FinishAsync(StoryApiType apiType, FinishRequest req, long viewerId);
|
||||
Task<FinishResponse> AllFinishAsync(StoryApiType apiType, int[] storyIds, bool isFinish, long viewerId);
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ public class StoryService : IStoryService
|
||||
resp["mission_parameter"] = Array.Empty<object>();
|
||||
return resp;
|
||||
}
|
||||
public async Task<FinishResponse> FinishAsync(StoryApiType apiType, FinishRequest req, long viewerId)
|
||||
public async Task<StoryFinishOutcome> FinishAsync(StoryApiType apiType, FinishRequest req, long viewerId)
|
||||
{
|
||||
var chapter = await _master.GetChapterByIdAsync(req.StoryId);
|
||||
if (chapter is null)
|
||||
@@ -497,9 +497,9 @@ public class StoryService : IStoryService
|
||||
// StorySubChapter lookup and record progress at the sub's id with isFinish+isSkipped
|
||||
// both true (sub-chapters are always narrative-only — no battle settings on the wire).
|
||||
var sub = await _master.FindSubChapterByStoryIdAsync(req.StoryId);
|
||||
if (sub is null) return new FinishResponse();
|
||||
if (sub is null) return new StoryFinishOutcome(new FinishResponse(), LeveledUp: false, ClassId: null);
|
||||
await _viewer.UpsertProgressAsync(viewerId, req.StoryId, isFinish: true, isSkipped: true);
|
||||
return new FinishResponse();
|
||||
return new StoryFinishOutcome(new FinishResponse(), LeveledUp: false, ClassId: null);
|
||||
}
|
||||
|
||||
var progress = (await _viewer.GetProgressForChaptersAsync(viewerId, new[] { req.StoryId }))
|
||||
@@ -581,6 +581,7 @@ public class StoryService : IStoryService
|
||||
resp.StoryRewardList.AddRange(storyRewardDeltas);
|
||||
}
|
||||
|
||||
bool leveledUp = false;
|
||||
if (firstClear && isPlayShape)
|
||||
{
|
||||
// XP grant requires a class_id (only sent on play-shape). No-battle chapters
|
||||
@@ -593,8 +594,11 @@ public class StoryService : IStoryService
|
||||
resp.GetClassExperience = xp.GetXp.ToString();
|
||||
resp.ClassExperience = xp.TotalXp;
|
||||
resp.ClassLevel = xp.Level.ToString();
|
||||
leveledUp = xp.LeveledUp;
|
||||
}
|
||||
}
|
||||
|
||||
return new StoryFinishOutcome(resp, LeveledUp: leveledUp, ClassId: req.ClassId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -609,7 +613,7 @@ public class StoryService : IStoryService
|
||||
await _viewer.UpsertProgressAsync(viewerId, req.StoryId, isFinish: null, isSkipped: true);
|
||||
}
|
||||
|
||||
return resp;
|
||||
return new StoryFinishOutcome(resp, LeveledUp: false, ClassId: null);
|
||||
}
|
||||
public async Task<FinishResponse> AllFinishAsync(StoryApiType apiType, int[] storyIds, bool isFinish, long viewerId)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user