fix: meditation multiplier cap 2.5x, discipline reactivation, Spire crash, earthShard recipe, fabricator E2E test
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 4m53s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 4m53s
This commit is contained in:
@@ -89,7 +89,7 @@ export const DisciplinesTab: React.FC = () => {
|
||||
|
||||
const handleToggle = useCallback((id: string, paused: boolean) => {
|
||||
if (paused) {
|
||||
activate(id, { elements, signedPacts });
|
||||
activate(id, { elements, signedPacts, rawMana });
|
||||
} else {
|
||||
deactivate(id);
|
||||
}
|
||||
|
||||
@@ -102,12 +102,30 @@ export function SpireCombatPage() {
|
||||
insight: s.insight,
|
||||
})));
|
||||
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
const { equippedInstances, equipmentInstances } = useCraftingStore(useShallow((s) => ({
|
||||
equippedInstances: s.equippedInstances,
|
||||
equipmentInstances: s.equipmentInstances,
|
||||
})));
|
||||
|
||||
const { maxMana, baseRegen } = useSpireStats(prestigeUpgrades, equippedInstances, equipmentInstances);
|
||||
|
||||
const totalRooms = useMemo(() => getRoomsForFloor(currentFloor), [currentFloor]);
|
||||
// Use a deterministic seed based on floor to avoid Math.random() causing
|
||||
// referential instability and infinite re-render loops.
|
||||
const seededRandom = useMemo(() => {
|
||||
let seed = currentFloor * 12345;
|
||||
return () => {
|
||||
seed = (seed * 16807 + 0) % 2147483647;
|
||||
return (seed - 1) / 2147483646;
|
||||
};
|
||||
}, [currentFloor]);
|
||||
const totalRooms = useMemo(() => {
|
||||
if (isGuardianFloor(currentFloor)) return 1;
|
||||
const base = 5;
|
||||
const range = 10;
|
||||
const floorBonus = Math.min(range, Math.floor(currentFloor / 20));
|
||||
const randomVariation = Math.floor(seededRandom() * 3);
|
||||
return base + floorBonus + randomVariation;
|
||||
}, [currentFloor, seededRandom]);
|
||||
|
||||
useEffect(() => {
|
||||
setRoomsCleared(0);
|
||||
|
||||
@@ -237,16 +237,16 @@ describe('getMeditationBonus', () => {
|
||||
});
|
||||
|
||||
it('should follow continuous ramp formula', () => {
|
||||
// At 4 hours: 1 + (4/8)*4 = 3.0
|
||||
// At 4 hours: 1 + (4/8)*4 = 3.0, but capped at 2.5
|
||||
const ticksFor4Hours = 4 / 0.04;
|
||||
const result = getMeditationBonus(ticksFor4Hours);
|
||||
expect(result).toBeCloseTo(3.0, 5);
|
||||
expect(result).toBeCloseTo(2.5, 5);
|
||||
});
|
||||
|
||||
it('should cap at 5.0', () => {
|
||||
it('should cap at 2.5', () => {
|
||||
const ticksFor8Hours = 8 / 0.04;
|
||||
const result = getMeditationBonus(ticksFor8Hours);
|
||||
expect(result).toBeCloseTo(5.0, 5);
|
||||
expect(result).toBeCloseTo(2.5, 5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -173,11 +173,11 @@ describe('getMeditationBonus', () => {
|
||||
expect(result).toBeCloseTo(2.0, 5);
|
||||
});
|
||||
|
||||
it('should cap at 5.0 with no discipline bonus', () => {
|
||||
// At 8+ hours: cap at 5.0
|
||||
it('should cap at 2.5 with no discipline bonus', () => {
|
||||
// At 3+ hours: cap at 2.5
|
||||
const ticksFor8Hours = 8 / HOURS_PER_TICK;
|
||||
const result = getMeditationBonus(ticksFor8Hours);
|
||||
expect(result).toBeCloseTo(5.0, 5);
|
||||
expect(result).toBeCloseTo(2.5, 5);
|
||||
});
|
||||
|
||||
it('should ramp linearly: 1 hour → 1.5', () => {
|
||||
@@ -194,12 +194,12 @@ describe('getMeditationBonus', () => {
|
||||
});
|
||||
|
||||
it('should respect discipline meditation cap bonus', () => {
|
||||
// With +3.5 discipline cap, max = 8.5
|
||||
// Need more than 8 hours for the higher cap to matter
|
||||
// At 16 hours without cap: 1 + (16/8)*4 = 9.0, capped to 8.5
|
||||
// With +3.5 discipline cap, max = 6.0
|
||||
// Need more than 3 hours for the higher cap to matter
|
||||
// At 16 hours without cap: 1 + (16/8)*4 = 9.0, capped to 6.0
|
||||
const ticksFor16Hours = 16 / HOURS_PER_TICK;
|
||||
const result = getMeditationBonus(ticksFor16Hours, 1, 3.5);
|
||||
expect(result).toBeCloseTo(8.5, 5);
|
||||
expect(result).toBeCloseTo(6.0, 5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -164,6 +164,22 @@ export const MATERIAL_RECIPES: FabricatorRecipe[] = [
|
||||
resultMaterial: 'crystalCrystal',
|
||||
resultAmount: 1,
|
||||
},
|
||||
{
|
||||
id: 'earthShardCraft',
|
||||
name: 'Earth Shard',
|
||||
description: 'Grind an Earth Attuned Crystal into a shard. Used for earth equipment crafting.',
|
||||
manaType: 'earth',
|
||||
equipmentTypeId: 'basicStaff',
|
||||
slot: 'mainHand',
|
||||
materials: { earthCrystal: 1 },
|
||||
manaCost: 50,
|
||||
craftTime: 1,
|
||||
rarity: 'common',
|
||||
gearTrait: 'Produces 1 Earth Shard',
|
||||
recipeType: 'material',
|
||||
resultMaterial: 'earthShard',
|
||||
resultAmount: 1,
|
||||
},
|
||||
{
|
||||
id: 'elementalCore',
|
||||
name: 'Elemental Core',
|
||||
|
||||
@@ -121,8 +121,8 @@ export function getMeditationBonus(
|
||||
): number {
|
||||
const hours = meditateTicks * HOURS_PER_TICK;
|
||||
|
||||
// Continuous ramp: 1 + (hours / 8) * 4, capped at 5.0 + disciplineMeditationCap
|
||||
const maxMultiplier = 5.0 + disciplineMeditationCap;
|
||||
// Continuous ramp: 1 + (hours / 8) * 4, capped at 2.5 + disciplineMeditationCap
|
||||
const maxMultiplier = 2.5 + disciplineMeditationCap;
|
||||
const bonus = Math.min(1 + (hours / 8) * 4, maxMultiplier);
|
||||
|
||||
// Apply meditation efficiency from upgrades
|
||||
|
||||
Reference in New Issue
Block a user