2020-10-03 21:52:51 +00:00
// ************ Number formatting ************
function exponentialFormat ( num , precision ) {
let e = num . log10 ( ) . floor ( )
let m = num . div ( Decimal . pow ( 10 , e ) )
return m . toStringWithDecimalPlaces ( 3 ) + "e" + e . toStringWithDecimalPlaces ( 0 )
}
function commaFormat ( num , precision ) {
if ( num === null || num === undefined ) return "NaN"
if ( num . mag < 0.001 ) return ( 0 ) . toFixed ( precision )
return num . toStringWithDecimalPlaces ( precision ) . replace ( /\B(?=(\d{3})+(?!\d))/g , "," )
}
function fixValue ( x , y = 0 ) {
return x || new Decimal ( y )
}
function sumValues ( x ) {
x = Object . values ( x )
if ( ! x [ 0 ] ) return new Decimal ( 0 )
return x . reduce ( ( a , b ) => Decimal . add ( a , b ) )
}
function format ( decimal , precision = 2 ) {
decimal = new Decimal ( decimal )
if ( isNaN ( decimal . sign ) || isNaN ( decimal . layer ) || isNaN ( decimal . mag ) ) {
player . hasNaN = true ;
return "NaN"
}
if ( decimal . sign < 0 ) return "-" + format ( decimal . neg ( ) , precision )
if ( decimal . mag == Number . POSITIVE _INFINITY ) return "Infinity"
if ( decimal . gte ( "eeee1000" ) ) {
var slog = decimal . slog ( )
if ( slog . gte ( 1e6 ) ) return "F" + format ( slog . floor ( ) )
else return Decimal . pow ( 10 , slog . sub ( slog . floor ( ) ) ) . toStringWithDecimalPlaces ( 3 ) + "F" + commaFormat ( slog . floor ( ) , 0 )
} else if ( decimal . gte ( "1e1000" ) ) return ( Math . floor ( decimal . mantissa + 0.01 ) + ( "e" + formatWhole ( decimal . log10 ( ) ) ) )
else if ( decimal . gte ( 1e9 ) ) return exponentialFormat ( decimal , precision )
else if ( decimal . gte ( 1e3 ) ) return commaFormat ( decimal , 0 )
else return commaFormat ( decimal , precision )
}
function formatWhole ( decimal ) {
return format ( decimal , 0 )
}
function formatTime ( s ) {
if ( s < 60 ) return format ( s ) + "s"
else if ( s < 3600 ) return formatWhole ( Math . floor ( s / 60 ) ) + "m " + format ( s % 60 ) + "s"
else return formatWhole ( Math . floor ( s / 3600 ) ) + "h " + formatWhole ( Math . floor ( s / 60 ) % 60 ) + "m " + format ( s % 60 ) + "s"
}
2020-10-05 01:36:03 +00:00
function toPlaces ( x , precision , maxAccepted ) {
x = new Decimal ( x )
let result = x . toStringWithDecimalPlaces ( precision )
if ( new Decimal ( result ) . gte ( maxAccepted ) ) {
result = new Decimal ( maxAccepted - Math . pow ( 0.1 , precision ) ) . toStringWithDecimalPlaces ( precision )
}
return result
}
2020-10-03 21:52:51 +00:00
// ************ Save stuff ************
function save ( ) {
localStorage . setItem ( modInfo . id , btoa ( JSON . stringify ( player ) ) )
}
2020-10-04 17:10:04 +00:00
function startPlayerBase ( ) {
return {
tab : "tree" ,
time : Date . now ( ) ,
autosave : true ,
notify : { } ,
msDisplay : "always" ,
offlineProd : true ,
2020-10-07 15:03:32 +00:00
versionType : modInfo . id ,
2020-10-04 17:10:04 +00:00
version : VERSION . num ,
beta : VERSION . beta ,
timePlayed : 0 ,
keepGoing : false ,
hasNaN : false ,
2020-10-05 01:36:03 +00:00
hideChalls : false ,
2020-10-04 17:10:04 +00:00
points : new Decimal ( 10 ) ,
2020-10-07 04:57:41 +00:00
subtabs : { } ,
2020-10-04 17:10:04 +00:00
}
}
function getStartPlayer ( ) {
playerdata = startPlayerBase ( )
for ( layer in layers ) {
playerdata [ layer ] = layers [ layer ] . startData ( )
playerdata [ layer ] . buyables = getStartBuyables ( layer )
2020-10-11 20:16:36 +00:00
if ( playerdata [ layer ] . clickables == undefined ) playerdata [ layer ] . clickables = getStartClickables ( layer )
2020-10-04 17:10:04 +00:00
playerdata [ layer ] . spentOnBuyables = new Decimal ( 0 )
playerdata [ layer ] . upgrades = [ ]
playerdata [ layer ] . milestones = [ ]
2020-10-11 20:42:32 +00:00
playerdata [ layer ] . challs = getStartChalls ( layer )
2020-10-07 03:11:36 +00:00
if ( layers [ layer ] . tabFormat && ! Array . isArray ( layers [ layer ] . tabFormat ) ) {
2020-10-07 04:57:41 +00:00
playerdata . subtabs [ layer ] = { }
playerdata . subtabs [ layer ] . mainTabs = Object . keys ( layers [ layer ] . tabFormat ) [ 0 ]
2020-10-07 03:11:36 +00:00
}
if ( layers [ layer ] . microtabs ) {
2020-10-07 04:57:41 +00:00
if ( playerdata . subtabs [ layer ] == undefined ) playerdata . subtabs [ layer ] = { }
2020-10-07 03:11:36 +00:00
for ( item in layers [ layer ] . microtabs )
2020-10-07 04:57:41 +00:00
playerdata . subtabs [ layer ] [ item ] = Object . keys ( layers [ layer ] . microtabs [ item ] ) [ 0 ]
2020-10-07 03:11:36 +00:00
}
2020-10-04 17:10:04 +00:00
}
return playerdata
}
2020-10-11 20:42:32 +00:00
function getStartBuyables ( layer ) {
let data = { }
if ( layers [ layer ] . buyables ) {
for ( id in layers [ layer ] . buyables )
if ( ! isNaN ( id ) )
data [ id ] = new Decimal ( 0 )
}
return data
}
function getStartClickables ( layer ) {
let data = { }
if ( layers [ layer ] . clickables ) {
for ( id in layers [ layer ] . clickables )
if ( ! isNaN ( id ) )
data [ id ] = ""
}
return data
}
function getStartChalls ( layer ) {
let data = { }
if ( layers [ layer ] . challs ) {
for ( id in layers [ layer ] . challs )
if ( ! isNaN ( id ) )
data [ id ] = 0
}
return data
}
2020-10-03 21:52:51 +00:00
function fixSave ( ) {
2020-10-08 23:42:26 +00:00
defaultData = getStartPlayer ( )
fixData ( defaultData , player )
2020-10-03 21:52:51 +00:00
2020-10-08 23:42:26 +00:00
for ( layer in layers )
{
if ( player [ layer ] . best !== undefined ) player [ layer ] . best = new Decimal ( player [ layer ] . best )
if ( player [ layer ] . total !== undefined ) player [ layer ] . total = new Decimal ( player [ layer ] . total )
}
}
2020-10-03 21:52:51 +00:00
2020-10-08 23:42:26 +00:00
function fixData ( defaultData , newData ) {
for ( item in defaultData ) {
if ( defaultData [ item ] == null ) {
if ( newData [ item ] === undefined )
newData [ item ] = null
2020-10-03 21:52:51 +00:00
}
2020-10-08 23:42:26 +00:00
else if ( Array . isArray ( defaultData [ item ] ) ) {
if ( newData [ item ] === undefined )
newData [ item ] = defaultData [ item ]
else
fixData ( defaultData [ item ] , newData [ item ] )
2020-10-07 03:11:36 +00:00
}
2020-10-08 23:42:26 +00:00
else if ( defaultData [ item ] instanceof Decimal ) { // Convert to Decimal
if ( newData [ item ] === undefined )
newData [ item ] = defaultData [ item ]
else
newData [ item ] = new Decimal ( newData [ item ] )
2020-10-07 03:11:36 +00:00
}
2020-10-08 23:42:26 +00:00
else if ( ( ! ! defaultData [ item ] ) && ( defaultData [ item ] . constructor === Object ) ) {
2020-10-11 20:42:32 +00:00
if ( newData [ item ] === undefined || ( defaultData [ item ] . constructor !== Object ) )
2020-10-08 23:42:26 +00:00
newData [ item ] = defaultData [ item ]
else
fixData ( defaultData [ item ] , newData [ item ] )
}
else {
if ( newData [ item ] === undefined )
newData [ item ] = defaultData [ item ]
}
}
2020-10-03 21:52:51 +00:00
}
function load ( ) {
let get = localStorage . getItem ( modInfo . id ) ;
if ( get === null || get === undefined ) player = getStartPlayer ( )
else player = Object . assign ( getStartPlayer ( ) , JSON . parse ( atob ( get ) ) )
fixSave ( )
player . tab = "tree"
if ( player . offlineProd ) {
if ( player . offTime === undefined ) player . offTime = { remain : 0 }
player . offTime . remain += ( Date . now ( ) - player . time ) / 1000
}
player . time = Date . now ( ) ;
versionCheck ( ) ;
changeTheme ( ) ;
changeTreeQuality ( ) ;
2020-10-07 22:41:03 +00:00
setupTemp ( ) ;
2020-10-03 21:52:51 +00:00
updateTemp ( ) ;
updateTemp ( ) ;
loadVue ( ) ;
}
function exportSave ( ) {
let str = btoa ( JSON . stringify ( player ) )
const el = document . createElement ( "textarea" ) ;
el . value = str ;
document . body . appendChild ( el ) ;
el . select ( ) ;
el . setSelectionRange ( 0 , 99999 ) ;
document . execCommand ( "copy" ) ;
document . body . removeChild ( el ) ;
}
function importSave ( imported = undefined , forced = false ) {
if ( imported === undefined ) imported = prompt ( "Paste your save here" )
try {
tempPlr = Object . assign ( getStartPlayer ( ) , JSON . parse ( atob ( imported ) ) )
2020-10-07 15:03:32 +00:00
if ( tempPlr . versionType != modInfo . id && ! forced && ! confirm ( "This save appears to be for a different mod! Are you sure you want to import?" ) ) // Wrong save (use "Forced" to force it to accept.)
2020-10-03 21:52:51 +00:00
return
player = tempPlr ;
player . versionType = modInfo . id
fixSave ( )
save ( )
window . location . reload ( )
} catch ( e ) {
return ;
}
}
function versionCheck ( ) {
let setVersion = true
if ( player . versionType === undefined || player . version === undefined ) {
2020-10-04 17:10:04 +00:00
player . versionType = modInfo . id
2020-10-03 21:52:51 +00:00
player . version = 0
}
if ( setVersion ) {
2020-10-04 17:10:04 +00:00
if ( player . versionType == modInfo . id && VERSION . num > player . version ) player . keepGoing = false
2020-10-03 21:52:51 +00:00
player . versionType = getStartPlayer ( ) . versionType
player . version = VERSION . num
player . beta = VERSION . beta
}
}
var saveInterval = setInterval ( function ( ) {
if ( player === undefined ) return ;
if ( gameEnded && ! player . keepGoing ) return ;
if ( player . autosave ) save ( ) ;
} , 5000 )
// ************ Themes ************
const themes = {
1 : "aqua"
}
const theme _names = {
aqua : "Aqua"
}
function changeTheme ( ) {
let aqua = player . theme == "aqua"
colors _theme = colors [ player . theme || "default" ]
document . body . style . setProperty ( '--background' , aqua ? "#001f3f" : "#0f0f0f" )
document . body . style . setProperty ( '--background_tooltip' , aqua ? "rgba(0, 15, 31, 0.75)" : "rgba(0, 0, 0, 0.75)" )
document . body . style . setProperty ( '--color' , aqua ? "#bfdfff" : "#dfdfdf" )
document . body . style . setProperty ( '--points' , aqua ? "#dfefff" : "#ffffff" )
2020-10-05 01:36:03 +00:00
document . body . style . setProperty ( "--locked" , aqua ? "#c4a7b3" : "#bf8f8f" )
2020-10-03 21:52:51 +00:00
}
function getThemeName ( ) {
return player . theme ? theme _names [ player . theme ] : "Default"
}
function switchTheme ( ) {
if ( player . theme === undefined ) player . theme = themes [ 1 ]
else {
player . theme = themes [ Object . keys ( themes ) [ player . theme ] + 1 ]
if ( ! player . theme ) delete player . theme
}
changeTheme ( )
resizeCanvas ( )
}
// ************ Options ************
function toggleOpt ( name ) {
2020-10-05 01:36:03 +00:00
if ( name == "oldStyle" && styleCooldown > 0 ) return ;
2020-10-03 21:52:51 +00:00
player [ name ] = ! player [ name ]
if ( name == "hqTree" ) changeTreeQuality ( )
2020-10-05 01:36:03 +00:00
if ( name == "oldStyle" ) updateStyle ( )
}
var styleCooldown = 0 ;
function updateStyle ( ) {
styleCooldown = 1 ;
let css = document . getElementById ( "styleStuff" )
css . href = player . oldStyle ? "oldStyle.css" : "style.css"
needCanvasUpdate = true ;
2020-10-03 21:52:51 +00:00
}
function changeTreeQuality ( ) {
var on = player . hqTree
document . body . style . setProperty ( '--hqProperty1' , on ? "2px solid" : "4px solid" )
document . body . style . setProperty ( '--hqProperty2a' , on ? "-4px -4px 4px rgba(0, 0, 0, 0.25) inset" : "-4px -4px 4px rgba(0, 0, 0, 0) inset" )
document . body . style . setProperty ( '--hqProperty2b' , on ? "0px 0px 20px var(--background)" : "" )
document . body . style . setProperty ( '--hqProperty3' , on ? "2px 2px 4px rgba(0, 0, 0, 0.25)" : "none" )
}
function toggleAuto ( toggle ) {
player [ toggle [ 0 ] ] [ toggle [ 1 ] ] = ! player [ toggle [ 0 ] ] [ toggle [ 1 ] ]
}
function adjustMSDisp ( ) {
let displays = [ "always" , "automation" , "incomplete" , "never" ] ;
player . msDisplay = displays [ ( displays . indexOf ( player . msDisplay ) + 1 ) % 4 ]
}
function milestoneShown ( layer , id ) {
complete = player [ layer ] . milestones . includes ( id )
auto = layers [ layer ] . milestones [ id ] . toggles
switch ( player . msDisplay ) {
case "always" :
return true ;
break ;
case "automation" :
return ( auto ) || ! complete
break ;
case "incomplete" :
return ! complete
break ;
case "never" :
return false ;
break ;
}
return false ;
}
2020-10-11 20:16:36 +00:00
// ************ Big Feature related ************
function respecBuyables ( layer ) {
if ( ! layers [ layer ] . buyables ) return
if ( ! layers [ layer ] . buyables . respec ) return
if ( ! confirm ( "Are you sure you want to respec? This will force you to do a \"" + ( layers [ layer ] . name ? layers [ layer ] . name : layer ) + "\" reset as well!" ) ) return
layers [ layer ] . buyables . respec ( )
updateBuyableTemp ( layer )
}
function canAffordUpg ( layer , id ) {
let upg = layers [ layer ] . upgrades [ id ]
let cost = tmp [ layer ] . upgrades [ id ] . cost
return canAffordPurchase ( layer , upg , cost )
}
function hasUpg ( layer , id ) {
return ( player [ layer ] . upgrades . includes ( toNumber ( id ) ) || player [ layer ] . upgrades . includes ( id . toString ( ) ) )
}
function hasMilestone ( layer , id ) {
return ( player [ layer ] . milestones . includes ( toNumber ( id ) ) || player [ layer ] . milestones . includes ( id . toString ( ) ) )
}
function hasChall ( layer , id ) {
2020-10-11 20:42:32 +00:00
return ( player [ layer ] . challs [ id ] )
}
function challCompletions ( layer , id ) {
return ( player [ layer ] . challs [ id ] )
2020-10-11 20:16:36 +00:00
}
function getBuyableAmt ( layer , id ) {
return ( player [ layer ] . buyables [ id ] )
}
function setBuyableAmt ( layer , id , amt ) {
player [ layer ] . buyables [ id ] = amt
}
function getClickableState ( layer , id ) {
return ( player [ layer ] . clickables [ id ] )
}
function setClickableState ( layer , id , state ) {
player [ layer ] . clickables [ id ] = state
}
function upgEffect ( layer , id ) {
return ( tmp [ layer ] . upgrades [ id ] . effect )
}
function challEffect ( layer , id ) {
return ( tmp [ layer ] . challs [ id ] . effect )
}
function buyableEffect ( layer , id ) {
return ( tmp [ layer ] . buyables [ id ] . effect )
}
function canAffordPurchase ( layer , thing , cost ) {
if ( thing . currencyInternalName ) {
let name = thing . currencyInternalName
if ( thing . currencyLayer ) {
let lr = thing . currencyLayer
return ! ( player [ lr ] [ name ] . lt ( cost ) )
}
else {
return ! ( player [ name ] . lt ( cost ) )
}
}
else {
return ! ( player [ layer ] . points . lt ( cost ) )
}
}
function buyUpg ( layer , id ) {
if ( ! player [ layer ] . unl ) return
2020-10-12 22:28:12 +00:00
if ( ! layers [ layer ] . upgrades [ id ] . unl ) return
2020-10-11 20:16:36 +00:00
if ( player [ layer ] . upgrades . includes ( id ) ) return
let upg = layers [ layer ] . upgrades [ id ]
let cost = tmp [ layer ] . upgrades [ id ] . cost
if ( upg . currencyInternalName ) {
let name = upg . currencyInternalName
if ( upg . currencyLayer ) {
let lr = upg . currencyLayer
if ( player [ lr ] [ name ] . lt ( cost ) ) return
player [ lr ] [ name ] = player [ lr ] [ name ] . sub ( cost )
}
else {
if ( player [ name ] . lt ( cost ) ) return
player [ name ] = player [ name ] . sub ( cost )
}
}
else {
if ( player [ layer ] . points . lt ( cost ) ) return
player [ layer ] . points = player [ layer ] . points . sub ( cost )
}
player [ layer ] . upgrades . push ( id ) ;
if ( upg . onPurchase != undefined )
upg . onPurchase ( )
}
function buyMaxBuyable ( layer , id ) {
if ( ! player [ layer ] . unl ) return
if ( ! tmp [ layer ] . buyables [ id ] . unl ) return
if ( ! tmp [ layer ] . buyables [ id ] . canAfford ) return
if ( ! layers [ layer ] . buyables [ id ] . buyMax ) return
layers [ layer ] . buyables [ id ] . buyMax ( )
updateBuyableTemp ( layer )
}
function buyBuyable ( layer , id ) {
if ( ! player [ layer ] . unl ) return
if ( ! tmp [ layer ] . buyables [ id ] . unl ) return
if ( ! tmp [ layer ] . buyables [ id ] . canAfford ) return
layers [ layer ] . buyables [ id ] . buy ( )
updateBuyableTemp ( layer )
}
function clickClickable ( layer , id ) {
if ( ! player [ layer ] . unl ) return
if ( ! tmp [ layer ] . clickables [ id ] . unl ) return
if ( ! tmp [ layer ] . clickables [ id ] . canClick ) return
layers [ layer ] . clickables [ id ] . onClick ( )
updateClickableTemp ( layer )
}
2020-10-11 22:38:54 +00:00
// Function to determine if the player is in a challenge
function inChallenge ( layer , id ) {
let chall = player [ layer ] . active
if ( chall == toNumber ( id ) ) return true
if ( layers [ layer ] . challs [ chall ] . countsAs )
return layers [ layer ] . challs [ id ] . countsAs . includes ( id )
}
2020-10-03 21:52:51 +00:00
// ************ Misc ************
var onTreeTab = true
function showTab ( name ) {
if ( LAYERS . includes ( name ) && ! layerUnl ( name ) ) return
var toTreeTab = name == "tree"
player . tab = name
if ( toTreeTab != onTreeTab ) {
document . getElementById ( "treeTab" ) . className = toTreeTab ? "fullWidth" : "col left"
onTreeTab = toTreeTab
resizeCanvas ( )
}
delete player . notify [ name ]
}
function notifyLayer ( name ) {
if ( player . tab == name || ! layerUnl ( name ) ) return
player . notify [ name ] = 1
}
function nodeShown ( layer ) {
2020-10-10 03:16:29 +00:00
if ( tmp [ layer ] . layerShown ) return true
2020-10-03 21:52:51 +00:00
switch ( layer ) {
case "idk" :
return player . l . unl
break ;
}
return false
}
function layerUnl ( layer ) {
2020-10-10 03:16:29 +00:00
return LAYERS . includes ( layer ) && ( player [ layer ] . unl || ( tmp [ layer ] . baseAmount . gte ( tmp [ layer ] . requires ) && layers [ layer ] . layerShown ( ) ) )
2020-10-03 21:52:51 +00:00
}
function keepGoing ( ) {
player . keepGoing = true ;
showTab ( "tree" )
}
function toNumber ( x ) {
if ( x . mag !== undefined ) return x . toNumber ( )
if ( x + 0 !== x ) return parseFloat ( x )
return x
}
function updateMilestones ( layer ) {
for ( id in layers [ layer ] . milestones ) {
if ( ! ( player [ layer ] . milestones . includes ( id ) ) && layers [ layer ] . milestones [ id ] . done ( ) )
player [ layer ] . milestones . push ( id )
}
}
function addTime ( diff , layer ) {
let data = player
let time = data . timePlayed
if ( layer ) {
data = data [ layer ]
time = data . time
}
//I am not that good to perfectly fix that leak. ~ DB Aarex
if ( time + 0 !== time ) {
console . log ( "Memory leak detected. Trying to fix..." )
time = toNumber ( time )
if ( isNaN ( time ) || time == 0 ) {
console . log ( "Couldn't fix! Resetting..." )
time = layer ? player . timePlayed : 0
if ( ! layer ) player . timePlayedReset = true
}
}
time += toNumber ( diff )
if ( layer ) data . time = time
else data . timePlayed = time
}
document . onkeydown = function ( e ) {
if ( player === undefined ) return ;
if ( gameEnded && ! player . keepGoing ) return ;
let shiftDown = e . shiftKey
let ctrlDown = e . ctrlKey
let key = e . key
if ( ctrlDown ) key = "ctrl+" + key
if ( onFocused ) return
if ( ctrlDown && key != "-" && key != "_" && key != "+" && key != "=" && key != "r" && key != "R" && key != "F5" ) e . preventDefault ( )
if ( hotkeys [ key ] ) {
if ( player [ hotkeys [ key ] . layer ] . unl )
hotkeys [ key ] . onPress ( )
}
}
var onFocused = false
function focused ( x ) {
onFocused = x
2020-10-09 03:13:15 +00:00
}
2020-10-10 03:16:29 +00:00
function prestigeButtonText ( layer )
{
if ( tmp [ layer ] . type == "normal" )
return ` ${ player [ layer ] . points . lt ( 1e3 ) ? ( tmp [ layer ] . resetDesc !== undefined ? tmp [ layer ] . resetDesc : "Reset for " ) : "" } +<b> ${ formatWhole ( tmp [ layer ] . resetGain ) } </b> ${ tmp [ layer ] . resource } ${ tmp [ layer ] . resetGain . lt ( 100 ) && player [ layer ] . points . lt ( 1e3 ) ? ` <br><br>Next at ${ ( tmp [ layer ] . resCeil ? formatWhole ( tmp [ layer ] . nextAt ) : format ( tmp [ layer ] . nextAt ) ) } ` : "" } ${ tmp [ layer ] . baseResource } `
else if ( tmp [ layer ] . type == "static" )
return ` ${ tmp [ layer ] . resetDesc !== undefined ? tmp [ layer ] . resetDesc : "Reset for " } +<b> ${ formatWhole ( tmp [ layer ] . resetGain ) } </b> ${ tmp [ layer ] . resource } <br><br> ${ player [ layer ] . points . lt ( 20 ) ? ( tmp [ layer ] . baseAmount . gte ( tmp [ layer ] . nextAt ) && ( tmp [ layer ] . canBuyMax !== undefined ) && tmp [ layer ] . canBuyMax ? "Next" : "Req" ) : "" } : ${ formatWhole ( tmp [ layer ] . baseAmount ) } / ${ ( tmp [ layer ] . resCeil ? formatWhole ( tmp [ layer ] . nextAtDisp ) : format ( tmp [ layer ] . nextAtDisp ) ) } ${ tmp [ layer ] . baseResource }
`
else
return layers [ layer ] . prestigeButtonText ( )
}
2020-10-09 03:13:15 +00:00
function isFunction ( obj ) {
return ! ! ( obj && obj . constructor && obj . call && obj . apply ) ;
} ;
2020-10-09 23:42:30 +00:00
document . title = modInfo . name