@@ -15,6 +15,7 @@ export interface TechUpgrade {
1515 points : number ;
1616 tier : number ;
1717 sprite_rect ?: { x : number ; y : number ; width : number ; height : number } ;
18+ gemCost ?: number ;
1819}
1920
2021export function useTreeOptimizer ( ) {
@@ -77,8 +78,10 @@ export function useTreeOptimizer() {
7778 } ;
7879
7980 // 4. Optimization Logic
81+ const { data : forgeConfig } = useGameData < any > ( 'ForgeConfig.json' ) ;
82+
8083 const optimization = useMemo ( ( ) => {
81- if ( ! mapping || ! library || ! upgradeLibrary || ! dayConfig ) return null ;
84+ if ( ! mapping || ! library || ! upgradeLibrary || ! dayConfig || ! forgeConfig ) return null ;
8285
8386 // Map Tier -> Points
8487 const tierPoints : Record < number , number > = {
@@ -107,25 +110,31 @@ export function useTreeOptimizer() {
107110 }
108111
109112 let totalPoints = 0 ;
110- let timeRemainingSeconds = timeLimitHours * 3600 ;
113+ const baseTimeLimitSeconds = timeLimitHours * 3600 ;
114+
115+ // Track resources
116+ let accumulatedTimeSeconds = 0 ;
117+ let accumulatedGemCost = 0 ;
118+ const gemLimit = ( profile . misc . useGemsInCalculators ? profile . misc . gemCount : 0 ) ;
111119 let potionsRemaining = potions ;
120+
112121 const actions : TechUpgrade [ ] = [ ] ;
122+ const gemCostPerSecond = forgeConfig . TechTreeGemSkipCostPerSecond || 0.003 ;
113123
114124 // Simple Greedy Simulation
115- // While we have time and potions, find all "available" upgrades
116- // Selection criteria: Max (Points / Duration) to fit most inside 1h or 24h?
117- // Actually Max Points / Duration is usually best for time-limited ranking
118-
119125 const maxIter = 500 ; // Safety break
120126 let iter = 0 ;
121127
122- while ( timeRemainingSeconds > 0 && potionsRemaining > 0 && iter < maxIter ) {
128+ // We continue as long as we can potentially perform an action.
129+ // Determining "can perform" is handled inside by the search for a candidate.
130+ while ( iter < maxIter ) {
123131 iter ++ ;
124- const possibleUpgrades : TechUpgrade [ ] = [ ] ;
125132
126133 // Calculate current bonuses
127134 const bonuses = calculateTechBonuses ( currentTree ) ;
128135
136+ const possibleUpgrades : TechUpgrade [ ] = [ ] ;
137+
129138 // Find all available upgrades
130139 Object . entries ( mapping . trees || { } ) . forEach ( ( [ treeName , treeDef ] : [ string , any ] ) => {
131140 treeDef . nodes . forEach ( ( node : any ) => {
@@ -137,7 +146,7 @@ export function useTreeOptimizer() {
137146 if ( currentLvl < maxLvl ) {
138147 // Check requirements
139148 const reqsMet = ( node . requirements || [ ] ) . every ( ( reqId : number ) => {
140- return ( currentTree [ treeName ] ?. [ reqId ] || 0 ) >= 1 ; // Usually need lvl 1 to unlock next
149+ return ( currentTree [ treeName ] ?. [ reqId ] || 0 ) >= 1 ;
141150 } ) ;
142151
143152 if ( reqsMet ) {
@@ -173,36 +182,76 @@ export function useTreeOptimizer() {
173182 if ( possibleUpgrades . length === 0 ) break ;
174183
175184 // Sort by efficiency (Points / Duration)
176- // If duration is 0, give it high priority
177185 possibleUpgrades . sort ( ( a , b ) => ( b . points / ( b . duration || 1 ) ) - ( a . points / ( a . duration || 1 ) ) ) ;
178186
179- // Find first one that fits budget
180- const best = possibleUpgrades . find ( upg => upg . cost <= potionsRemaining && upg . duration <= timeRemainingSeconds ) ;
187+ // Find first one that fits budget (Potions AND Gems)
188+ const best = possibleUpgrades . find ( upg => {
189+ // Check Potion Cost
190+ if ( upg . cost > potionsRemaining ) return false ;
191+
192+ // Check Gem Cost
193+ const startTime = accumulatedTimeSeconds ;
194+ const endTime = startTime + upg . duration ;
195+ let neededGems = 0 ;
196+
197+ if ( endTime > baseTimeLimitSeconds ) {
198+ const overlap = Math . min ( upg . duration , endTime - Math . max ( startTime , baseTimeLimitSeconds ) ) ;
199+ if ( overlap > 0 ) {
200+ neededGems = Math . ceil ( overlap * gemCostPerSecond ) ;
201+ }
202+ }
203+
204+ if ( neededGems > ( gemLimit - accumulatedGemCost ) ) return false ;
205+
206+ return true ;
207+ } ) ;
181208
182209 if ( best ) {
183- actions . push ( best ) ;
210+ // Re-calculate gem cost to attach (could optimize by returning it from find, but this is cheap)
211+ const startTime = accumulatedTimeSeconds ;
212+ const endTime = startTime + best . duration ;
213+ let gemCost = 0 ;
214+ if ( endTime > baseTimeLimitSeconds ) {
215+ const overlap = Math . min ( best . duration , endTime - Math . max ( startTime , baseTimeLimitSeconds ) ) ;
216+ if ( overlap > 0 ) {
217+ gemCost = Math . ceil ( overlap * gemCostPerSecond ) ;
218+ }
219+ }
220+
221+ actions . push ( { ...best , gemCost } ) ;
222+
184223 totalPoints += best . points ;
185224 potionsRemaining -= best . cost ;
186- timeRemainingSeconds -= best . duration ;
225+ accumulatedTimeSeconds += best . duration ;
226+ accumulatedGemCost += gemCost ;
227+
187228 // Update virtual tree
188229 if ( ! currentTree [ best . tree ] ) currentTree [ best . tree ] = { } ;
189230 currentTree [ best . tree ] [ best . nodeId ] = best . toLevel ;
190231 } else {
191- // No more upgrades fit
232+ // No upgrades fit our remaining resources
192233 break ;
193234 }
194235 }
195236
237+ // Calculate total time used
238+ const usedSeconds = accumulatedTimeSeconds ;
239+ const gemTimeSeconds = Math . max ( 0 , usedSeconds - baseTimeLimitSeconds ) ;
240+ const baseTimeSeconds = Math . min ( usedSeconds , baseTimeLimitSeconds ) ;
241+
196242 return {
197243 totalPoints,
198244 actions,
199- timeUsed : ( timeLimitHours * 3600 - timeRemainingSeconds ) / 3600 ,
245+ timeUsed : usedSeconds / 3600 ,
246+ baseTimeUsed : baseTimeSeconds / 3600 ,
247+ gemTimeUsed : gemTimeSeconds / 3600 ,
200248 potionsUsed : potions - potionsRemaining ,
201249 remainingPotions : potionsRemaining ,
202- finalBonuses : calculateTechBonuses ( currentTree )
250+ finalBonuses : calculateTechBonuses ( currentTree ) ,
251+ totalGemsUsed : accumulatedGemCost
203252 } ;
204253
205- } , [ mapping , library , upgradeLibrary , dayConfig , treeMode , profile . techTree , timeLimitHours , potions ] ) ;
254+ } , [ mapping , library , upgradeLibrary , dayConfig , treeMode , profile . techTree , timeLimitHours , potions , forgeConfig , profile . misc . gemCount , profile . misc . useGemsInCalculators ] ) ;
206255
207256 const applyUpgrades = ( selectedActions : TechUpgrade [ ] ) => {
208257 if ( selectedActions . length === 0 ) return ;
0 commit comments