@@ -44,6 +44,8 @@ export default function GameCanvas({ gameState, frame }: Props) {
4444 const visibleEffects = ( effects ?? [ ] ) . filter ( fx => vis ( fx . x , fx . y ) ) ;
4545 const visibleDeathParticles = ( deathParticles ?? [ ] ) . filter ( dp => vis ( dp . x , dp . y ) ) ;
4646 const visibleDmgNums = ( dmgNums ?? [ ] ) . filter ( d => vis ( d . x , d . y ) ) ;
47+ const relicIds = new Set ( ( state . relics ?? [ ] ) . map ( relic => relic . id ) ) ;
48+ const effectivePickupRange = p . pickupRange * ( relicIds . has ( 'abyssalMagnet' ) ? 1.9 : 1 ) * ( 1 + p . luck * 0.01 ) ;
4749 const crowded = state . wave . number >= 5 || visibleEnemies . length + visibleProjectiles . length > 45 || visibleEffects . length + visibleDmgNums . length > 35 ;
4850 const displayProjectiles = crowded ? visibleProjectiles . slice ( 0 , 48 ) : visibleProjectiles ;
4951 const displayEffects = crowded ? visibleEffects . filter ( fx => fx . kind !== 'muzzle' && fx . kind !== 'spark' ) . slice ( - 14 ) : visibleEffects ;
@@ -105,7 +107,7 @@ export default function GameCanvas({ gameState, frame }: Props) {
105107 const dx = pk . x - p . x ;
106108 const dy = pk . y - p . y ;
107109 const dist = Math . sqrt ( dx * dx + dy * dy ) ;
108- const inMagnet = dist < p . pickupRange && dist > 12 ;
110+ const inMagnet = dist < effectivePickupRange && dist > 12 ;
109111 const trailColor = pk . type === 'egg' ? 'rgba(251,191,36,0.5)' : pk . type === 'heal' ? 'rgba(34,197,94,0.5)' : 'rgba(45,212,191,0.4)' ;
110112 return (
111113 < React . Fragment key = { pk . id } >
@@ -185,8 +187,9 @@ export default function GameCanvas({ gameState, frame }: Props) {
185187 } ) }
186188 { /* Enemies */ }
187189 { visibleEnemies . map ( e => {
188- const showTelegraph = e . telegraphTimer > 0 && e . telegraphType === 'attack' ;
190+ const showTelegraph = e . telegraphTimer > 0 && ( e . telegraphType === 'attack' || e . telegraphType === 'charge' ) ;
189191 const telegraphProg = showTelegraph ? 1 - ( e . telegraphTimer / e . telegraphMax ) : 0 ;
192+ const chargeTelegraph = e . telegraphType === 'charge' ;
190193 const isElite = e . elite !== 'none' ;
191194 const visualFontSize = e . fontSize + ( e . isBoss ? 0 : 2 ) ;
192195 return (
@@ -220,10 +223,22 @@ export default function GameCanvas({ gameState, frame }: Props) {
220223 } ] } />
221224 ) }
222225 { showTelegraph && (
223- < View style = { s . telegraphBarBg } >
224- < View style = { [ s . telegraphBar , { width : `${ Math . min ( 100 , Math . max ( 8 , telegraphProg * 100 ) ) } %` } ] } />
226+ < View style = { [ s . telegraphBarBg , chargeTelegraph && s . chargeTelegraphBarBg ] } >
227+ < View style = { [ s . telegraphBar , chargeTelegraph && s . chargeTelegraphBar , { width : `${ Math . min ( 100 , Math . max ( 8 , telegraphProg * 100 ) ) } %` } ] } />
225228 </ View >
226229 ) }
230+ { chargeTelegraph && (
231+ < View style = { [
232+ s . chargeWarn ,
233+ {
234+ width : e . radius * 4.8 ,
235+ left : e . radius - e . radius * 2.4 ,
236+ top : e . radius + 8 ,
237+ opacity : 0.3 + telegraphProg * 0.45 ,
238+ transform : [ { rotate : `${ Math . atan2 ( e . chargeVy , e . chargeVx ) } rad` } ] ,
239+ } ,
240+ ] } />
241+ ) }
227242 < Text style = { [ s . entity , s . enemyEmoji , e . flashTimer > 0 && s . enemyEmojiHit , { fontSize : visualFontSize , opacity : e . flashTimer > 0 ? 0.45 : 1 } ] } >
228243 { e . isBoss ? '👑' : '' } { e . emoji }
229244 </ Text >
@@ -469,6 +484,26 @@ export default function GameCanvas({ gameState, frame }: Props) {
469484 </ React . Fragment >
470485 ) ;
471486 }
487+ if ( fx . kind === 'smoke' ) {
488+ return (
489+ < View
490+ key = { fx . id }
491+ style = { [
492+ s . smokeFx ,
493+ {
494+ left : fx . x - fx . radius * ( 0.4 + prog * 0.3 ) ,
495+ top : fx . y - fx . radius * ( 0.4 + prog * 0.3 ) ,
496+ width : fx . radius * ( 0.8 + prog * 0.6 ) ,
497+ height : fx . radius * ( 0.45 + prog * 0.45 ) ,
498+ borderRadius : fx . radius ,
499+ backgroundColor : fx . color ,
500+ opacity : ( 1 - prog ) * 0.18 ,
501+ transform : [ { rotate : `${ fx . angle } rad` } , { scale : 0.9 + prog * 0.8 } ] ,
502+ } ,
503+ ] }
504+ />
505+ ) ;
506+ }
472507 if ( fx . kind === 'burst' ) {
473508 return (
474509 < React . Fragment key = { fx . id } >
@@ -767,6 +802,9 @@ const s = StyleSheet.create({
767802 eliteAura : { position : 'absolute' , borderWidth : 2 , backgroundColor : 'transparent' } ,
768803 telegraphBarBg : { position : 'absolute' , top : - 8 , width : 24 , height : 3 , borderRadius : 2 , backgroundColor : 'rgba(239,68,68,0.18)' , overflow : 'hidden' } ,
769804 telegraphBar : { height : 3 , borderRadius : 2 , backgroundColor : '#EF4444' } ,
805+ chargeTelegraphBarBg : { backgroundColor : 'rgba(56,189,248,0.18)' } ,
806+ chargeTelegraphBar : { backgroundColor : '#38BDF8' } ,
807+ chargeWarn : { position : 'absolute' , height : 4 , borderRadius : 3 , backgroundColor : '#38BDF8' , shadowColor : '#38BDF8' , shadowOpacity : 0.6 , shadowRadius : 8 , shadowOffset : { width : 0 , height : 0 } } ,
770808 hpBarBg : { width : 30 , height : 3 , backgroundColor : 'rgba(255,255,255,0.15)' , borderRadius : 2 , marginTop : 2 } ,
771809 hpBar : { height : 3 , backgroundColor : '#EF4444' , borderRadius : 2 } ,
772810 playerWrap : { position : 'absolute' , alignItems : 'center' , width : 48 } ,
@@ -801,6 +839,7 @@ const s = StyleSheet.create({
801839 shieldBar : { height : 2 , backgroundColor : '#2DD4BF' , borderRadius : 1 } ,
802840 sparkFx : { position : 'absolute' } ,
803841 sparkRay : { position : 'absolute' , height : 2 , borderRadius : 1 } ,
842+ smokeFx : { position : 'absolute' } ,
804843 burstFx : { position : 'absolute' , borderWidth : 3 , backgroundColor : 'transparent' } ,
805844 burstInner : { position : 'absolute' } ,
806845 deathParticle : { position : 'absolute' , textAlign : 'center' , fontWeight : '800' } ,
0 commit comments