feat(遊戲化): 新增大廳、附身、時間流、後日談進度等遊戲化系統功能

新增角色大廳(lobby)邀約與通話、附身(possession)、時鐘(clock)抽象、
時間流與睡眠負債、後日談進度、行為選擇與旁觀反應等模組,並補齊對應冒煙
測試(R~V)與單元測試;同步調整 web/mobile 對應頁面與元件。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 09:10:26 +08:00
co-authored by Claude Sonnet 5
parent 127a59dafd
commit 1bea06e3cd
195 changed files with 17219 additions and 738 deletions
+16 -11
View File
@@ -99,9 +99,10 @@ export default async function smokeK() {
}
// K-2 群聊行為變化:同角色、同輸入,在一對一與群聊應輸出不同(群聊更矜持,命中不同親密度分層的模板)。
const oneOnOneReply = await post(`/chat/${GENKI_ID}`, {
// T38:一對一改走 SOLO 房(DialogueService 管線),群聊仍走 GROUP 房(RoomGenerationService 管線)。
const soloRoom = await post("/room/solo", { userId: USER_ID, characterId: GENKI_ID });
const oneOnOneReply = await post(`/room/${soloRoom.id}/message`, {
userId: USER_ID,
sessionId: "smoke-k-11-session",
text: "今天過得如何呀",
});
const groupIntimacyRoom = await post("/room", { userId: USER_ID, characterIds: [GENKI_ID], mode: "GROUP" });
@@ -162,7 +163,9 @@ export default async function smokeK() {
throw new Error("被虧的那位(小元)記憶權重應該高於旁觀者(小冷)");
}
// K-6 角色自聊:話題種子驅動、發言權機制沿用、空轉偵測與輪數上限確保一定會收斂(不會無限跑下去)。
// K-6/T6 角色自聊:話題種子驅動、發言權機制沿用;行為種類(TALK/ACTION/DELEGATE/LEAVE)由
// BehaviorSelectionService 依人格參數+情緒/行程/話題狀態算權重抽樣決定,不再受舊版 12 輪硬上限
// 節制——測試角色沒有設定行程/極端情緒,三項離開動因皆不成立,驗證的是「可以持續推進超過舊上限」。
const selfChatRoom = await post("/room", {
userId: USER_ID,
characterIds: [GENKI_ID, TSUNDERE_ID, COOL_ID],
@@ -171,8 +174,9 @@ export default async function smokeK() {
});
let ended = false;
let turns = 0;
const SAFETY_CAP = 30; // 遠大於 SELF_CHAT_MAX_TURNS,純粹防止測試本身無限迴圈
for (let i = 0; i < SAFETY_CAP && !ended; i++) {
const PAST_OLD_CAP = 20; // 大於舊版 SELF_CHAT_MAX_TURNS(12),驗證不再被舊上限強制結束
const OLD_CAP = 12;
for (let i = 0; i < PAST_OLD_CAP && !ended; i++) {
const result = await post(`/room/${selfChatRoom.id}/self-chat/advance`, {});
if (result.ended) {
ended = true;
@@ -180,11 +184,11 @@ export default async function smokeK() {
turns += 1;
}
}
if (!ended) {
throw new Error("角色自聊必須在有限輪數內收斂結束,不應該無限進行下去");
}
if (turns === 0) {
throw new Error("角色自聊應該至少產生過一輪對話");
// 測試角色沒有設定行程/極端情緒,LEAVE 只可能因話題耗盡觸發;三人須「各自都」耗盡話題才會整室結束,
// 這在短短 20 輪內發生的機率極低,因此正常情況下不應該提前結束——若真的提前結束也至少要確認曾經推進
// 超過舊版 12 輪上限,而不是被舊上限機制卡住(該機制已整個移除,理論上不會再發生)。
if (turns <= OLD_CAP) {
throw new Error(`角色自聊應可連續推進超過舊版 ${OLD_CAP} 輪上限,實際只推進了 ${turns} 輪就停止`);
}
// 使用者插話即切換為群聊模式。
@@ -205,6 +209,7 @@ export default async function smokeK() {
throw new Error("使用者插話後應該切換為群聊模式");
}
// 清理本次測試建立的角色(cascade 會一併清掉關係、記憶等)。
// 清理本次測試建立的角色(cascade 會一併清掉關係、記憶等)與本次建立的 SOLO 房。
await prisma.room.deleteMany({ where: { id: soloRoom.id } });
await prisma.character.deleteMany({ where: { id: { in: [GENKI_ID, TSUNDERE_ID, COOL_ID] } } });
}