fix: add runId to seed calculations and use seeded random for treasure loot
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
Fixes #299: Seed calculation now includes runId component per spec (seed = floor × 12345 + runId) Fixes #298: Treasure loot now uses seeded random instead of Math.random() Changes: - Added runId field to CombatState type - Generated random runId on spire entry in createEnterSpireMode - Updated getRoomsForFloor, generateSpireRoomType, generateSpireFloorState, generateTreasureLoot to accept and use runId - Updated all call sites in combat-descent-actions.ts and combatStore.ts - Treasure loot item count now uses seeded RNG instead of Math.random()
This commit is contained in:
@@ -35,6 +35,7 @@ export function enterDescentMode(get: GetFn, set: SetFn): void {
|
||||
climbDirection: 'down',
|
||||
descentPeak: { floor: s.currentFloor, roomIndex: s.currentRoomIndex },
|
||||
isDescentComplete: false,
|
||||
runId: s.runId,
|
||||
});
|
||||
get().addActivityLog('floor_transition',
|
||||
`Beginning descent from Floor ${s.currentFloor}, Room ${s.currentRoomIndex + 1}`);
|
||||
@@ -58,9 +59,10 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
|
||||
|
||||
if (s.currentRoomIndex <= 0) {
|
||||
const newFloor = s.currentFloor - 1;
|
||||
const newRoomsPerFloor = getRoomsForFloor(newFloor, newFloor * 12345);
|
||||
const seed = newFloor * 12345 + s.runId;
|
||||
const newRoomsPerFloor = getRoomsForFloor(newFloor, seed);
|
||||
const newRoomIndex = newRoomsPerFloor - 1;
|
||||
const newRoom = generateSpireFloorState(newFloor, newRoomIndex, newRoomsPerFloor);
|
||||
const newRoom = generateSpireFloorState(newFloor, newRoomIndex, newRoomsPerFloor, s.runId);
|
||||
const newFloorHP = calcRoomHP(newRoom);
|
||||
set({
|
||||
currentFloor: newFloor,
|
||||
@@ -74,7 +76,7 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
|
||||
get().addActivityLog('floor_transition', `Descended to Floor ${newFloor}`);
|
||||
} else {
|
||||
const newRoomIndex = s.currentRoomIndex - 1;
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, newRoomIndex, s.roomsPerFloor);
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, newRoomIndex, s.roomsPerFloor, s.runId);
|
||||
const newFloorHP = calcRoomHP(newRoom);
|
||||
set({
|
||||
currentRoomIndex: newRoomIndex,
|
||||
@@ -98,7 +100,7 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
|
||||
if (s.currentRoomIndex + 1 >= s.roomsPerFloor) {
|
||||
const newFloor = Math.min(s.currentFloor + 1, 100);
|
||||
const newRoomsPerFloor = getRoomsForFloor(newFloor, newFloor * 12345);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, newRoomsPerFloor);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, newRoomsPerFloor, s.runId);
|
||||
const newFloorHP = calcRoomHP(newRoom);
|
||||
set({
|
||||
currentFloor: newFloor,
|
||||
@@ -112,7 +114,7 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
|
||||
get().addActivityLog('floor_transition', `Ascending to Floor ${newFloor}`);
|
||||
} else {
|
||||
const newRoomIndex = s.currentRoomIndex + 1;
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, newRoomIndex, s.roomsPerFloor);
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, newRoomIndex, s.roomsPerFloor, s.runId);
|
||||
const newFloorHP = calcRoomHP(newRoom);
|
||||
set({
|
||||
currentRoomIndex: newRoomIndex,
|
||||
@@ -213,7 +215,7 @@ export function onEnterRoomDescend(get: GetFn, set: SetFn): void {
|
||||
const didReset = get().roomResetState[key];
|
||||
|
||||
if (didReset) {
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, s.currentRoomIndex, s.roomsPerFloor);
|
||||
const newRoom = generateSpireFloorState(s.currentFloor, s.currentRoomIndex, s.roomsPerFloor, s.runId);
|
||||
set({ currentRoom: newRoom, castProgress: 0 });
|
||||
get().addActivityLog('floor_transition',
|
||||
`Floor ${s.currentFloor} Room ${s.currentRoomIndex + 1} has reset — enemies respawned`);
|
||||
@@ -243,9 +245,10 @@ export function createEnterSpireMode(get: GetFn, set: SetFn) {
|
||||
const prestigeStore = usePrestigeStore.getState();
|
||||
const spireKey = prestigeStore.prestigeUpgrades.spireKey || 0;
|
||||
const startFloor = 1 + (spireKey * 2);
|
||||
const seed = startFloor * 12345;
|
||||
const runId = Math.floor(Math.random() * 2147483647);
|
||||
const seed = startFloor * 12345 + runId;
|
||||
const rooms = getRoomsForFloor(startFloor, seed);
|
||||
const freshRoom = generateSpireFloorState(startFloor, 0, rooms);
|
||||
const freshRoom = generateSpireFloorState(startFloor, 0, rooms, runId);
|
||||
|
||||
set({
|
||||
spireMode: true,
|
||||
@@ -253,6 +256,7 @@ export function createEnterSpireMode(get: GetFn, set: SetFn) {
|
||||
currentFloor: startFloor,
|
||||
startFloor,
|
||||
exitFloor: startFloor,
|
||||
runId,
|
||||
currentRoomIndex: 0,
|
||||
roomsPerFloor: rooms,
|
||||
floorHP: calcRoomHP(freshRoom),
|
||||
|
||||
@@ -31,6 +31,10 @@ export interface CombatState {
|
||||
climbDirection: 'up' | 'down' | null;
|
||||
isDescending: boolean;
|
||||
|
||||
// ─── Spec: Run identity (climbing spec §4.2, §7) ──────────────────────
|
||||
/** Unique run ID, generated on spire entry. Used as seed component: floor × 12345 + runId */
|
||||
runId: number;
|
||||
|
||||
// ─── Spec: Room navigation (climbing spec §6) ───────────────────────────
|
||||
/** Floor the player entered at (= 1 + spireKey × 2) */
|
||||
startFloor: number;
|
||||
|
||||
@@ -47,6 +47,9 @@ export const useCombatStore = create<CombatStore>()(
|
||||
currentRoomIndex: 0,
|
||||
roomsPerFloor: 1,
|
||||
|
||||
// ─── Spec: Run identity (climbing spec §4.2, §7) ────────────────────
|
||||
runId: 0,
|
||||
|
||||
// ─── Spec: Descent tracking state ─────────────────────────────────────
|
||||
descentPeak: null,
|
||||
roomResetState: {},
|
||||
@@ -166,8 +169,9 @@ export const useCombatStore = create<CombatStore>()(
|
||||
set((s) => {
|
||||
if (s.currentFloor <= 1) return s;
|
||||
const newFloor = s.currentFloor - 1;
|
||||
const rooms = getRoomsForFloor(newFloor, newFloor * 12345);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, rooms);
|
||||
const seed = newFloor * 12345 + s.runId;
|
||||
const rooms = getRoomsForFloor(newFloor, seed);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, rooms, s.runId);
|
||||
return {
|
||||
currentFloor: newFloor,
|
||||
currentRoom: newRoom,
|
||||
@@ -180,7 +184,8 @@ export const useCombatStore = create<CombatStore>()(
|
||||
|
||||
exitSpireMode: () => {
|
||||
set((s) => {
|
||||
const rooms = getRoomsForFloor(s.exitFloor, s.exitFloor * 12345);
|
||||
const seed = s.exitFloor * 12345 + s.runId;
|
||||
const rooms = getRoomsForFloor(s.exitFloor, seed);
|
||||
return {
|
||||
spireMode: false,
|
||||
currentAction: 'meditate',
|
||||
@@ -189,7 +194,7 @@ export const useCombatStore = create<CombatStore>()(
|
||||
currentFloor: s.exitFloor,
|
||||
floorHP: getFloorMaxHP(s.exitFloor),
|
||||
floorMaxHP: getFloorMaxHP(s.exitFloor),
|
||||
currentRoom: generateSpireFloorState(s.exitFloor, 0, rooms),
|
||||
currentRoom: generateSpireFloorState(s.exitFloor, 0, rooms, s.runId),
|
||||
castProgress: 0,
|
||||
clearedFloors: {},
|
||||
clearedRooms: {},
|
||||
@@ -362,6 +367,7 @@ export const useCombatStore = create<CombatStore>()(
|
||||
guardianBarrier: state.guardianBarrier,
|
||||
guardianBarrierMax: state.guardianBarrierMax,
|
||||
meleeSwordProgress: state.meleeSwordProgress,
|
||||
runId: state.runId,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user