@ -1,22 +1,54 @@
import { Math _max } from "../../../core/builtins" ;
import { ClickDetector } from "../../../core/click_detector" ;
import { formatBigNumber , makeDiv } from "../../../core/utils" ;
import { formatBigNumber , makeDiv , arrayDelete , arrayDeleteValue } from "../../../core/utils" ;
import { ShapeDefinition } from "../../shape_definition" ;
import { BaseHUDPart } from "../base_hud_part" ;
import { blueprintShape } from "../../upgrades" ;
import { blueprintShape , UPGRADES } from "../../upgrades" ;
import { enumHubGoalRewards } from "../../tutorial_goals" ;
/ * *
* Manages the pinned shapes on the left side of the screen
* /
export class HUDPinnedShapes extends BaseHUDPart {
constructor ( root ) {
super ( root ) ;
/ * *
* Store a list of pinned shapes
* @ type { Array < string > }
* /
this . pinnedShapes = [ ] ;
/ * *
* Store handles to the currently rendered elements , so we can update them more
* convenient . Also allows for cleaning up handles .
* @ type { Array < {
* key : string ,
* amountLabel : HTMLElement ,
* lastRenderedValue : string ,
* element : HTMLElement ,
* detector ? : ClickDetector
* } > }
* /
this . handles = [ ] ;
}
createElements ( parent ) {
this . element = makeDiv ( parent , "ingame_HUD_PinnedShapes" , [ ] ) ;
}
/ * *
* Serializes the pinned shapes
* /
serialize ( ) {
return {
shapes : this . pinnedShapes ,
} ;
}
/ * *
* Deserializes the pinned shapes
* @ param { { shapes : Array < string > } } data
* /
deserialize ( data ) {
if ( ! data || ! data . shapes || ! Array . isArray ( data . shapes ) ) {
return "Invalid pinned shapes data" ;
@ -24,48 +56,99 @@ export class HUDPinnedShapes extends BaseHUDPart {
this . pinnedShapes = data . shapes ;
}
/ * *
* Initializes the hud component
* /
initialize ( ) {
/** @type {Array<{ key: string, goal: number }>} */
this . pinnedShapes = [ ] ;
/** @type {Array<{key: string, amountLabel: HTMLElement, lastRenderedValue: number, element: HTMLElement, detector?: ClickDetector}>} */
this . handles = [ ] ;
this . rerenderFull ( ) ;
// Connect to any relevant signals
this . root . signals . storyGoalCompleted . add ( this . rerenderFull , this ) ;
this . root . signals . upgradePurchased . add ( this . updateShapesAfterUpgrade , this ) ;
this . root . signals . postLoadHook . add ( this . rerenderFull , this ) ;
this . root . hud . signals . shapePinRequested . add ( this . pinNewShape , this ) ;
this . root . hud . signals . shapeUnpinRequested . add ( this . unpinShape , this ) ;
// Perform initial render
this . updateShapesAfterUpgrade ( ) ;
}
/ * *
* Returns whether a given shape is pinned
* @ param { string } key
* Updates all shapes after an upgrade has been purchased and removes the unused ones
* /
isShapePinned ( key ) {
if ( ! this . pinnedShapes ) {
return false ;
updateShapesAfterUpgrade ( ) {
for ( let i = 0 ; i < this . pinnedShapes . length ; ++ i ) {
const key = this . pinnedShapes [ i ] ;
if ( key === blueprintShape ) {
// Ignore blueprint shapes
continue ;
}
let goal = this . findGoalValueForShape ( key ) ;
if ( ! goal ) {
// Seems no longer relevant
this . pinnedShapes . splice ( i , 1 ) ;
i -= 1 ;
}
}
this . rerenderFull ( ) ;
}
/ * *
* Finds the current goal for the given key . If the key is the story goal , returns
* the story goal . If its the blueprint shape , no goal is returned . Otherwise
* it ' s searched for upgrades .
* @ param { string } key
* /
findGoalValueForShape ( key ) {
if ( key === this . root . hubGoals . currentGoal . definition . getHash ( ) ) {
return true ;
return this . root . hubGoals . currentGoal . required ;
}
if ( key === blueprintShape ) {
return true ;
return null ;
}
for ( let i = 0 ; i < this . pinnedShapes . length ; ++ i ) {
if ( this . pinnedShapes [ i ] . key === key ) {
return true ;
// Check if this shape is required for any upgrade
for ( const upgradeId in UPGRADES ) {
const { tiers } = UPGRADES [ upgradeId ] ;
const currentTier = this . root . hubGoals . getUpgradeLevel ( upgradeId ) ;
const tierHandle = tiers [ currentTier ] ;
if ( ! tierHandle ) {
// Max level
continue ;
}
for ( let i = 0 ; i < tierHandle . required . length ; ++ i ) {
const { shape , amount } = tierHandle . required [ i ] ;
if ( shape === key ) {
return amount ;
}
}
}
return false ;
return null ;
}
/ * *
* Returns whether a given shape is currently pinned
* @ param { string } key
* /
isShapePinned ( key ) {
if ( key === this . root . hubGoals . currentGoal . definition . getHash ( ) || key === blueprintShape ) {
// This is a "special" shape which is always pinned
return true ;
}
return this . pinnedShapes . indexOf ( key ) >= 0 ;
}
/ * *
* Rerenders the whole component
* /
rerenderFull ( ) {
const currentGoal = this . root . hubGoals . currentGoal ;
const currentKey = currentGoal . definition . getHash ( ) ;
// First, remove old ones
// First, remove all old shap es
for ( let i = 0 ; i < this . handles . length ; ++ i ) {
this . handles [ i ] . element . remove ( ) ;
const detector = this . handles [ i ] . detector ;
@ -75,28 +158,30 @@ export class HUDPinnedShapes extends BaseHUDPart {
}
this . handles = [ ] ;
this . internalPinShape ( currentKey , currentGoal . required , false , "goal" ) ;
// Pin story goal
this . internalPinShape ( currentKey , false , "goal" ) ;
// Pin blueprint shape as well
if ( this . root . hubGoals . isRewardUnlocked ( enumHubGoalRewards . reward _blueprints ) ) {
this . internalPinShape ( blueprintShape , null, false, "blueprint" ) ;
this . internalPinShape ( blueprintShape , false, "blueprint" ) ;
}
// Pin manually pinned shapes
for ( let i = 0 ; i < this . pinnedShapes . length ; ++ i ) {
const key = this . pinnedShapes [ i ] .key ;
const key = this . pinnedShapes [ i ] ;
if ( key !== currentKey ) {
this . internalPinShape ( key , this . pinnedShapes [ i ] . goal );
this . internalPinShape ( key );
}
}
}
/ * *
* Pins a shape
* Pins a new shape
* @ param { string } key
* @ param { number } goal
* @ param { boolean } canUnpin
* @ param { string = } className
* /
internalPinShape ( key , goal, canUnpin = true , className = null ) {
internalPinShape ( key , canUnpin = true , className = null ) {
const definition = this . root . shapeDefinitionMgr . getShapeFromShortKey ( key ) ;
const element = makeDiv ( this . element , null , [ "shape" ] ) ;
@ -121,6 +206,7 @@ export class HUDPinnedShapes extends BaseHUDPart {
const amountLabel = makeDiv ( element , null , [ "amountLabel" ] , "" ) ;
const goal = this . findGoalValueForShape ( key ) ;
if ( goal ) {
makeDiv ( element , null , [ "goalLabel" ] , "/" + formatBigNumber ( goal ) ) ;
}
@ -129,18 +215,24 @@ export class HUDPinnedShapes extends BaseHUDPart {
key ,
element ,
amountLabel ,
lastRenderedValue : - 1 ,
lastRenderedValue : "" ,
} ) ;
}
/ * *
* Updates all amount labels
* /
update ( ) {
for ( let i = 0 ; i < this . handles . length ; ++ i ) {
const handle = this . handles [ i ] ;
const currentValue = this . root . hubGoals . getShapesStoredByKey ( handle . key ) ;
if ( currentValue !== handle . lastRenderedValue ) {
handle . lastRenderedValue = currentValue ;
handle . amountLabel . innerText = formatBigNumber ( currentValue ) ;
const currentValueFormatted = formatBigNumber ( currentValue ) ;
if ( currentValueFormatted !== handle . lastRenderedValue ) {
handle . lastRenderedValue = currentValueFormatted ;
handle . amountLabel . innerText = currentValueFormatted ;
const goal = this . findGoalValueForShape ( handle . key ) ;
handle . element . classList . toggle ( "completed" , goal && currentValue > goal ) ;
}
}
}
@ -150,20 +242,15 @@ export class HUDPinnedShapes extends BaseHUDPart {
* @ param { string } key
* /
unpinShape ( key ) {
for ( let i = 0 ; i < this . pinnedShapes . length ; ++ i ) {
if ( this . pinnedShapes [ i ] . key === key ) {
this . pinnedShapes . splice ( i , 1 ) ;
this . rerenderFull ( ) ;
return ;
}
}
arrayDeleteValue ( this . pinnedShapes , key ) ;
this . rerenderFull ( ) ;
}
/ * *
* Requests to pin a new shape
* @ param { ShapeDefinition } definition
* @ param { number } goal
* /
pinNewShape ( definition , goal ) {
pinNewShape ( definition ) {
const key = definition . getHash ( ) ;
if ( key === this . root . hubGoals . currentGoal . definition . getHash ( ) ) {
// Can not pin current goal
@ -171,18 +258,16 @@ export class HUDPinnedShapes extends BaseHUDPart {
}
if ( key === blueprintShape ) {
// Can not pin the blueprint shape
return ;
}
for ( let i = 0 ; i < this . pinnedShapes . length ; ++ i ) {
if ( this . pinnedShapes [ i ] . key === key ) {
// Already pinned
this . pinnedShapes [ i ] . goal = Math _max ( this . pinnedShapes [ i ] . goal , goal ) ;
return ;
}
// Check if its already pinned
if ( this . pinnedShapes . indexOf ( key ) >= 0 ) {
return ;
}
this . pinnedShapes . push ( { key , goal } ) ;
this . pinnedShapes . push ( key ) ;
this . rerenderFull ( ) ;
}
}