@@ -7,6 +7,7 @@ import { levelFromXp } from '../dex/xpTable'
77import { getSpeciesData } from '../dex/species'
88import { MAX_EV_PER_STAT , MAX_EV_TOTAL } from '../dex/evMapping'
99import { Dex } from '@pkmn/sim'
10+ import { getBaseExperience , getEvYield as getPokedexEvYield } from '../dex/pokedex-data'
1011
1112/**
1213 * Settle battle results: XP, EV, level ups, move learning, evolution detection.
@@ -25,14 +26,13 @@ export async function settleBattle(
2526 return { data, learnableMoves : [ ] , pendingEvolutions : [ ] }
2627 }
2728
28- // Calculate XP reward (simplified: base XP from species)
29- const oppSpecies = Dex . species . get ( opponentSpeciesId )
30- const baseXp = ( oppSpecies ?. baseStats ?. hp ?? 50 ) * opponentLevel / 7
31- const xpGained = Math . max ( 1 , Math . floor ( baseXp ) )
29+ // Calculate XP reward using real base_experience from PokeAPI
30+ const baseXp = getBaseExperience ( opponentSpeciesId )
31+ const xpGained = Math . max ( 1 , Math . floor ( baseXp * opponentLevel / 7 ) )
3232
33- // Calculate EV reward
33+ // Calculate EV reward using real EV yield from PokeAPI
3434 const evGained : Record < StatName , number > = { hp : 0 , attack : 0 , defense : 0 , spAtk : 0 , spDef : 0 , speed : 0 }
35- const evYield = getEvYield ( opponentSpeciesId )
35+ const evYield = getPokedexEvYield ( opponentSpeciesId )
3636 for ( const stat of STAT_NAMES ) {
3737 evGained [ stat ] = evYield [ TO_DEX_STAT [ stat ] ] ?? 0
3838 }
@@ -88,18 +88,33 @@ export async function settleBattle(
8888 }
8989 }
9090
91- // Detect evolution
91+ // Detect evolution — check ALL evolution targets (handles branch evolutions)
9292 if ( newLevel > oldLevel ) {
9393 const species = Dex . species . get ( creature . speciesId )
9494 if ( species ?. evos ?. length ) {
95- const targetId = species . evos [ 0 ] ! . toLowerCase ( )
96- const target = Dex . species . get ( targetId )
97- if ( target ?. evoLevel && newLevel >= target . evoLevel ) {
98- pendingEvolutions . push ( {
99- creatureId : creature . id ,
100- from : creature . speciesId ,
101- to : targetId as SpeciesId ,
102- } )
95+ for ( const evoId of species . evos ) {
96+ const targetId = evoId . toLowerCase ( )
97+ const target = Dex . species . get ( targetId )
98+ if ( ! target ?. exists ) continue
99+ const trigger = species . evoType
100+ // Level-up evolutions
101+ if ( ( ! trigger || trigger === 'levelUp' ) && target . evoLevel && newLevel >= target . evoLevel ) {
102+ pendingEvolutions . push ( {
103+ creatureId : creature . id ,
104+ from : creature . speciesId ,
105+ to : targetId as SpeciesId ,
106+ } )
107+ break // Only evolve into one target per level-up
108+ }
109+ // Friendship evolutions (friendship >= 220)
110+ if ( trigger === 'levelFriendship' && creature . friendship >= 220 ) {
111+ pendingEvolutions . push ( {
112+ creatureId : creature . id ,
113+ from : creature . speciesId ,
114+ to : targetId as SpeciesId ,
115+ } )
116+ break
117+ }
103118 }
104119 }
105120 }
@@ -172,20 +187,3 @@ export function applyEvolution(
172187 } ,
173188 }
174189}
175-
176- function getEvYield ( speciesId : string ) : Record < string , number > {
177- // @pkmn /sim Dex.species doesn't have evs field
178- // Use baseStats as proxy: highest base stat gets 1-2 EVs
179- const species = Dex . species . get ( speciesId )
180- if ( ! species ?. baseStats ) return { }
181- const stats = species . baseStats as Record < string , number >
182- const entries = Object . entries ( stats )
183- if ( entries . length === 0 ) return { }
184- // Sort by value descending, give 1-2 EV to top stats
185- entries . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] )
186- const result : Record < string , number > = { }
187- // Top stat gets 2 EVs, second gets 1
188- if ( entries [ 0 ] ) result [ entries [ 0 ] [ 0 ] ] = 2
189- if ( entries [ 1 ] ) result [ entries [ 1 ] [ 0 ] ] = 1
190- return result
191- }
0 commit comments