fix: prevent non-combat room repeated rewards during descent (Issue #382)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
- Wrap library XP grant in progress < required guard so completed rooms don't grant XP every tick - Add early return in treasure room tick when progress >= required to skip loot processing - Initialize non-combat rooms (library, treasure, recovery) during descent in onEnterRoomDescend - Add regression test file: non-combat-room-reward-guards.test.ts (8 tests)
This commit is contained in:
@@ -214,6 +214,17 @@ export function onEnterRoomDescend(get: GetFn, set: SetFn): void {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize non-combat rooms during descent so they have proper progress tracking
|
||||
if (s.currentRoom.roomType === 'library') {
|
||||
onEnterLibraryRoom(get, set);
|
||||
} else if (s.currentRoom.roomType === 'recovery') {
|
||||
onEnterRecoveryRoom(get, set);
|
||||
} else if (s.currentRoom.roomType === 'treasure') {
|
||||
onEnterTreasureRoom(get, set);
|
||||
} else if (s.currentRoom.roomType === 'puzzle') {
|
||||
onEnterPuzzleRoom(get, set);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,25 +126,27 @@ function tickLibraryRoom(get: GetFn, set: SetFn, hours: number, advance: Advance
|
||||
const progress = (room.libraryProgress || 0) + hours;
|
||||
const required = room.libraryRequired || 1;
|
||||
|
||||
const BASE_LIBRARY_XP_PER_HOUR = 50;
|
||||
const xpGrant = Math.floor(BASE_LIBRARY_XP_PER_HOUR * 25 * hours);
|
||||
if (progress < required) {
|
||||
const BASE_LIBRARY_XP_PER_HOUR = 50;
|
||||
const xpGrant = Math.floor(BASE_LIBRARY_XP_PER_HOUR * 25 * hours);
|
||||
|
||||
if (xpGrant > 0) {
|
||||
const disciplineStore = useDisciplineStore.getState();
|
||||
const allDisciplines = disciplineStore.disciplines;
|
||||
const entries = Object.entries(allDisciplines).filter(([, ds]) => ds && !ds.paused);
|
||||
if (xpGrant > 0) {
|
||||
const disciplineStore = useDisciplineStore.getState();
|
||||
const allDisciplines = disciplineStore.disciplines;
|
||||
const entries = Object.entries(allDisciplines).filter(([, ds]) => ds && !ds.paused);
|
||||
|
||||
if (entries.length > 0) {
|
||||
const [targetId, targetDs] = entries[Math.floor(Math.random() * entries.length)];
|
||||
useDisciplineStore.setState((prev) => ({
|
||||
disciplines: {
|
||||
...prev.disciplines,
|
||||
[targetId]: { ...targetDs, xp: (targetDs?.xp || 0) + xpGrant },
|
||||
},
|
||||
totalXP: prev.totalXP + xpGrant,
|
||||
}));
|
||||
get().addActivityLog('special_effect',
|
||||
`${targetDs?.id || targetId} gained ${xpGrant} XP from studying ancient tomes`);
|
||||
if (entries.length > 0) {
|
||||
const [targetId, targetDs] = entries[Math.floor(Math.random() * entries.length)];
|
||||
useDisciplineStore.setState((prev) => ({
|
||||
disciplines: {
|
||||
...prev.disciplines,
|
||||
[targetId]: { ...targetDs, xp: (targetDs?.xp || 0) + xpGrant },
|
||||
},
|
||||
totalXP: prev.totalXP + xpGrant,
|
||||
}));
|
||||
get().addActivityLog('special_effect',
|
||||
`${targetDs?.id || targetId} gained ${xpGrant} XP from studying ancient tomes`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +182,13 @@ function tickTreasureRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc
|
||||
const loot = room.treasureLoot || [];
|
||||
const claimed = room.treasureLootClaimed || [];
|
||||
|
||||
if (progress >= required) {
|
||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: claimed } });
|
||||
get().addActivityLog('special_effect', `Treasure room looted — ${claimed.length} items recovered`);
|
||||
advance(get, set);
|
||||
return;
|
||||
}
|
||||
|
||||
const thresholds = [0.10, 0.50, 0.95, 1.0];
|
||||
const newlyClaimed: number[] = [];
|
||||
const claimedSet = new Set(claimed);
|
||||
@@ -228,14 +237,7 @@ function tickTreasureRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc
|
||||
}
|
||||
}
|
||||
|
||||
if (progress >= required) {
|
||||
const claimedCount = claimedSet.size;
|
||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
||||
get().addActivityLog('special_effect', `Treasure room looted — ${claimedCount} items recovered`);
|
||||
advance(get, set);
|
||||
} else {
|
||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
||||
}
|
||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
||||
}
|
||||
|
||||
function tickPuzzleRoom(get: GetFn, set: SetFn, hours: number, advance: AdvanceFn): void {
|
||||
|
||||
Reference in New Issue
Block a user