Phase 4: Study effects (remaining 6)
This commit is contained in:
@@ -157,9 +157,10 @@ export function computeTotalMaxMana(
|
|||||||
effects?: UnifiedEffects
|
effects?: UnifiedEffects
|
||||||
): number {
|
): number {
|
||||||
const pu = state.prestigeUpgrades;
|
const pu = state.prestigeUpgrades;
|
||||||
|
const skillMult = effects?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
100 +
|
100 +
|
||||||
(state.skills.manaWell || 0) * 100 +
|
(state.skills.manaWell || 0) * 100 * skillMult +
|
||||||
(pu.manaWell || 0) * 500;
|
(pu.manaWell || 0) * 500;
|
||||||
|
|
||||||
if (!effects) {
|
if (!effects) {
|
||||||
@@ -178,10 +179,11 @@ export function computeTotalRegen(
|
|||||||
): number {
|
): number {
|
||||||
const pu = state.prestigeUpgrades;
|
const pu = state.prestigeUpgrades;
|
||||||
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
||||||
|
const skillMult = effects?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
2 +
|
2 +
|
||||||
(state.skills.manaFlow || 0) * 1 +
|
(state.skills.manaFlow || 0) * 1 * skillMult +
|
||||||
(state.skills.manaSpring || 0) * 2 +
|
(state.skills.manaSpring || 0) * 2 * skillMult +
|
||||||
(pu.manaFlow || 0) * 0.5;
|
(pu.manaFlow || 0) * 0.5;
|
||||||
|
|
||||||
let regen = base * temporalBonus;
|
let regen = base * temporalBonus;
|
||||||
@@ -202,10 +204,11 @@ export function computeTotalClickMana(
|
|||||||
state: Pick<GameState, 'skills' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
state: Pick<GameState, 'skills' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||||
effects?: UnifiedEffects
|
effects?: UnifiedEffects
|
||||||
): number {
|
): number {
|
||||||
|
const skillMult = effects?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
1 +
|
1 +
|
||||||
(state.skills.manaTap || 0) * 1 +
|
(state.skills.manaTap || 0) * 1 * skillMult +
|
||||||
(state.skills.manaSurge || 0) * 3;
|
(state.skills.manaSurge || 0) * 3 * skillMult;
|
||||||
|
|
||||||
if (!effects) {
|
if (!effects) {
|
||||||
effects = getUnifiedEffects(state as any);
|
effects = getUnifiedEffects(state as any);
|
||||||
|
|||||||
+32
-9
@@ -302,9 +302,10 @@ export function computeMaxMana(
|
|||||||
effects?: ComputedEffects | UnifiedEffects
|
effects?: ComputedEffects | UnifiedEffects
|
||||||
): number {
|
): number {
|
||||||
const pu = state.prestigeUpgrades;
|
const pu = state.prestigeUpgrades;
|
||||||
|
const skillMult = (effects as any)?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
100 +
|
100 +
|
||||||
(state.skills.manaWell || 0) * 100 +
|
(state.skills.manaWell || 0) * 100 * skillMult +
|
||||||
(pu.manaWell || 0) * 500;
|
(pu.manaWell || 0) * 500;
|
||||||
|
|
||||||
// If effects not provided, compute unified effects (includes equipment)
|
// If effects not provided, compute unified effects (includes equipment)
|
||||||
@@ -350,10 +351,11 @@ export function computeRegen(
|
|||||||
): number {
|
): number {
|
||||||
const pu = state.prestigeUpgrades;
|
const pu = state.prestigeUpgrades;
|
||||||
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
||||||
|
const skillMult = (effects as any)?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
2 +
|
2 +
|
||||||
(state.skills.manaFlow || 0) * 1 +
|
(state.skills.manaFlow || 0) * 1 * skillMult +
|
||||||
(state.skills.manaSpring || 0) * 2 +
|
(state.skills.manaSpring || 0) * 2 * skillMult +
|
||||||
(pu.manaFlow || 0) * 0.5;
|
(pu.manaFlow || 0) * 0.5;
|
||||||
|
|
||||||
let regen = base * temporalBonus;
|
let regen = base * temporalBonus;
|
||||||
@@ -399,10 +401,11 @@ export function computeClickMana(
|
|||||||
state: Pick<GameState, 'skills' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
state: Pick<GameState, 'skills' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||||
effects?: ComputedEffects | UnifiedEffects
|
effects?: ComputedEffects | UnifiedEffects
|
||||||
): number {
|
): number {
|
||||||
|
const skillMult = (effects as any)?.skillLevelMultiplier || 1;
|
||||||
const base =
|
const base =
|
||||||
1 +
|
1 +
|
||||||
(state.skills.manaTap || 0) * 1 +
|
(state.skills.manaTap || 0) * 1 * skillMult +
|
||||||
(state.skills.manaSurge || 0) * 3;
|
(state.skills.manaSurge || 0) * 3 * skillMult;
|
||||||
|
|
||||||
// If effects not provided, compute unified effects (includes equipment)
|
// If effects not provided, compute unified effects (includes equipment)
|
||||||
if (!effects && state.equipmentInstances && state.equippedInstances) {
|
if (!effects && state.equipmentInstances && state.equippedInstances) {
|
||||||
@@ -437,16 +440,18 @@ function getElementalBonus(spellElem: string, floorElem: string): number {
|
|||||||
export function calcDamage(
|
export function calcDamage(
|
||||||
state: Pick<GameState, 'skills' | 'signedPacts'>,
|
state: Pick<GameState, 'skills' | 'signedPacts'>,
|
||||||
spellId: string,
|
spellId: string,
|
||||||
floorElem?: string
|
floorElem?: string,
|
||||||
|
effects?: ComputedEffects | UnifiedEffects
|
||||||
): number {
|
): number {
|
||||||
const sp = SPELLS_DEF[spellId];
|
const sp = SPELLS_DEF[spellId];
|
||||||
if (!sp) return 5;
|
if (!sp) return 5;
|
||||||
const skills = state.skills;
|
const skills = state.skills;
|
||||||
const baseDmg = sp.dmg + (skills.combatTrain || 0) * 5;
|
const skillMult = (effects as any)?.skillLevelMultiplier || 1;
|
||||||
const pct = 1 + (skills.arcaneFury || 0) * 0.1;
|
const baseDmg = sp.dmg + (skills.combatTrain || 0) * 5 * skillMult;
|
||||||
|
const pct = 1 + (skills.arcaneFury || 0) * 0.1 * skillMult;
|
||||||
|
|
||||||
// Elemental mastery bonus
|
// Elemental mastery bonus
|
||||||
const elemMasteryBonus = 1 + (skills.elementalMastery || 0) * 0.15;
|
const elemMasteryBonus = 1 + (skills.elementalMastery || 0) * 0.15 * skillMult;
|
||||||
|
|
||||||
// Guardian bane bonus
|
// Guardian bane bonus
|
||||||
const guardianBonus = floorElem && GUARDIANS[Object.values(GUARDIANS).find(g => g.element === floorElem)?.hp ? 0 : 0]
|
const guardianBonus = floorElem && GUARDIANS[Object.values(GUARDIANS).find(g => g.element === floorElem)?.hp ? 0 : 0]
|
||||||
@@ -954,6 +959,17 @@ export const useGameStore = create<GameStore>()(
|
|||||||
// Calculate base study speed
|
// Calculate base study speed
|
||||||
let studySpeedMult = getStudySpeedMultiplier(skills);
|
let studySpeedMult = getStudySpeedMultiplier(skills);
|
||||||
|
|
||||||
|
// STUDY_RUSH: First hour of study is 2x speed
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.STUDY_RUSH) && consecutiveStudyHours === 0) {
|
||||||
|
studySpeedMult *= 2;
|
||||||
|
log = [`⚡ Study Rush activated! Double speed for the first hour!`, ...log.slice(0, 49)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MENTAL_CLARITY: +10% study speed when mana > 75%
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.MENTAL_CLARITY) && state.rawMana > maxMana * 0.75) {
|
||||||
|
studySpeedMult *= 1.10;
|
||||||
|
}
|
||||||
|
|
||||||
// DEEP_CONCENTRATION: +20% study speed when mana > 90%
|
// DEEP_CONCENTRATION: +20% study speed when mana > 90%
|
||||||
if (hasSpecial(effects, SPECIAL_EFFECTS.DEEP_CONCENTRATION) && state.rawMana > maxMana * 0.9) {
|
if (hasSpecial(effects, SPECIAL_EFFECTS.DEEP_CONCENTRATION) && state.rawMana > maxMana * 0.9) {
|
||||||
studySpeedMult *= 1.20;
|
studySpeedMult *= 1.20;
|
||||||
@@ -1009,6 +1025,13 @@ export const useGameStore = create<GameStore>()(
|
|||||||
skillProgress = { ...skillProgress, [skillId]: 0 };
|
skillProgress = { ...skillProgress, [skillId]: 0 };
|
||||||
log = [`✅ ${SKILLS_DEF[skillId]?.name} Lv.${newLevel} mastered!`, ...log.slice(0, 49)];
|
log = [`✅ ${SKILLS_DEF[skillId]?.name} Lv.${newLevel} mastered!`, ...log.slice(0, 49)];
|
||||||
|
|
||||||
|
// STUDY_REFUND: 25% mana back on study complete
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.STUDY_REFUND)) {
|
||||||
|
const refundAmount = Math.floor(currentStudyTarget.totalCost * 0.25);
|
||||||
|
rawMana += refundAmount;
|
||||||
|
log = [`💰 Study Refund: ${refundAmount} mana returned!`, ...log.slice(0, 49)];
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this skill unlocks effects (research skills)
|
// Check if this skill unlocks effects (research skills)
|
||||||
const effectsToUnlock = EFFECT_RESEARCH_MAPPING[skillId];
|
const effectsToUnlock = EFFECT_RESEARCH_MAPPING[skillId];
|
||||||
if (effectsToUnlock && newLevel >= (SKILLS_DEF[skillId]?.max || 1)) {
|
if (effectsToUnlock && newLevel >= (SKILLS_DEF[skillId]?.max || 1)) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import type { GameState } from './types';
|
import type { GameState } from './types';
|
||||||
import { SKILLS_DEF, SPELLS_DEF, getStudyCostMultiplier } from './constants';
|
import { SKILLS_DEF, SPELLS_DEF, getStudyCostMultiplier } from './constants';
|
||||||
|
import { computeEffects, hasSpecial, SPECIAL_EFFECTS } from './upgrade-effects';
|
||||||
|
|
||||||
// ─── Study Actions Interface ──────────────────────────────────────────────────
|
// ─── Study Actions Interface ──────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -39,19 +40,37 @@ export function createStudySlice(
|
|||||||
|
|
||||||
// Calculate total mana cost and cost per hour
|
// Calculate total mana cost and cost per hour
|
||||||
const costMult = getStudyCostMultiplier(state.skills);
|
const costMult = getStudyCostMultiplier(state.skills);
|
||||||
const totalCost = Math.floor(sk.base * (currentLevel + 1) * costMult);
|
let totalCost = Math.floor(sk.base * (currentLevel + 1) * costMult);
|
||||||
|
|
||||||
|
// CHAIN_STUDY: -5% cost per maxed skill
|
||||||
|
const effects = computeEffects(state.skillUpgrades || {}, state.skillTiers || {});
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.CHAIN_STUDY)) {
|
||||||
|
const maxedSkills = Object.entries(SKILLS_DEF).filter(([id, sk]) =>
|
||||||
|
(state.skills[id] || 0) >= sk.max
|
||||||
|
).length;
|
||||||
|
const discount = Math.pow(0.95, maxedSkills); // -5% per maxed skill
|
||||||
|
totalCost = Math.floor(totalCost * discount);
|
||||||
|
}
|
||||||
|
|
||||||
const manaCostPerHour = Math.ceil(totalCost / sk.studyTime);
|
const manaCostPerHour = Math.ceil(totalCost / sk.studyTime);
|
||||||
|
|
||||||
// Must have at least 1 hour worth of mana to start
|
// Must have at least 1 hour worth of mana to start
|
||||||
if (state.rawMana < manaCostPerHour) return;
|
if (state.rawMana < manaCostPerHour) return;
|
||||||
|
|
||||||
|
// KNOWLEDGE_TRANSFER: New skills start at 10% progress
|
||||||
|
let initialProgress = state.skillProgress[skillId] || 0;
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.KNOWLEDGE_TRANSFER) && initialProgress === 0) {
|
||||||
|
initialProgress = sk.studyTime * 0.10; // 10% of required time
|
||||||
|
log = [`📖 Knowledge Transfer: Starting with 10% progress!`, ...state.log.slice(0, 49)];
|
||||||
|
}
|
||||||
|
|
||||||
// Start studying (no upfront cost - mana is deducted per hour during study)
|
// Start studying (no upfront cost - mana is deducted per hour during study)
|
||||||
set({
|
set({
|
||||||
currentAction: 'study',
|
currentAction: 'study',
|
||||||
currentStudyTarget: {
|
currentStudyTarget: {
|
||||||
type: 'skill',
|
type: 'skill',
|
||||||
id: skillId,
|
id: skillId,
|
||||||
progress: state.skillProgress[skillId] || 0,
|
progress: initialProgress,
|
||||||
required: sk.studyTime,
|
required: sk.studyTime,
|
||||||
manaCostPerHour: manaCostPerHour,
|
manaCostPerHour: manaCostPerHour,
|
||||||
totalCost: totalCost,
|
totalCost: totalCost,
|
||||||
@@ -68,7 +87,18 @@ export function createStudySlice(
|
|||||||
|
|
||||||
// Calculate total mana cost and cost per hour
|
// Calculate total mana cost and cost per hour
|
||||||
const costMult = getStudyCostMultiplier(state.skills);
|
const costMult = getStudyCostMultiplier(state.skills);
|
||||||
const totalCost = Math.floor(sp.unlock * costMult);
|
let totalCost = Math.floor(sp.unlock * costMult);
|
||||||
|
|
||||||
|
// CHAIN_STUDY: -5% cost per maxed skill
|
||||||
|
const effects = computeEffects(state.skillUpgrades || {}, state.skillTiers || {});
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.CHAIN_STUDY)) {
|
||||||
|
const maxedSkills = Object.entries(SKILLS_DEF).filter(([id, sk]) =>
|
||||||
|
(state.skills[id] || 0) >= sk.max
|
||||||
|
).length;
|
||||||
|
const discount = Math.pow(0.95, maxedSkills); // -5% per maxed skill
|
||||||
|
totalCost = Math.floor(totalCost * discount);
|
||||||
|
}
|
||||||
|
|
||||||
const studyTime = sp.studyTime || (sp.tier * 4);
|
const studyTime = sp.studyTime || (sp.tier * 4);
|
||||||
const manaCostPerHour = Math.ceil(totalCost / studyTime);
|
const manaCostPerHour = Math.ceil(totalCost / studyTime);
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ export interface ComputedEffects {
|
|||||||
|
|
||||||
// All active upgrades for display
|
// All active upgrades for display
|
||||||
activeUpgrades: ActiveUpgradeEffect[];
|
activeUpgrades: ActiveUpgradeEffect[];
|
||||||
|
|
||||||
|
// DEEP_UNDERSTANDING: +10% bonus from all skill levels
|
||||||
|
skillLevelMultiplier: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Special Effect IDs ────────────────────────────────────────────────────────
|
// ─── Special Effect IDs ────────────────────────────────────────────────────────
|
||||||
@@ -231,8 +234,14 @@ export function computeEffects(
|
|||||||
permanentRegenBonus: 0,
|
permanentRegenBonus: 0,
|
||||||
specials: new Set<string>(),
|
specials: new Set<string>(),
|
||||||
activeUpgrades,
|
activeUpgrades,
|
||||||
|
skillLevelMultiplier: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Apply DEEP_UNDERSTANDING: +10% bonus from all skill levels
|
||||||
|
if (hasSpecial(effects, SPECIAL_EFFECTS.DEEP_UNDERSTANDING)) {
|
||||||
|
effects.skillLevelMultiplier = 1.10;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply each upgrade effect
|
// Apply each upgrade effect
|
||||||
for (const upgrade of activeUpgrades) {
|
for (const upgrade of activeUpgrades) {
|
||||||
const { effect } = upgrade;
|
const { effect } = upgrade;
|
||||||
|
|||||||
Reference in New Issue
Block a user