2019-04-11 10:37:40 +02:00
import { Augmentations } from "./Augmentation/Augmentations" ;
import { AugmentationNames } from "./Augmentation/data/AugmentationNames" ;
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers" ;
import { CONSTANTS } from "./Constants" ;
import { Engine } from "./engine" ;
import { Faction } from "./Faction/Faction" ;
import { Factions , factionExists } from "./Faction/Factions" ;
import { joinFaction , displayFactionContent } from "./Faction/FactionHelpers" ;
import { Player } from "./Player" ;
import { hackWorldDaemon , redPillFlag } from "./RedPill" ;
2021-04-06 09:50:09 +02:00
import { calculateHospitalizationCost } from "./Hospital/Hospital" ;
2018-12-07 11:54:26 +01:00
2019-04-11 10:37:40 +02:00
import { Page , routing } from "./ui/navigationTracking" ;
import { numeralWrapper } from "./ui/numeralFormat" ;
import { dialogBoxCreate } from "../utils/DialogBox" ;
import {
Reviver ,
Generic _toJSON ,
Generic _fromJSON
} from "../utils/JSONReviver" ;
import { setTimeoutRef } from "./utils/SetTimeoutRef" ;
import { formatNumber } from "../utils/StringHelperFunctions" ;
2021-03-13 21:10:55 +01:00
import { ConsoleHelpText } from "./Bladeburner/data/Help" ;
import { City } from "./Bladeburner/City" ;
import { BladeburnerConstants } from "./Bladeburner/data/Constants" ;
import { Skill } from "./Bladeburner/Skill" ;
import { Skills } from "./Bladeburner/Skills" ;
import { SkillNames } from "./Bladeburner/data/SkillNames" ;
import { Operation } from "./Bladeburner/Operation" ;
import { BlackOperation } from "./Bladeburner/BlackOperation" ;
import { BlackOperations } from "./Bladeburner/BlackOperations" ;
import { Contract } from "./Bladeburner/Contract" ;
import { GeneralActions } from "./Bladeburner/GeneralActions" ;
import { ActionTypes } from "./Bladeburner/data/ActionTypes" ;
2019-04-11 10:37:40 +02:00
import { addOffset } from "../utils/helpers/addOffset" ;
import { clearObject } from "../utils/helpers/clearObject" ;
import { createProgressBarText } from "../utils/helpers/createProgressBarText" ;
import { exceptionAlert } from "../utils/helpers/exceptionAlert" ;
import { getRandomInt } from "../utils/helpers/getRandomInt" ;
import { getTimestamp } from "../utils/helpers/getTimestamp" ;
import { KEY } from "../utils/helpers/keyCodes" ;
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement" ;
import { appendLineBreaks } from "../utils/uiHelpers/appendLineBreaks" ;
2021-03-07 19:19:36 +01:00
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions" ;
2019-04-11 10:37:40 +02:00
import { createElement } from "../utils/uiHelpers/createElement" ;
import { createPopup } from "../utils/uiHelpers/createPopup" ;
import { removeElement } from "../utils/uiHelpers/removeElement" ;
import { removeElementById } from "../utils/uiHelpers/removeElementById" ;
2018-05-02 19:38:11 +02:00
2021-03-17 18:40:31 +01:00
import { StatsTable } from "./ui/React/StatsTable" ;
2021-03-31 06:45:21 +02:00
import { CopyableText } from "./ui/React/CopyableText" ;
import { Money } from "./ui/React/Money" ;
import React from "react" ;
2021-03-17 18:40:31 +01:00
import ReactDOM from "react-dom" ;
2019-03-23 01:17:06 +01:00
const stealthIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 166 132" style="fill:#adff2f;"><g><path d="M132.658-0.18l-24.321,24.321c-7.915-2.71-16.342-4.392-25.087-4.392c-45.84,0-83,46-83,46 s14.1,17.44,35.635,30.844L12.32,120.158l12.021,12.021L144.68,11.841L132.658-0.18z M52.033,80.445 c-2.104-4.458-3.283-9.438-3.283-14.695c0-19.054,15.446-34.5,34.5-34.5c5.258,0,10.237,1.179,14.695,3.284L52.033,80.445z"/><path d="M134.865,37.656l-18.482,18.482c0.884,3.052,1.367,6.275,1.367,9.612c0,19.055-15.446,34.5-34.5,34.5 c-3.337,0-6.56-0.483-9.611-1.367l-10.124,10.124c6.326,1.725,12.934,2.743,19.735,2.743c45.84,0,83-46,83-46 S153.987,50.575,134.865,37.656z"/></g></svg> `
const killIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="-22 0 511 511.99561" style="fill:#adff2f;"><path d="m.496094 466.242188 39.902344-39.902344 45.753906 45.753906-39.898438 39.902344zm0 0"/><path d="m468.421875 89.832031-1.675781-89.832031-300.265625 300.265625 45.753906 45.753906zm0 0"/><path d="m95.210938 316.785156 16.84375 16.847656h.003906l83.65625 83.65625 22.753906-22.753906-100.503906-100.503906zm0 0"/><path d="m101.445312 365.300781-39.902343 39.902344 45.753906 45.753906 39.902344-39.902343-39.90625-39.902344zm0 0"/></svg> `
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// DOM related variables
const ActiveActionCssClass = "bladeburner-active-action" ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Console related stuff
let consoleHistoryIndex = 0 ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Keypresses for Console
2018-05-02 19:38:11 +02:00
$ ( document ) . keydown ( function ( event ) {
2018-07-20 03:51:18 +02:00
if ( routing . isOn ( Page . Bladeburner ) ) {
2019-04-13 03:22:46 +02:00
if ( ! ( Player . bladeburner instanceof Bladeburner ) ) { return ; }
2018-11-01 19:18:32 +01:00
let consoleHistory = Player . bladeburner . consoleHistory ;
2018-05-02 19:38:11 +02:00
if ( event . keyCode === KEY . ENTER ) {
event . preventDefault ( ) ;
var command = DomElems . consoleInput . value ;
if ( command . length > 0 ) {
Player . bladeburner . postToConsole ( "> " + command ) ;
Player . bladeburner . resetConsoleInput ( ) ;
Player . bladeburner . executeConsoleCommands ( command ) ;
}
}
if ( event . keyCode === KEY . UPARROW ) {
if ( DomElems . consoleInput == null ) { return ; }
var i = consoleHistoryIndex ;
var len = consoleHistory . length ;
if ( len === 0 ) { return ; }
if ( i < 0 || i > len ) {
consoleHistoryIndex = len ;
}
if ( i !== 0 ) {
-- consoleHistoryIndex ;
}
var prevCommand = consoleHistory [ consoleHistoryIndex ] ;
DomElems . consoleInput . value = prevCommand ;
2019-02-20 09:42:27 +01:00
setTimeoutRef ( function ( ) { DomElems . consoleInput . selectionStart = DomElems . consoleInput . selectionEnd = 10000 ; } , 0 ) ;
2018-05-02 19:38:11 +02:00
}
if ( event . keyCode === KEY . DOWNARROW ) {
if ( DomElems . consoleInput == null ) { return ; }
var i = consoleHistoryIndex ;
var len = consoleHistory . length ;
if ( len == 0 ) { return ; }
if ( i < 0 || i > len ) {
consoleHistoryIndex = len ;
}
2019-04-13 03:22:46 +02:00
// Latest command, put nothing
2018-05-02 19:38:11 +02:00
if ( i == len || i == len - 1 ) {
consoleHistoryIndex = len ;
DomElems . consoleInput . value = "" ;
} else {
++ consoleHistoryIndex ;
var prevCommand = consoleHistory [ consoleHistoryIndex ] ;
DomElems . consoleInput . value = prevCommand ;
}
}
}
} ) ;
function ActionIdentifier ( params = { } ) {
if ( params . name ) { this . name = params . name ; }
if ( params . type ) { this . type = params . type ; }
}
2019-04-13 03:22:46 +02:00
2018-05-02 19:38:11 +02:00
ActionIdentifier . prototype . toJSON = function ( ) {
return Generic _toJSON ( "ActionIdentifier" , this ) ;
}
2019-04-13 03:22:46 +02:00
2018-05-02 19:38:11 +02:00
ActionIdentifier . fromJSON = function ( value ) {
return Generic _fromJSON ( ActionIdentifier , value . data ) ;
}
2019-04-13 03:22:46 +02:00
2018-05-02 19:38:11 +02:00
Reviver . constructors . ActionIdentifier = ActionIdentifier ;
function Bladeburner ( params = { } ) {
2019-04-13 03:22:46 +02:00
this . numHosp = 0 ; // Number of hospitalizations
this . moneyLost = 0 ; // Money lost due to hospitalizations
2018-05-02 19:38:11 +02:00
this . rank = 0 ;
2019-04-13 03:22:46 +02:00
this . maxRank = 0 ; // Used to determine skill points
2018-05-02 19:38:11 +02:00
this . skillPoints = 0 ;
this . totalSkillPoints = 0 ;
2019-04-13 03:22:46 +02:00
this . teamSize = 0 ; // Number of team members
this . teamLost = 0 ; // Number of team members lost
2018-05-02 19:38:11 +02:00
this . storedCycles = 0 ;
2019-04-13 03:22:46 +02:00
this . randomEventCounter = getRandomInt ( 240 , 600 ) ; // 4-10 minutes
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// These times are in seconds
this . actionTimeToComplete = 0 ; // 0 or -1 is an infinite running action (like training)
2018-05-02 19:38:11 +02:00
this . actionTimeCurrent = 0 ;
2019-04-13 03:22:46 +02:00
// ActionIdentifier Object
2018-05-02 19:38:11 +02:00
var idleActionType = ActionTypes [ "Idle" ] ;
this . action = new ActionIdentifier ( { type : idleActionType } ) ;
this . cities = { } ;
2021-03-13 21:10:55 +01:00
for ( var i = 0 ; i < BladeburnerConstants . CityNames . length ; ++ i ) {
this . cities [ BladeburnerConstants . CityNames [ i ] ] = new City ( { name : BladeburnerConstants . CityNames [ i ] } ) ;
2018-05-02 19:38:11 +02:00
}
2021-03-13 21:10:55 +01:00
this . city = BladeburnerConstants . CityNames [ 2 ] ; // Sector-12
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Map of SkillNames -> level
2018-05-02 19:38:11 +02:00
this . skills = { } ;
this . skillMultipliers = { } ;
2019-04-13 03:22:46 +02:00
this . updateSkillMultipliers ( ) ; // Calls resetSkillMultipliers()
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Max Stamina is based on stats and Bladeburner-specific bonuses
this . staminaBonus = 0 ; // Gained from training
2018-05-02 19:38:11 +02:00
this . maxStamina = 0 ;
this . calculateMaxStamina ( ) ;
this . stamina = this . maxStamina ;
2019-04-13 03:22:46 +02:00
/ * *
* Contracts and Operations objects . These objects have unique
* properties because they are randomized in each instance and have stats like
* successes / failures , so they need to be saved / loaded by the game .
* /
2018-05-02 19:38:11 +02:00
this . contracts = { } ;
this . operations = { } ;
2019-04-13 03:22:46 +02:00
// Object that contains name of all Black Operations that have been completed
2018-05-02 19:38:11 +02:00
this . blackops = { } ;
2019-04-13 03:22:46 +02:00
// Flags for whether these actions should be logged to console
2018-05-02 19:38:11 +02:00
this . logging = {
general : true ,
contracts : true ,
ops : true ,
blackops : true ,
events : true ,
}
2019-04-13 03:22:46 +02:00
// Simple automation values
2018-05-02 19:38:11 +02:00
this . automateEnabled = false ;
this . automateActionHigh = 0 ;
this . automateThreshHigh = 0 ; //Stamina Threshold
this . automateActionLow = 0 ;
this . automateThreshLow = 0 ; //Stamina Threshold
2019-04-13 03:22:46 +02:00
// Console command history
2018-11-01 19:18:32 +01:00
this . consoleHistory = [ ] ;
2018-11-04 23:39:30 +01:00
this . consoleLogs = [ ] ;
2018-11-01 19:18:32 +01:00
2019-04-13 03:22:46 +02:00
// Initialization
2018-05-02 19:38:11 +02:00
this . initializeDomElementRefs ( ) ;
if ( params . new ) { this . create ( ) ; }
}
2018-06-13 06:16:23 +02:00
Bladeburner . prototype . prestige = function ( ) {
this . resetAction ( ) ;
var bladeburnerFac = Factions [ "Bladeburners" ] ;
2021-03-13 21:10:55 +01:00
if ( this . rank >= BladeburnerConstants . RankNeededForFaction ) {
2018-06-13 06:16:23 +02:00
joinFaction ( bladeburnerFac ) ;
}
}
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . create = function ( ) {
this . contracts [ "Tracking" ] = new Contract ( {
name : "Tracking" ,
desc : "Identify and locate Synthoids. This contract involves reconnaissance " +
"and information-gathering ONLY. Do NOT engage. Stealth is of the utmost importance.<br><br>" +
"Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for " +
"whatever city you are currently in." ,
2018-05-06 22:27:47 +02:00
baseDifficulty : 125 , difficultyFac : 1.02 , rewardFac : 1.041 ,
2018-05-02 19:38:11 +02:00
rankGain : 0.3 , hpLoss : 0.5 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 25 , 150 ) , countGrowth : getRandomInt ( 5 , 75 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0 , str : 0.05 , def : 0.05 , dex : 0.35 , agi : 0.35 , cha : 0.1 , int : 0.05 } ,
decays : { hack : 0 , str : 0.91 , def : 0.91 , dex : 0.91 , agi : 0.91 , cha : 0.9 , int : 1 } ,
isStealth : true
} ) ;
this . contracts [ "Bounty Hunter" ] = new Contract ( {
name : "Bounty Hunter" ,
desc : "Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.<br><br>" +
"Successfully completing a Bounty Hunter contract will lower the population in your " +
"current city, and will also increase its chaos level." ,
baseDifficulty : 250 , difficultyFac : 1.04 , rewardFac : 1.085 ,
rankGain : 0.9 , hpLoss : 1 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 5 , 150 ) , countGrowth : getRandomInt ( 5 , 75 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0 , str : 0.15 , def : 0.15 , dex : 0.25 , agi : 0.25 , cha : 0.1 , int : 0.1 } ,
decays : { hack : 0 , str : 0.91 , def : 0.91 , dex : 0.91 , agi : 0.91 , cha : 0.8 , int : 0.9 } ,
isKill : true
} ) ;
this . contracts [ "Retirement" ] = new Contract ( {
name : "Retirement" ,
desc : "Hunt down and retire (kill) rogue Synthoids.<br><br>" +
2019-04-15 11:49:49 +02:00
"Successfully completing a Retirement contract will lower the population in your current " +
2018-05-02 19:38:11 +02:00
"city, and will also increase its chaos level." ,
baseDifficulty : 200 , difficultyFac : 1.03 , rewardFac : 1.065 ,
rankGain : 0.6 , hpLoss : 1 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 5 , 150 ) , countGrowth : getRandomInt ( 5 , 75 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0 , str : 0.2 , def : 0.2 , dex : 0.2 , agi : 0.2 , cha : 0.1 , int : 0.1 } ,
decays : { hack : 0 , str : 0.91 , def : 0.91 , dex : 0.91 , agi : 0.91 , cha : 0.8 , int : 0.9 } ,
isKill : true
} ) ;
this . operations [ "Investigation" ] = new Operation ( {
name : "Investigation" ,
desc : "As a field agent, investigate and identify Synthoid " +
"populations, movements, and operations.<br><br>Successful " +
"Investigation ops will increase the accuracy of your " +
"synthoid data.<br><br>" +
"You will NOT lose HP from failed Investigation ops." ,
baseDifficulty : 400 , difficultyFac : 1.03 , rewardFac : 1.07 , reqdRank : 25 ,
2018-08-12 03:05:32 +02:00
rankGain : 2.2 , rankLoss : 0.2 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 100 ) , countGrowth : getRandomInt ( 10 , 40 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.25 , str : 0.05 , def : 0.05 , dex : 0.2 , agi : 0.1 , cha : 0.25 , int : 0.1 } ,
decays : { hack : 0.85 , str : 0.9 , def : 0.9 , dex : 0.9 , agi : 0.9 , cha : 0.7 , int : 0.9 } ,
isStealth : true
} ) ;
this . operations [ "Undercover Operation" ] = new Operation ( {
name : "Undercover Operation" ,
desc : "Conduct undercover operations to identify hidden " +
"and underground Synthoid communities and organizations.<br><br>" +
"Successful Undercover ops will increase the accuracy of your synthoid " +
"data." ,
baseDifficulty : 500 , difficultyFac : 1.04 , rewardFac : 1.09 , reqdRank : 100 ,
2018-08-12 03:05:32 +02:00
rankGain : 4.4 , rankLoss : 0.4 , hpLoss : 2 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 100 ) , countGrowth : getRandomInt ( 10 , 40 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.2 , str : 0.05 , def : 0.05 , dex : 0.2 , agi : 0.2 , cha : 0.2 , int : 0.1 } ,
decays : { hack : 0.8 , str : 0.9 , def : 0.9 , dex : 0.9 , agi : 0.9 , cha : 0.7 , int : 0.9 } ,
isStealth : true
} ) ;
this . operations [ "Sting Operation" ] = new Operation ( {
name : "Sting Operation" ,
desc : "Conduct a sting operation to bait and capture particularly " +
"notorious Synthoid criminals." ,
baseDifficulty : 650 , difficultyFac : 1.04 , rewardFac : 1.095 , reqdRank : 500 ,
2018-08-12 03:05:32 +02:00
rankGain : 5.5 , rankLoss : 0.5 , hpLoss : 2.5 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 150 ) , countGrowth : getRandomInt ( 3 , 40 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.25 , str : 0.05 , def : 0.05 , dex : 0.25 , agi : 0.1 , cha : 0.2 , int : 0.1 } ,
decays : { hack : 0.8 , str : 0.85 , def : 0.85 , dex : 0.85 , agi : 0.85 , cha : 0.7 , int : 0.9 } ,
isStealth : true
} ) ;
this . operations [ "Raid" ] = new Operation ( {
name : "Raid" ,
desc : "Lead an assault on a known Synthoid community. Note that " +
"there must be an existing Synthoid community in your current city " +
"in order for this Operation to be successful" ,
baseDifficulty : 800 , difficultyFac : 1.045 , rewardFac : 1.1 , reqdRank : 3000 ,
2018-08-12 03:05:32 +02:00
rankGain : 55 , rankLoss : 2.5 , hpLoss : 50 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 150 ) , countGrowth : getRandomInt ( 2 , 40 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.1 , str : 0.2 , def : 0.2 , dex : 0.2 , agi : 0.2 , cha : 0 , int : 0.1 } ,
decays : { hack : 0.7 , str : 0.8 , def : 0.8 , dex : 0.8 , agi : 0.8 , cha : 0 , int : 0.9 } ,
isKill : true
} ) ;
this . operations [ "Stealth Retirement Operation" ] = new Operation ( {
name : "Stealth Retirement Operation" ,
desc : "Lead a covert operation to retire Synthoids. The " +
"objective is to complete the task without " +
"drawing any attention. Stealth and discretion are key." ,
baseDifficulty : 1000 , difficultyFac : 1.05 , rewardFac : 1.11 , reqdRank : 20 e3 ,
2018-08-12 03:05:32 +02:00
rankGain : 22 , rankLoss : 2 , hpLoss : 10 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 150 ) , countGrowth : getRandomInt ( 1 , 20 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.1 , str : 0.1 , def : 0.1 , dex : 0.3 , agi : 0.3 , cha : 0 , int : 0.1 } ,
decays : { hack : 0.7 , str : 0.8 , def : 0.8 , dex : 0.8 , agi : 0.8 , cha : 0 , int : 0.9 } ,
isStealth : true , isKill : true
} ) ;
this . operations [ "Assassination" ] = new Operation ( {
name : "Assassination" ,
desc : "Assassinate Synthoids that have been identified as " +
"important, high-profile social and political leaders " +
"in the Synthoid communities." ,
baseDifficulty : 1500 , difficultyFac : 1.06 , rewardFac : 1.14 , reqdRank : 50 e3 ,
2018-08-12 03:05:32 +02:00
rankGain : 44 , rankLoss : 4 , hpLoss : 5 ,
2019-02-22 22:26:30 +01:00
count : getRandomInt ( 1 , 150 ) , countGrowth : getRandomInt ( 1 , 20 ) / 10 ,
2018-05-02 19:38:11 +02:00
weights : { hack : 0.1 , str : 0.1 , def : 0.1 , dex : 0.3 , agi : 0.3 , cha : 0 , int : 0.1 } ,
decays : { hack : 0.6 , str : 0.8 , def : 0.8 , dex : 0.8 , agi : 0.8 , cha : 0 , int : 0.8 } ,
isStealth : true , isKill : true
} ) ;
}
Bladeburner . prototype . storeCycles = function ( numCycles = 1 ) {
this . storedCycles += numCycles ;
}
2021-03-13 21:10:55 +01:00
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . process = function ( ) {
2019-04-13 03:22:46 +02:00
// Edge case condition...if Operation Daedalus is complete trigger the BitNode
2018-06-13 06:16:23 +02:00
if ( redPillFlag === false && this . blackops . hasOwnProperty ( "Operation Daedalus" ) ) {
return hackWorldDaemon ( Player . bitNodeN ) ;
}
2019-04-13 03:22:46 +02:00
// If the Player starts doing some other actions, set action to idle and alert
2018-06-13 06:16:23 +02:00
if ( Augmentations [ AugmentationNames . BladesSimulacrum ] . owned === false && Player . isWorking ) {
2018-05-02 19:38:11 +02:00
if ( this . action . type !== ActionTypes [ "Idle" ] ) {
2018-08-12 03:05:32 +02:00
let msg = "Your Bladeburner action was cancelled because you started doing something else." ;
if ( this . automateEnabled ) {
msg += ` <br><br>Your automation was disabled as well. You will have to re-enable it through the Bladeburner console `
this . automateEnabled = false ;
}
dialogBoxCreate ( msg ) ;
2018-05-02 19:38:11 +02:00
}
this . resetAction ( ) ;
}
2018-11-04 23:39:30 +01:00
// If the Player has no Stamina, set action to idle
if ( this . stamina <= 0 ) {
this . log ( "Your Bladeburner action was cancelled because your stamina hit 0" ) ;
this . resetAction ( ) ;
}
2019-04-13 03:22:46 +02:00
// A 'tick' for this mechanic is one second (= 5 game cycles)
2021-03-13 21:10:55 +01:00
if ( this . storedCycles >= BladeburnerConstants . CyclesPerSecond ) {
var seconds = Math . floor ( this . storedCycles / BladeburnerConstants . CyclesPerSecond ) ;
2019-04-13 03:22:46 +02:00
seconds = Math . min ( seconds , 5 ) ; // Max of 5 'ticks'
2021-03-13 21:10:55 +01:00
this . storedCycles -= seconds * BladeburnerConstants . CyclesPerSecond ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Stamina
2018-05-02 19:38:11 +02:00
this . calculateMaxStamina ( ) ;
this . stamina += ( this . calculateStaminaGainPerSecond ( ) * seconds ) ;
this . stamina = Math . min ( this . maxStamina , this . stamina ) ;
2019-04-13 03:22:46 +02:00
// Count increase for contracts/operations
2018-05-02 19:38:11 +02:00
for ( var contractName in this . contracts ) {
if ( this . contracts . hasOwnProperty ( contractName ) ) {
var contract = this . contracts [ contractName ] ;
2021-03-13 21:10:55 +01:00
contract . count += ( seconds * contract . countGrowth / BladeburnerConstants . ActionCountGrowthPeriod ) ;
2018-05-02 19:38:11 +02:00
}
}
for ( var operationName in this . operations ) {
if ( this . operations . hasOwnProperty ( operationName ) ) {
var op = this . operations [ operationName ] ;
2021-03-13 21:10:55 +01:00
op . count += ( seconds * op . countGrowth / BladeburnerConstants . ActionCountGrowthPeriod ) ;
2018-05-02 19:38:11 +02:00
}
}
2019-04-13 03:22:46 +02:00
// Chaos goes down very slowly
2021-03-13 21:10:55 +01:00
for ( var i = 0 ; i < BladeburnerConstants . CityNames . length ; ++ i ) {
var city = this . cities [ BladeburnerConstants . CityNames [ i ] ] ;
2018-05-02 19:38:11 +02:00
if ( ! ( city instanceof City ) ) { throw new Error ( "Invalid City object when processing passive chaos reduction in Bladeburner.process" ) ; }
city . chaos -= ( 0.0001 * seconds ) ;
city . chaos = Math . max ( 0 , city . chaos ) ;
}
2019-04-13 03:22:46 +02:00
// Random Events
2018-05-02 19:38:11 +02:00
this . randomEventCounter -= seconds ;
if ( this . randomEventCounter <= 0 ) {
this . randomEvent ( ) ;
2018-09-06 18:07:59 +02:00
this . randomEventCounter = getRandomInt ( 240 , 600 ) ;
2018-05-02 19:38:11 +02:00
}
this . processAction ( seconds ) ;
2019-04-13 03:22:46 +02:00
// Automation
2018-05-02 19:38:11 +02:00
if ( this . automateEnabled ) {
2018-11-19 10:03:38 +01:00
// Note: Do NOT set this.action = this.automateActionHigh/Low since it creates a reference
2018-05-02 19:38:11 +02:00
if ( this . stamina <= this . automateThreshLow ) {
if ( this . action . name !== this . automateActionLow . name || this . action . type !== this . automateActionLow . type ) {
2018-11-19 10:03:38 +01:00
this . action = new ActionIdentifier ( { type : this . automateActionLow . type , name : this . automateActionLow . name } ) ;
2018-05-02 19:38:11 +02:00
this . startAction ( this . action ) ;
}
} else if ( this . stamina >= this . automateThreshHigh ) {
if ( this . action . name !== this . automateActionHigh . name || this . action . type !== this . automateActionHigh . type ) {
2018-11-19 10:03:38 +01:00
this . action = new ActionIdentifier ( { type : this . automateActionHigh . type , name : this . automateActionHigh . name } ) ;
2018-05-02 19:38:11 +02:00
this . startAction ( this . action ) ;
}
}
}
2018-07-20 03:51:18 +02:00
if ( routing . isOn ( Page . Bladeburner ) ) {
2018-05-02 19:38:11 +02:00
this . updateContent ( ) ;
}
}
}
Bladeburner . prototype . calculateMaxStamina = function ( ) {
2021-03-07 22:48:48 +01:00
const effAgility = Player . agility * this . skillMultipliers . effAgi ;
let maxStamina = ( Math . pow ( effAgility , 0.8 ) + this . staminaBonus ) *
this . skillMultipliers . stamina *
Player . bladeburner _max _stamina _mult ;
if ( this . maxStamina !== maxStamina ) {
const oldMax = this . maxStamina ;
this . maxStamina = maxStamina ;
this . stamina = this . maxStamina * this . stamina / oldMax ;
}
2018-05-02 19:38:11 +02:00
if ( isNaN ( maxStamina ) ) { throw new Error ( "Max Stamina calculated to be NaN in Bladeburner.calculateMaxStamina()" ) ; }
}
Bladeburner . prototype . calculateStaminaGainPerSecond = function ( ) {
var effAgility = Player . agility * this . skillMultipliers . effAgi ;
2021-03-13 21:10:55 +01:00
var maxStaminaBonus = this . maxStamina / BladeburnerConstants . MaxStaminaToGainFactor ;
var gain = ( BladeburnerConstants . StaminaGainPerSecond + maxStaminaBonus ) * Math . pow ( effAgility , 0.17 ) ;
2018-05-02 19:38:11 +02:00
return gain * ( this . skillMultipliers . stamina * Player . bladeburner _stamina _gain _mult ) ;
}
Bladeburner . prototype . calculateStaminaPenalty = function ( ) {
return Math . min ( 1 , this . stamina / ( 0.5 * this . maxStamina ) ) ;
}
Bladeburner . prototype . changeRank = function ( change ) {
if ( isNaN ( change ) ) { throw new Error ( "NaN passed into Bladeburner.changeRank()" ) ; }
this . rank += change ;
if ( this . rank < 0 ) { this . rank = 0 ; }
this . maxRank = Math . max ( this . rank , this . maxRank ) ;
var bladeburnersFactionName = "Bladeburners" ;
if ( factionExists ( bladeburnersFactionName ) ) {
var bladeburnerFac = Factions [ bladeburnersFactionName ] ;
if ( ! ( bladeburnerFac instanceof Faction ) ) {
throw new Error ( "Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button" ) ;
}
if ( bladeburnerFac . isMember ) {
2018-06-13 06:16:23 +02:00
var favorBonus = 1 + ( bladeburnerFac . favor / 100 ) ;
2021-03-13 21:10:55 +01:00
bladeburnerFac . playerReputation += ( BladeburnerConstants . RankToFactionRepFactor * change * Player . faction _rep _mult * favorBonus ) ;
2018-05-02 19:38:11 +02:00
}
}
2019-02-14 11:02:25 +01:00
// Gain skill points
2021-03-13 21:10:55 +01:00
var rankNeededForSp = ( this . totalSkillPoints + 1 ) * BladeburnerConstants . RanksPerSkillPoint ;
2018-05-02 19:38:11 +02:00
if ( this . maxRank >= rankNeededForSp ) {
2019-04-13 03:22:46 +02:00
// Calculate how many skill points to gain
2021-03-13 21:10:55 +01:00
var gainedSkillPoints = Math . floor ( ( this . maxRank - rankNeededForSp ) / BladeburnerConstants . RanksPerSkillPoint + 1 ) ;
2018-05-02 19:38:11 +02:00
this . skillPoints += gainedSkillPoints ;
this . totalSkillPoints += gainedSkillPoints ;
}
}
Bladeburner . prototype . getCurrentCity = function ( ) {
var city = this . cities [ this . city ] ;
if ( ! ( city instanceof City ) ) {
throw new Error ( "Bladeburner.getCurrentCity() did not properly return a City object" ) ;
}
return city ;
}
Bladeburner . prototype . resetSkillMultipliers = function ( ) {
this . skillMultipliers = {
2019-02-02 10:51:00 +01:00
successChanceAll : 1 ,
successChanceStealth : 1 ,
successChanceKill : 1 ,
successChanceContract : 1 ,
successChanceOperation : 1 ,
successChanceEstimate : 1 ,
actionTime : 1 ,
effHack : 1 ,
effStr : 1 ,
effDef : 1 ,
effDex : 1 ,
effAgi : 1 ,
effCha : 1 ,
effInt : 1 ,
stamina : 1 ,
money : 1 ,
expGain : 1 ,
2018-05-02 19:38:11 +02:00
} ;
}
Bladeburner . prototype . updateSkillMultipliers = function ( ) {
this . resetSkillMultipliers ( ) ;
for ( var skillName in this . skills ) {
if ( this . skills . hasOwnProperty ( skillName ) ) {
var skill = Skills [ skillName ] ;
if ( skill == null ) {
throw new Error ( "Could not find Skill Object for: " + skillName ) ;
}
var level = this . skills [ skillName ] ;
if ( level == null || level <= 0 ) { continue ; } //Not upgraded
var multiplierNames = Object . keys ( this . skillMultipliers ) ;
for ( var i = 0 ; i < multiplierNames . length ; ++ i ) {
var multiplierName = multiplierNames [ i ] ;
if ( skill [ multiplierName ] != null && ! isNaN ( skill [ multiplierName ] ) ) {
var value = skill [ multiplierName ] * level ;
var multiplierValue = 1 + ( value / 100 ) ;
if ( multiplierName === "actionTime" ) {
multiplierValue = 1 - ( value / 100 ) ;
}
this . skillMultipliers [ multiplierName ] *= multiplierValue ;
}
}
}
}
}
Bladeburner . prototype . upgradeSkill = function ( skill ) {
2019-04-13 03:22:46 +02:00
// This does NOT handle deduction of skill points
2018-05-02 19:38:11 +02:00
var skillName = skill . name ;
if ( this . skills [ skillName ] ) {
++ this . skills [ skillName ] ;
} else {
this . skills [ skillName ] = 1 ;
}
if ( isNaN ( this . skills [ skillName ] ) || this . skills [ skillName ] < 0 ) {
throw new Error ( "Level of Skill " + skillName + " is invalid: " + this . skills [ skillName ] ) ;
}
this . updateSkillMultipliers ( ) ;
}
Bladeburner . prototype . getActionObject = function ( actionId ) {
2019-04-13 03:22:46 +02:00
/ * *
* Given an ActionIdentifier object , returns the corresponding
* GeneralAction , Contract , Operation , or BlackOperation object
* /
2018-05-02 19:38:11 +02:00
switch ( actionId . type ) {
case ActionTypes [ "Contract" ] :
return this . contracts [ actionId . name ] ;
case ActionTypes [ "Operation" ] :
return this . operations [ actionId . name ] ;
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
return BlackOperations [ actionId . name ] ;
2018-07-28 02:00:57 +02:00
case ActionTypes [ "Training" ] :
return GeneralActions [ "Training" ] ;
case ActionTypes [ "Field Analysis" ] :
return GeneralActions [ "Field Analysis" ] ;
case ActionTypes [ "Recruitment" ] :
return GeneralActions [ "Recruitment" ] ;
2018-05-02 19:38:11 +02:00
default :
return null ;
}
}
2019-04-13 03:22:46 +02:00
// Sets the player to the "IDLE" action
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . resetAction = function ( ) {
this . action = new ActionIdentifier ( { type : ActionTypes . Idle } ) ;
}
Bladeburner . prototype . startAction = function ( actionId ) {
2018-06-08 17:51:48 +02:00
if ( actionId == null ) { return ; }
2018-05-02 19:38:11 +02:00
this . action = actionId ;
this . actionTimeCurrent = 0 ;
switch ( actionId . type ) {
case ActionTypes [ "Idle" ] :
this . actionTimeToComplete = 0 ;
break ;
case ActionTypes [ "Contract" ] :
try {
var action = this . getActionObject ( actionId ) ;
if ( action == null ) {
throw new Error ( "Failed to get Contract Object for: " + actionId . name ) ;
}
if ( action . count < 1 ) { return this . resetAction ( ) ; }
this . actionTimeToComplete = action . getActionTime ( this ) ;
} catch ( e ) {
exceptionAlert ( e ) ;
}
break ;
case ActionTypes [ "Operation" ] :
try {
var action = this . getActionObject ( actionId ) ;
if ( action == null ) {
throw new Error ( "Failed to get Operation Object for: " + actionId . name ) ;
}
if ( action . count < 1 ) { return this . resetAction ( ) ; }
2018-07-28 02:58:52 +02:00
if ( actionId . name === "Raid" && this . getCurrentCity ( ) . commsEst === 0 ) { return this . resetAction ( ) ; }
2018-05-02 19:38:11 +02:00
this . actionTimeToComplete = action . getActionTime ( this ) ;
} catch ( e ) {
exceptionAlert ( e ) ;
}
break ;
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
try {
2018-11-19 10:03:38 +01:00
// Safety measure - don't repeat BlackOps that are already done
if ( this . blackops [ actionId . name ] != null ) {
this . resetAction ( ) ;
this . log ( "Error: Tried to start a Black Operation that had already been completed" ) ;
break ;
}
2018-05-02 19:38:11 +02:00
var action = this . getActionObject ( actionId ) ;
if ( action == null ) {
throw new Error ( "Failed to get BlackOperation object for: " + actionId . name ) ;
}
this . actionTimeToComplete = action . getActionTime ( this ) ;
} catch ( e ) {
exceptionAlert ( e ) ;
}
break ;
case ActionTypes [ "Recruitment" ] :
2018-06-08 17:51:48 +02:00
this . actionTimeToComplete = this . getRecruitmentTime ( ) ;
2018-05-02 19:38:11 +02:00
break ;
2019-02-20 09:42:27 +01:00
case ActionTypes [ "Training" ] :
2018-05-02 19:38:11 +02:00
case ActionTypes [ "FieldAnalysis" ] :
case ActionTypes [ "Field Analysis" ] :
this . actionTimeToComplete = 30 ;
break ;
2019-02-20 09:42:27 +01:00
case ActionTypes [ "Diplomacy" ] :
case ActionTypes [ "Hyperbolic Regeneration Chamber" ] :
this . actionTimeToComplete = 60 ;
break ;
2018-05-02 19:38:11 +02:00
default :
throw new Error ( "Invalid Action Type in Bladeburner.startAction(): " + actionId . type ) ;
break ;
}
}
Bladeburner . prototype . processAction = function ( seconds ) {
if ( this . action . type === ActionTypes [ "Idle" ] ) { return ; }
if ( this . actionTimeToComplete <= 0 ) {
2021-03-08 04:46:50 +01:00
throw new Error ( ` Invalid actionTimeToComplete value: ${ this . actionTimeToComplete } , type; ${ this . action . type } ` ) ;
2018-05-02 19:38:11 +02:00
}
if ( ! ( this . action instanceof ActionIdentifier ) ) {
throw new Error ( "Bladeburner.action is not an ActionIdentifier Object" ) ;
}
this . actionTimeCurrent += seconds ;
if ( this . actionTimeCurrent >= this . actionTimeToComplete ) {
return this . completeAction ( ) ;
}
}
Bladeburner . prototype . completeAction = function ( ) {
switch ( this . action . type ) {
case ActionTypes [ "Contract" ] :
case ActionTypes [ "Operation" ] :
try {
var isOperation = ( this . action . type === ActionTypes [ "Operation" ] ) ;
var action = this . getActionObject ( this . action ) ;
if ( action == null ) {
throw new Error ( "Failed to get Contract/Operation Object for: " + this . action . name ) ;
}
var difficulty = action . getDifficulty ( ) ;
2021-03-13 21:10:55 +01:00
var difficultyMultiplier = Math . pow ( difficulty , BladeburnerConstants . DiffMultExponentialFactor ) + difficulty / BladeburnerConstants . DiffMultLinearFactor ;
2018-05-02 19:38:11 +02:00
var rewardMultiplier = Math . pow ( action . rewardFac , action . level - 1 ) ;
2019-04-13 03:22:46 +02:00
// Stamina loss is based on difficulty
2021-03-13 21:10:55 +01:00
this . stamina -= ( BladeburnerConstants . BaseStaminaLoss * difficultyMultiplier ) ;
2018-05-02 19:38:11 +02:00
if ( this . stamina < 0 ) { this . stamina = 0 ; }
2019-04-13 03:22:46 +02:00
// Process Contract/Operation success/failure
2018-05-02 19:38:11 +02:00
if ( action . attempt ( this ) ) {
this . gainActionStats ( action , true ) ;
++ action . successes ;
-- action . count ;
2019-04-13 03:22:46 +02:00
// Earn money for contracts
2018-05-23 02:09:04 +02:00
var moneyGain = 0 ;
if ( ! isOperation ) {
2021-03-13 21:10:55 +01:00
moneyGain = BladeburnerConstants . ContractBaseMoneyGain * rewardMultiplier * this . skillMultipliers . money ;
2018-05-23 02:09:04 +02:00
Player . gainMoney ( moneyGain ) ;
2019-02-22 03:26:28 +01:00
Player . recordMoneySource ( moneyGain , "bladeburner" ) ;
2018-05-23 02:09:04 +02:00
}
2018-05-02 19:38:11 +02:00
if ( isOperation ) {
2021-03-13 21:10:55 +01:00
action . setMaxLevel ( BladeburnerConstants . OperationSuccessesPerLevel ) ;
2018-05-02 19:38:11 +02:00
} else {
2021-03-13 21:10:55 +01:00
action . setMaxLevel ( BladeburnerConstants . ContractSuccessesPerLevel ) ;
2018-05-02 19:38:11 +02:00
}
if ( action . rankGain ) {
2018-06-25 02:13:50 +02:00
var gain = addOffset ( action . rankGain * rewardMultiplier * BitNodeMultipliers . BladeburnerRank , 10 ) ;
2018-05-02 19:38:11 +02:00
this . changeRank ( gain ) ;
if ( isOperation && this . logging . ops ) {
this . log ( action . name + " successfully completed! Gained " + formatNumber ( gain , 3 ) + " rank" ) ;
} else if ( ! isOperation && this . logging . contracts ) {
2021-03-31 06:45:21 +02:00
this . log ( action . name + " contract successfully completed! Gained " + formatNumber ( gain , 3 ) + " rank and " + numeralWrapper . formatMoney ( moneyGain ) ) ;
2018-05-02 19:38:11 +02:00
}
}
isOperation ? this . completeOperation ( true ) : this . completeContract ( true ) ;
} else {
this . gainActionStats ( action , false ) ;
++ action . failures ;
var loss = 0 , damage = 0 ;
if ( action . rankLoss ) {
loss = addOffset ( action . rankLoss * rewardMultiplier , 10 ) ;
this . changeRank ( - 1 * loss ) ;
}
if ( action . hpLoss ) {
damage = action . hpLoss * difficultyMultiplier ;
damage = Math . ceil ( addOffset ( damage , 10 ) ) ;
this . hpLost += damage ;
2021-04-06 09:50:09 +02:00
const cost = calculateHospitalizationCost ( Player , damage ) ;
2018-05-02 19:38:11 +02:00
if ( Player . takeDamage ( damage ) ) {
++ this . numHosp ;
2021-04-06 09:50:09 +02:00
this . moneyLost += cost ;
2018-05-02 19:38:11 +02:00
}
}
var logLossText = "" ;
if ( loss > 0 ) { logLossText += "Lost " + formatNumber ( loss , 3 ) + " rank." ; }
if ( damage > 0 ) { logLossText += "Took " + formatNumber ( damage , 0 ) + " damage." ; }
if ( isOperation && this . logging . ops ) {
this . log ( action . name + " failed! " + logLossText ) ;
} else if ( ! isOperation && this . logging . contracts ) {
this . log ( action . name + " contract failed! " + logLossText ) ;
}
isOperation ? this . completeOperation ( false ) : this . completeContract ( false ) ;
}
2019-04-13 03:22:46 +02:00
if ( action . autoLevel ) { action . level = action . maxLevel ; } // Autolevel
this . startAction ( this . action ) ; // Repeat action
2018-05-02 19:38:11 +02:00
} catch ( e ) {
exceptionAlert ( e ) ;
}
break ;
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
try {
var action = this . getActionObject ( this . action ) ;
if ( action == null || ! ( action instanceof BlackOperation ) ) {
throw new Error ( "Failed to get BlackOperation Object for: " + this . action . name ) ;
}
var difficulty = action . getDifficulty ( ) ;
2021-03-13 21:10:55 +01:00
var difficultyMultiplier = Math . pow ( difficulty , BladeburnerConstants . DiffMultExponentialFactor ) + difficulty / BladeburnerConstants . DiffMultLinearFactor ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Stamina loss is based on difficulty
2021-03-13 21:10:55 +01:00
this . stamina -= ( BladeburnerConstants . BaseStaminaLoss * difficultyMultiplier ) ;
2018-05-02 19:38:11 +02:00
if ( this . stamina < 0 ) { this . stamina = 0 ; }
2019-04-13 03:22:46 +02:00
// Team loss variables
2018-05-02 19:38:11 +02:00
var teamCount = action . teamCount , teamLossMax ;
if ( action . attempt ( this ) ) {
this . gainActionStats ( action , true ) ;
action . count = 0 ;
this . blackops [ action . name ] = true ;
var rankGain = 0 ;
if ( action . rankGain ) {
2018-06-25 02:13:50 +02:00
rankGain = addOffset ( action . rankGain * BitNodeMultipliers . BladeburnerRank , 10 ) ;
2018-05-02 19:38:11 +02:00
this . changeRank ( rankGain ) ;
}
teamLossMax = Math . ceil ( teamCount / 2 ) ;
2019-04-13 03:22:46 +02:00
// Operation Daedalus
2018-05-02 19:38:11 +02:00
if ( action . name === "Operation Daedalus" ) {
this . resetAction ( ) ;
return hackWorldDaemon ( Player . bitNodeN ) ;
}
2018-07-20 03:51:18 +02:00
if ( routing . isOn ( Page . Bladeburner ) ) {
2018-05-19 05:15:58 +02:00
this . createActionAndSkillsContent ( ) ;
}
2018-05-02 19:38:11 +02:00
if ( this . logging . blackops ) {
this . log ( action . name + " successful! Gained " + formatNumber ( rankGain , 1 ) + " rank" ) ;
}
} else {
this . gainActionStats ( action , false ) ;
var rankLoss = 0 , damage = 0 ;
if ( action . rankLoss ) {
rankLoss = addOffset ( action . rankLoss , 10 ) ;
this . changeRank ( - 1 * rankLoss ) ;
}
if ( action . hpLoss ) {
damage = action . hpLoss * difficultyMultiplier ;
damage = Math . ceil ( addOffset ( damage , 10 ) ) ;
2021-04-06 09:50:09 +02:00
const cost = calculateHospitalizationCost ( Player , damage ) ;
2018-05-02 19:38:11 +02:00
if ( Player . takeDamage ( damage ) ) {
++ this . numHosp ;
2021-04-06 09:50:09 +02:00
this . moneyLost += cost ;
2018-05-02 19:38:11 +02:00
}
}
teamLossMax = Math . floor ( teamCount ) ;
if ( this . logging . blackops ) {
2018-07-22 19:55:17 +02:00
this . log ( action . name + " failed! Lost " + formatNumber ( rankLoss , 1 ) + " rank and took " + formatNumber ( damage , 0 ) + " damage" ) ;
2018-05-02 19:38:11 +02:00
}
}
2019-04-13 03:22:46 +02:00
this . resetAction ( ) ; // Stop regardless of success or fail
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Calculate team lossses
2018-05-02 19:38:11 +02:00
if ( teamCount >= 1 ) {
var losses = getRandomInt ( 1 , teamLossMax ) ;
this . teamSize -= losses ;
this . teamLost += losses ;
if ( this . logging . blackops ) {
this . log ( "You lost " + formatNumber ( losses , 0 ) + " team members during " + action . name ) ;
}
}
} catch ( e ) {
exceptionAlert ( e ) ;
}
break ;
case ActionTypes [ "Training" ] :
2021-03-13 21:10:55 +01:00
this . stamina -= ( 0.5 * BladeburnerConstants . BaseStaminaLoss ) ;
2018-05-10 19:14:45 +02:00
var strExpGain = 30 * Player . strength _exp _mult ,
defExpGain = 30 * Player . defense _exp _mult ,
dexExpGain = 30 * Player . dexterity _exp _mult ,
agiExpGain = 30 * Player . agility _exp _mult ,
2018-05-02 19:38:11 +02:00
staminaGain = 0.04 * this . skillMultipliers . stamina ;
Player . gainStrengthExp ( strExpGain ) ;
Player . gainDefenseExp ( defExpGain ) ;
Player . gainDexterityExp ( dexExpGain ) ;
Player . gainAgilityExp ( agiExpGain ) ;
this . staminaBonus += ( staminaGain ) ;
if ( this . logging . general ) {
this . log ( "Training completed. Gained: " +
formatNumber ( strExpGain , 1 ) + " str exp, " +
formatNumber ( defExpGain , 1 ) + " def exp, " +
formatNumber ( dexExpGain , 1 ) + " dex exp, " +
formatNumber ( agiExpGain , 1 ) + " agi exp, " +
formatNumber ( staminaGain , 3 ) + " max stamina" ) ;
}
2019-04-13 03:22:46 +02:00
this . startAction ( this . action ) ; // Repeat action
2018-05-02 19:38:11 +02:00
break ;
case ActionTypes [ "FieldAnalysis" ] :
case ActionTypes [ "Field Analysis" ] :
2019-04-13 03:22:46 +02:00
// Does not use stamina. Effectiveness depends on hacking, int, and cha
2018-05-02 19:38:11 +02:00
var eff = 0.04 * Math . pow ( Player . hacking _skill , 0.3 ) +
0.04 * Math . pow ( Player . intelligence , 0.9 ) +
0.02 * Math . pow ( Player . charisma , 0.3 ) ;
eff *= Player . bladeburner _analysis _mult ;
if ( isNaN ( eff ) || eff < 0 ) {
throw new Error ( "Field Analysis Effectiveness calculated to be NaN or negative" ) ;
}
var hackingExpGain = 20 * Player . hacking _exp _mult ,
charismaExpGain = 20 * Player . charisma _exp _mult ;
Player . gainHackingExp ( hackingExpGain ) ;
2021-03-13 21:10:55 +01:00
Player . gainIntelligenceExp ( BladeburnerConstants . BaseIntGain ) ;
2018-05-02 19:38:11 +02:00
Player . gainCharismaExp ( charismaExpGain ) ;
2018-08-12 03:05:32 +02:00
this . changeRank ( 0.1 * BitNodeMultipliers . BladeburnerRank ) ;
2018-05-02 19:38:11 +02:00
this . getCurrentCity ( ) . improvePopulationEstimateByPercentage ( eff * this . skillMultipliers . successChanceEstimate ) ;
if ( this . logging . general ) {
2018-05-23 02:09:04 +02:00
this . log ( "Field analysis completed. Gained 0.1 rank, " + formatNumber ( hackingExpGain , 1 ) + " hacking exp, and " + formatNumber ( charismaExpGain , 1 ) + " charisma exp" ) ;
2018-05-02 19:38:11 +02:00
}
2019-04-13 03:22:46 +02:00
this . startAction ( this . action ) ; // Repeat action
2018-05-02 19:38:11 +02:00
break ;
case ActionTypes [ "Recruitment" ] :
2018-06-08 17:51:48 +02:00
var successChance = this . getRecruitmentSuccessChance ( ) ;
2018-05-02 19:38:11 +02:00
if ( Math . random ( ) < successChance ) {
2021-03-13 21:10:55 +01:00
var expGain = 2 * BladeburnerConstants . BaseStatGain * this . actionTimeToComplete ;
2018-05-02 19:38:11 +02:00
Player . gainCharismaExp ( expGain ) ;
++ this . teamSize ;
if ( this . logging . general ) {
this . log ( "Successfully recruited a team member! Gained " + formatNumber ( expGain , 1 ) + " charisma exp" ) ;
}
} else {
2021-03-13 21:10:55 +01:00
var expGain = BladeburnerConstants . BaseStatGain * this . actionTimeToComplete ;
2018-05-02 19:38:11 +02:00
Player . gainCharismaExp ( expGain ) ;
if ( this . logging . general ) {
this . log ( "Failed to recruit a team member. Gained " + formatNumber ( expGain , 1 ) + " charisma exp" ) ;
}
}
2019-04-13 03:22:46 +02:00
this . startAction ( this . action ) ; // Repeat action
2018-05-02 19:38:11 +02:00
break ;
2019-02-14 11:02:25 +01:00
case ActionTypes [ "Diplomacy" ] :
var eff = this . getDiplomacyEffectiveness ( ) ;
this . getCurrentCity ( ) . chaos *= eff ;
2019-02-20 09:42:27 +01:00
if ( this . getCurrentCity ( ) . chaos < 0 ) { this . getCurrentCity ( ) . chaos = 0 ; }
2019-02-14 11:02:25 +01:00
if ( this . logging . general ) {
this . log ( ` Diplomacy completed. Chaos levels in the current city fell by ${ numeralWrapper . formatPercentage ( 1 - eff ) } ` ) ;
}
this . startAction ( this . action ) ; // Repeat Action
break ;
2019-03-27 09:36:14 +01:00
case ActionTypes [ "Hyperbolic Regeneration Chamber" ] : {
2021-03-13 21:10:55 +01:00
Player . regenerateHp ( BladeburnerConstants . HrcHpGain ) ;
2019-03-27 09:36:14 +01:00
2021-03-13 21:10:55 +01:00
const staminaGain = this . maxStamina * ( BladeburnerConstants . HrcStaminaGain / 100 ) ;
2019-03-27 09:36:14 +01:00
this . stamina = Math . min ( this . maxStamina , this . stamina + staminaGain ) ;
2019-02-20 09:42:27 +01:00
this . startAction ( this . action ) ;
2019-02-24 05:27:06 +01:00
if ( this . logging . general ) {
2021-03-31 06:45:21 +02:00
this . log ( ` Rested in Hyperbolic Regeneration Chamber. Restored ${ BladeburnerConstants . HrcHpGain } HP and gained ${ numeralWrapper . formatStamina ( staminaGain ) } stamina ` ) ;
2019-02-24 05:27:06 +01:00
}
2019-02-20 09:42:27 +01:00
break ;
2019-03-27 09:36:14 +01:00
}
2018-05-02 19:38:11 +02:00
default :
2019-02-20 09:42:27 +01:00
console . error ( ` Bladeburner.completeAction() called for invalid action: ${ this . action . type } ` ) ;
2018-05-02 19:38:11 +02:00
break ;
}
}
Bladeburner . prototype . completeContract = function ( success ) {
if ( this . action . type !== ActionTypes . Contract ) {
throw new Error ( "completeContract() called even though current action is not a Contract" ) ;
}
var city = this . getCurrentCity ( ) ;
if ( success ) {
switch ( this . action . name ) {
case "Tracking" :
2019-04-13 03:22:46 +02:00
// Increase estimate accuracy by a relatively small amount
2018-05-02 19:38:11 +02:00
city . improvePopulationEstimateByCount ( getRandomInt ( 100 , 1 e3 ) ) ;
break ;
case "Bounty Hunter" :
city . changePopulationByCount ( - 1 , { estChange : - 1 } ) ;
city . changeChaosByCount ( 0.02 ) ;
break ;
case "Retirement" :
city . changePopulationByCount ( - 1 , { estChange : - 1 } ) ;
city . changeChaosByCount ( 0.04 ) ;
break ;
default :
throw new Error ( "Invalid Action name in completeContract: " + this . action . name ) ;
}
}
}
Bladeburner . prototype . completeOperation = function ( success ) {
if ( this . action . type !== ActionTypes . Operation ) {
throw new Error ( "completeOperation() called even though current action is not an Operation" ) ;
}
var action = this . getActionObject ( this . action ) ;
if ( action == null ) {
throw new Error ( "Failed to get Contract/Operation Object for: " + this . action . name ) ;
}
2019-04-13 03:22:46 +02:00
// Calculate team losses
2018-05-02 19:38:11 +02:00
var teamCount = action . teamCount , max ;
if ( teamCount >= 1 ) {
if ( success ) {
max = Math . ceil ( teamCount / 2 ) ;
} else {
max = Math . floor ( teamCount )
}
var losses = getRandomInt ( 0 , max ) ;
this . teamSize -= losses ;
this . teamLost += losses ;
if ( this . logging . ops && losses > 0 ) {
this . log ( "Lost " + formatNumber ( losses , 0 ) + " team members during this " + action . name ) ;
}
}
var city = this . getCurrentCity ( ) ;
switch ( action . name ) {
case "Investigation" :
if ( success ) {
city . improvePopulationEstimateByPercentage ( 0.4 * this . skillMultipliers . successChanceEstimate ) ;
if ( Math . random ( ) < ( 0.02 * this . skillMultipliers . successChanceEstimate ) ) {
city . improveCommunityEstimate ( 1 ) ;
}
} else {
this . triggerPotentialMigration ( this . city , 0.1 ) ;
}
break ;
case "Undercover Operation" :
if ( success ) {
city . improvePopulationEstimateByPercentage ( 0.8 * this . skillMultipliers . successChanceEstimate ) ;
if ( Math . random ( ) < ( 0.02 * this . skillMultipliers . successChanceEstimate ) ) {
city . improveCommunityEstimate ( 1 ) ;
}
} else {
this . triggerPotentialMigration ( this . city , 0.15 ) ;
}
break ;
case "Sting Operation" :
if ( success ) {
city . changePopulationByPercentage ( - 0.1 , { changeEstEqually : true , nonZero : true } ) ;
}
city . changeChaosByCount ( 0.1 ) ;
break ;
case "Raid" :
if ( success ) {
city . changePopulationByPercentage ( - 1 , { changeEstEqually : true , nonZero : true } ) ;
-- city . comms ;
-- city . commsEst ;
} else {
2019-02-22 22:26:30 +01:00
var change = getRandomInt ( - 10 , - 5 ) / 10 ;
2018-05-02 19:38:11 +02:00
city . changePopulationByPercentage ( change , { nonZero : true } ) ;
}
city . changeChaosByPercentage ( getRandomInt ( 1 , 5 ) ) ;
break ;
case "Stealth Retirement Operation" :
if ( success ) {
city . changePopulationByPercentage ( - 0.5 , { changeEstEqually : true , nonZero : true } ) ;
}
city . changeChaosByPercentage ( getRandomInt ( - 3 , - 1 ) ) ;
break ;
case "Assassination" :
if ( success ) {
city . changePopulationByCount ( - 1 , { estChange : - 1 } ) ;
}
city . changeChaosByPercentage ( getRandomInt ( - 5 , 5 ) ) ;
break ;
default :
throw new Error ( "Invalid Action name in completeOperation: " + this . action . name ) ;
}
}
2018-06-08 17:51:48 +02:00
Bladeburner . prototype . getRecruitmentTime = function ( ) {
var effCharisma = Player . charisma * this . skillMultipliers . effCha ;
var charismaFactor = Math . pow ( effCharisma , 0.81 ) + effCharisma / 90 ;
2021-03-13 21:10:55 +01:00
return Math . max ( 10 , Math . round ( BladeburnerConstants . BaseRecruitmentTimeNeeded - charismaFactor ) ) ;
2018-06-08 17:51:48 +02:00
}
Bladeburner . prototype . getRecruitmentSuccessChance = function ( ) {
return Math . pow ( Player . charisma , 0.45 ) / ( this . teamSize + 1 ) ;
}
2019-02-14 11:02:25 +01:00
Bladeburner . prototype . getDiplomacyEffectiveness = function ( ) {
// Returns a decimal by which the city's chaos level should be multiplied (e.g. 0.98)
2019-02-26 09:29:53 +01:00
const CharismaLinearFactor = 1 e3 ;
2019-02-24 05:27:06 +01:00
const CharismaExponentialFactor = 0.045 ;
2019-02-14 11:02:25 +01:00
const charismaEff = Math . pow ( Player . charisma , CharismaExponentialFactor ) + Player . charisma / CharismaLinearFactor ;
return ( 100 - charismaEff ) / 100 ;
}
2019-04-13 03:22:46 +02:00
/ * *
* Process stat gains from Contracts , Operations , and Black Operations
* @ param action ( Action obj ) - Derived action class
* @ param success ( bool ) - Whether action was successful
* /
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . gainActionStats = function ( action , success ) {
var difficulty = action . getDifficulty ( ) ;
2019-04-13 03:22:46 +02:00
/ * *
* Gain multiplier based on difficulty . If this changes then the
* same variable calculated in completeAction ( ) needs to change too
* /
2021-03-13 21:10:55 +01:00
var difficultyMult = Math . pow ( difficulty , BladeburnerConstants . DiffMultExponentialFactor ) + difficulty / BladeburnerConstants . DiffMultLinearFactor ;
2018-05-02 19:38:11 +02:00
var time = this . actionTimeToComplete ;
var successMult = success ? 1 : 0.5 ;
2021-03-13 21:10:55 +01:00
var unweightedGain = time * BladeburnerConstants . BaseStatGain * successMult * difficultyMult ;
var unweightedIntGain = time * BladeburnerConstants . BaseIntGain * successMult * difficultyMult ;
2019-02-02 10:51:00 +01:00
const skillMult = this . skillMultipliers . expGain ;
Player . gainHackingExp ( unweightedGain * action . weights . hack * Player . hacking _exp _mult * skillMult ) ;
Player . gainStrengthExp ( unweightedGain * action . weights . str * Player . strength _exp _mult * skillMult ) ;
Player . gainDefenseExp ( unweightedGain * action . weights . def * Player . defense _exp _mult * skillMult ) ;
Player . gainDexterityExp ( unweightedGain * action . weights . dex * Player . dexterity _exp _mult * skillMult ) ;
Player . gainAgilityExp ( unweightedGain * action . weights . agi * Player . agility _exp _mult * skillMult ) ;
Player . gainCharismaExp ( unweightedGain * action . weights . cha * Player . charisma _exp _mult * skillMult ) ;
2021-04-20 03:26:51 +02:00
let intExp = unweightedIntGain * action . weights . int * skillMult ;
if ( intExp > 1 ) {
intExp = Math . pow ( intExp , 0.8 ) ;
}
Player . gainIntelligenceExp ( intExp ) ;
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . randomEvent = function ( ) {
var chance = Math . random ( ) ;
2019-04-13 03:22:46 +02:00
// Choose random source/destination city for events
2021-03-13 21:10:55 +01:00
var sourceCityName = BladeburnerConstants . CityNames [ getRandomInt ( 0 , 5 ) ] ;
2018-05-02 19:38:11 +02:00
var sourceCity = this . cities [ sourceCityName ] ;
if ( ! ( sourceCity instanceof City ) ) {
throw new Error ( "sourceCity was not a City object in Bladeburner.randomEvent()" ) ;
}
2021-03-13 21:10:55 +01:00
var destCityName = BladeburnerConstants . CityNames [ getRandomInt ( 0 , 5 ) ] ;
2018-05-02 19:38:11 +02:00
while ( destCityName === sourceCityName ) {
2021-03-13 21:10:55 +01:00
destCityName = BladeburnerConstants . CityNames [ getRandomInt ( 0 , 5 ) ] ;
2018-05-02 19:38:11 +02:00
}
var destCity = this . cities [ destCityName ] ;
if ( ! ( sourceCity instanceof City ) || ! ( destCity instanceof City ) ) {
2018-08-12 03:05:32 +02:00
throw new Error ( "sourceCity/destCity was not a City object in Bladeburner.randomEvent()" ) ;
2018-05-02 19:38:11 +02:00
}
if ( chance <= 0.05 ) {
2019-04-13 03:22:46 +02:00
// New Synthoid Community, 5%
2018-05-02 19:38:11 +02:00
++ sourceCity . comms ;
var percentage = getRandomInt ( 10 , 20 ) / 100 ;
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop += count ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that a new Synthoid community was formed in a city" ) ;
}
} else if ( chance <= 0.1 ) {
2019-04-13 03:22:46 +02:00
// Synthoid Community Migration, 5%
2018-05-02 19:38:11 +02:00
if ( sourceCity . comms <= 0 ) {
2019-04-13 03:22:46 +02:00
// If no comms in source city, then instead trigger a new Synthoid community event
2018-05-02 19:38:11 +02:00
++ sourceCity . comms ;
var percentage = getRandomInt ( 10 , 20 ) / 100 ;
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop += count ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that a new Synthoid community was formed in a city" ) ;
}
} else {
-- sourceCity . comms ;
++ destCity . comms ;
2019-04-13 03:22:46 +02:00
// Change pop
2018-05-02 19:38:11 +02:00
var percentage = getRandomInt ( 10 , 20 ) / 100 ;
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop -= count ;
destCity . pop += count ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that a Synthoid community migrated from " + sourceCityName + " to some other city" ) ;
}
}
} else if ( chance <= 0.3 ) {
2019-04-13 03:22:46 +02:00
// New Synthoids (non community), 20%
2018-05-02 19:38:11 +02:00
var percentage = getRandomInt ( 8 , 24 ) / 100 ;
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop += count ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that the Synthoid population of " + sourceCityName + " just changed significantly" ) ;
}
} else if ( chance <= 0.5 ) {
2019-04-13 03:22:46 +02:00
// Synthoid migration (non community) 20%
2018-05-02 19:38:11 +02:00
this . triggerMigration ( sourceCityName ) ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that a large number of Synthoids migrated from " + sourceCityName + " to some other city" ) ;
}
} else if ( chance <= 0.7 ) {
2019-04-13 03:22:46 +02:00
// Synthoid Riots (+chaos), 20%
2018-05-02 19:38:11 +02:00
sourceCity . chaos += 1 ;
2019-02-22 22:26:30 +01:00
sourceCity . chaos *= ( 1 + getRandomInt ( 5 , 20 ) / 100 ) ;
2018-05-02 19:38:11 +02:00
if ( this . logging . events ) {
this . log ( "Tensions between Synthoids and humans lead to riots in " + sourceCityName + "! Chaos increased" ) ;
}
} else if ( chance <= 0.9 ) {
2019-04-13 03:22:46 +02:00
// Less Synthoids, 20%
2019-02-26 09:29:53 +01:00
var percentage = getRandomInt ( 8 , 20 ) / 100 ;
2018-05-02 19:38:11 +02:00
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop -= count ;
if ( this . logging . events ) {
this . log ( "Intelligence indicates that the Synthoid population of " + sourceCityName + " just changed significantly" ) ;
}
}
2019-02-22 22:26:30 +01:00
// 10% chance of nothing happening
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . triggerPotentialMigration = function ( sourceCityName , chance ) {
if ( chance == null || isNaN ( chance ) ) {
2021-03-08 04:46:50 +01:00
console . error ( "Invalid 'chance' parameter passed into Bladeburner.triggerPotentialMigration()" ) ;
2018-05-02 19:38:11 +02:00
}
if ( chance > 1 ) { chance /= 100 ; }
if ( Math . random ( ) < chance ) { this . triggerMigration ( sourceCityName ) ; }
}
Bladeburner . prototype . triggerMigration = function ( sourceCityName ) {
2021-03-13 21:10:55 +01:00
var destCityName = BladeburnerConstants . CityNames [ getRandomInt ( 0 , 5 ) ] ;
2018-05-02 19:38:11 +02:00
while ( destCityName === sourceCityName ) {
2021-03-13 21:10:55 +01:00
destCityName = BladeburnerConstants . CityNames [ getRandomInt ( 0 , 5 ) ] ;
2018-05-02 19:38:11 +02:00
}
var destCity = this . cities [ destCityName ] ;
var sourceCity = this . cities [ sourceCityName ] ;
if ( destCity == null || sourceCity == null ) {
throw new Error ( "Failed to find City with name: " + destCityName ) ;
}
var rand = Math . random ( ) , percentage = getRandomInt ( 3 , 15 ) / 100 ;
2019-04-13 03:22:46 +02:00
if ( rand < 0.05 && sourceCity . comms > 0 ) { // 5% chance for community migration
percentage *= getRandomInt ( 2 , 4 ) ; // Migration increases population change
2018-05-02 19:38:11 +02:00
-- sourceCity . comms ;
++ destCity . comms ;
}
var count = Math . round ( sourceCity . pop * percentage ) ;
sourceCity . pop -= count ;
destCity . pop += count ;
}
2019-04-13 03:22:46 +02:00
let DomElems = { } ;
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . initializeDomElementRefs = function ( ) {
DomElems = {
bladeburnerDiv : null ,
2019-04-13 03:22:46 +02:00
// Main Divs
2018-05-02 19:38:11 +02:00
overviewConsoleParentDiv : null ,
2019-04-13 03:22:46 +02:00
overviewDiv : null , // Overview of stats that stays fixed on left
actionAndSkillsDiv : null , // Panel for different sections (contracts, ops, skills)
currentTab : null , // Contracts, Operations, Black Ops, Skills
2018-05-02 19:38:11 +02:00
consoleDiv : null ,
consoleTable : null ,
2019-04-13 03:22:46 +02:00
consoleInputRow : null , // tr
consoleInputCell : null , // td
consoleInputHeader : null , // "> "
consoleInput : null , // Actual input element
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Overview Content
2018-05-02 19:38:11 +02:00
overviewRank : null ,
overviewStamina : null ,
overviewStaminaHelpTip : null ,
2019-04-13 03:22:46 +02:00
overviewGen1 : null , // Stamina Penalty, Team, Hospitalized stats, current city
2018-05-02 19:38:11 +02:00
overviewEstPop : null ,
overviewEstPopHelpTip : null ,
overviewEstComms : null ,
overviewChaos : null ,
overviewSkillPoints : null ,
2018-07-25 05:52:53 +02:00
overviewBonusTime : null ,
2021-03-17 18:40:31 +01:00
overviewAugMults : null ,
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Actions and Skills Content
2018-05-02 19:38:11 +02:00
actionsAndSkillsDesc : null ,
2019-04-13 03:22:46 +02:00
actionsAndSkillsList : null , // ul element of all UI elements in this panel
2018-05-02 19:38:11 +02:00
generalActions : { } ,
contracts : { } ,
operations : { } ,
blackops : { } ,
skills : { } ,
2018-06-13 06:16:23 +02:00
skillPointsDisplay : null ,
2018-05-02 19:38:11 +02:00
} ;
}
Bladeburner . prototype . createContent = function ( ) {
DomElems . bladeburnerDiv = createElement ( "div" , {
id : "bladeburner-container" , position : "fixed" , class : "generic-menupage-container" ,
} ) ;
2019-04-13 03:22:46 +02:00
// Parent Div for Overview and Console
2018-05-02 19:38:11 +02:00
DomElems . overviewConsoleParentDiv = createElement ( "div" , {
height : "60%" , display : "block" , position : "relative" ,
} ) ;
2019-04-13 03:22:46 +02:00
// Overview and Action/Skill pane
2018-05-02 19:38:11 +02:00
DomElems . overviewDiv = createElement ( "div" , {
width : "30%" , display : "inline-block" , border : "1px solid white" ,
} ) ;
DomElems . actionAndSkillsDiv = createElement ( "div" , {
height : "60%" , width : "70%" , display : "block" ,
border : "1px solid white" , margin : "6px" , padding : "6px" ,
} ) ;
DomElems . currentTab = "general" ;
this . createOverviewContent ( ) ;
this . createActionAndSkillsContent ( ) ;
2019-04-13 03:22:46 +02:00
// Console
2018-05-02 19:38:11 +02:00
DomElems . consoleDiv = createElement ( "div" , {
class : "bladeburner-console-div" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
if ( DomElems . consoleInput instanceof Element ) {
DomElems . consoleInput . focus ( ) ;
}
return false ;
}
} ) ;
DomElems . consoleTable = createElement ( "table" , { class : "bladeburner-console-table" } ) ;
2021-03-31 06:45:21 +02:00
DomElems . consoleInputRow = createElement ( "tr" , { class : "bladeburner-console-input-row" , id : "bladeburner-console-input-row" } ) ;
2018-05-02 19:38:11 +02:00
DomElems . consoleInputCell = createElement ( "td" , { class : "bladeburner-console-input-cell" } ) ;
DomElems . consoleInputHeader = createElement ( "pre" , { innerText : "> " } ) ;
DomElems . consoleInput = createElement ( "input" , {
type : "text" , class : "bladeburner-console-input" , tabIndex : 1 ,
2021-03-31 06:45:21 +02:00
onfocus : ( ) => { DomElems . consoleInput . value = DomElems . consoleInput . value }
2018-05-02 19:38:11 +02:00
} ) ;
DomElems . consoleInputCell . appendChild ( DomElems . consoleInputHeader ) ;
DomElems . consoleInputCell . appendChild ( DomElems . consoleInput ) ;
DomElems . consoleInputRow . appendChild ( DomElems . consoleInputCell ) ;
DomElems . consoleTable . appendChild ( DomElems . consoleInputRow ) ;
DomElems . consoleDiv . appendChild ( DomElems . consoleTable ) ;
DomElems . overviewConsoleParentDiv . appendChild ( DomElems . overviewDiv ) ;
DomElems . overviewConsoleParentDiv . appendChild ( DomElems . consoleDiv ) ;
DomElems . bladeburnerDiv . appendChild ( DomElems . overviewConsoleParentDiv ) ;
DomElems . bladeburnerDiv . appendChild ( DomElems . actionAndSkillsDiv ) ;
2019-03-23 03:32:36 +01:00
// legend
const legend = createElement ( "div" )
legend . innerHTML = ` <span class="text"> ${ stealthIcon } = This action requires stealth, ${ killIcon } = This action involves retirement</span> `
DomElems . bladeburnerDiv . appendChild ( legend ) ;
2018-05-02 19:38:11 +02:00
document . getElementById ( "entire-game-container" ) . appendChild ( DomElems . bladeburnerDiv ) ;
2018-11-04 23:39:30 +01:00
if ( this . consoleLogs . length === 0 ) {
this . postToConsole ( "Bladeburner Console BETA" ) ;
this . postToConsole ( "Type 'help' to see console commands" ) ;
} else {
for ( let i = 0 ; i < this . consoleLogs . length ; ++ i ) {
this . postToConsole ( this . consoleLogs [ i ] , false ) ;
}
2018-11-01 19:18:32 +01:00
}
2018-11-04 23:39:30 +01:00
2018-05-02 19:38:11 +02:00
DomElems . consoleInput . focus ( ) ;
}
Bladeburner . prototype . clearContent = function ( ) {
if ( DomElems . bladeburnerDiv instanceof Element ) {
removeChildrenFromElement ( DomElems . bladeburnerDiv ) ;
removeElement ( DomElems . bladeburnerDiv ) ;
}
clearObject ( DomElems ) ;
this . initializeDomElementRefs ( ) ;
}
Bladeburner . prototype . createOverviewContent = function ( ) {
if ( DomElems . overviewDiv == null ) {
throw new Error ( "Bladeburner.createOverviewContent() called with DomElems.overviewDiv = null" ) ;
}
DomElems . overviewRank = createElement ( "p" , {
innerText : "Rank: " ,
display : "inline-block" ,
tooltip : "Your rank within the Bladeburner division" ,
} ) ;
DomElems . overviewStamina = createElement ( "p" , {
display : "inline-block" ,
} ) ;
DomElems . overviewStaminaHelpTip = createElement ( "div" , {
2018-10-23 20:55:42 +02:00
class : "help-tip" ,
innerText : "?" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
dialogBoxCreate ( "Performing actions will use up your stamina.<br><br>" +
"Your max stamina is determined primarily by your agility stat.<br><br>" +
"Your stamina gain rate is determined by both your agility and your " +
"max stamina. Higher max stamina leads to a higher gain rate.<br><br>" +
"Once your " +
"stamina falls below 50% of its max value, it begins to negatively " +
"affect the success rate of your contracts/operations. This penalty " +
"is shown in the overview panel. If the penalty is 15%, then this means " +
"your success rate would be multipled by 85% (100 - 15).<br><br>" +
"Your max stamina and stamina gain rate can also be increased by " +
"training, or through skills and Augmentation upgrades." ) ;
2018-10-23 20:55:42 +02:00
} ,
2018-05-02 19:38:11 +02:00
} ) ;
DomElems . overviewGen1 = createElement ( "p" , {
display : "block" ,
} ) ;
DomElems . overviewEstPop = createElement ( "p" , {
innerText : "Est. Synthoid Population: " ,
display : "inline-block" ,
tooltip : "This is your Bladeburner division's estimate of how many Synthoids exist " +
"in your current city."
} ) ;
DomElems . overviewEstPopHelpTip = createElement ( "div" , {
innerText : "?" , class : "help-tip" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
dialogBoxCreate ( "The success rate of your contracts/operations depends on " +
"the population of Synthoids in your current city. " +
"The success rate that is shown to you is only an estimate, " +
"and it is based on your Synthoid population estimate.<br><br>" +
"Therefore, it is important that this Synthoid population estimate " +
"is accurate so that you have a better idea of your " +
"success rate for contracts/operations. Certain " +
"actions will increase the accuracy of your population " +
"estimate.<br><br>" +
"The Synthoid populations of cities can change due to your " +
"actions or random events. If random events occur, they will " +
"be logged in the Bladeburner Console." ) ;
}
} ) ;
DomElems . overviewEstComms = createElement ( "p" , {
innerText : "Est. Synthoid Communities: " ,
display : "inline-block" ,
tooltip : "This is your Bladeburner divison's estimate of how many Synthoid " +
"communities exist in your current city." ,
} ) ;
DomElems . overviewChaos = createElement ( "p" , {
innerText : "City Chaos: " ,
display : "inline-block" ,
tooltip : "The city's chaos level due to tensions and conflicts between humans and Synthoids. " +
"Having too high of a chaos level can make contracts and operations harder."
} ) ;
2018-07-25 05:52:53 +02:00
DomElems . overviewBonusTime = createElement ( "p" , {
innerText : "Bonus time: " ,
display : "inline-block" ,
2018-08-05 09:37:28 +02:00
tooltip : "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser). " +
2018-10-23 20:55:42 +02:00
"Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed."
2018-07-25 05:52:53 +02:00
} ) ;
2018-05-02 19:38:11 +02:00
DomElems . overviewSkillPoints = createElement ( "p" , { display : "block" } ) ;
2018-09-06 18:07:59 +02:00
2018-05-02 19:38:11 +02:00
2021-03-17 18:40:31 +01:00
DomElems . overviewAugMults = createElement ( "div" , { display : "block" } ) ;
2018-05-02 19:38:11 +02:00
DomElems . overviewDiv . appendChild ( DomElems . overviewRank ) ;
appendLineBreaks ( DomElems . overviewDiv , 1 ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewStamina ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewStaminaHelpTip ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewGen1 ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewEstPop ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewEstPopHelpTip ) ;
appendLineBreaks ( DomElems . overviewDiv , 1 ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewEstComms ) ;
appendLineBreaks ( DomElems . overviewDiv , 1 ) ;
DomElems . overviewDiv . appendChild ( DomElems . overviewChaos ) ;
appendLineBreaks ( DomElems . overviewDiv , 2 ) ;
2018-07-25 05:52:53 +02:00
DomElems . overviewDiv . appendChild ( DomElems . overviewBonusTime ) ;
2018-05-02 19:38:11 +02:00
DomElems . overviewDiv . appendChild ( DomElems . overviewSkillPoints ) ;
appendLineBreaks ( DomElems . overviewDiv , 1 ) ;
2021-03-17 18:40:31 +01:00
DomElems . overviewDiv . appendChild ( DomElems . overviewAugMults ) ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// Travel to new city button
2018-05-02 19:38:11 +02:00
appendLineBreaks ( DomElems . overviewDiv , 1 ) ;
DomElems . overviewDiv . appendChild ( createElement ( "a" , {
innerHTML : "Travel" , class : "a-link-button" , display : "inline-block" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
var popupId = "bladeburner-travel-popup-cancel-btn" ;
var popupArguments = [ ] ;
2019-04-13 03:22:46 +02:00
popupArguments . push ( createElement ( "a" , { // Cancel Button
2018-05-02 19:38:11 +02:00
innerText : "Cancel" , class : "a-link-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
removeElementById ( popupId ) ; return false ;
}
} ) )
2019-04-13 03:22:46 +02:00
popupArguments . push ( createElement ( "p" , { // Info Text
2018-05-02 19:38:11 +02:00
innerText : "Travel to a different city for your Bladeburner " +
"activities. This does not cost any money. The city you are " +
"in for your Bladeburner duties does not affect " +
"your location in the game otherwise" ,
} ) ) ;
2021-03-13 21:10:55 +01:00
for ( var i = 0 ; i < BladeburnerConstants . CityNames . length ; ++ i ) {
2018-05-02 19:38:11 +02:00
( function ( inst , i ) {
popupArguments . push ( createElement ( "div" , {
2019-04-13 03:22:46 +02:00
/ * *
* Reusing this css class ... it adds a border and makes it
* so that background color changes when you hover
* /
2018-05-02 19:38:11 +02:00
class : "cmpy-mgmt-find-employee-option" ,
2021-03-13 21:10:55 +01:00
innerText : BladeburnerConstants . CityNames [ i ] ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2021-03-13 21:10:55 +01:00
inst . city = BladeburnerConstants . CityNames [ i ] ;
2018-05-02 19:38:11 +02:00
removeElementById ( popupId ) ;
inst . updateOverviewContent ( ) ;
return false ;
}
} ) ) ;
} ) ( this , i ) ;
}
createPopup ( popupId , popupArguments ) ;
}
} ) ) ;
2019-04-13 03:22:46 +02:00
// Faction button
2018-06-13 06:16:23 +02:00
const bladeburnersFactionName = "Bladeburners" ;
2018-05-02 19:38:11 +02:00
if ( factionExists ( bladeburnersFactionName ) ) {
var bladeburnerFac = Factions [ bladeburnersFactionName ] ;
if ( ! ( bladeburnerFac instanceof Faction ) ) {
throw new Error ( "Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button" ) ;
}
DomElems . overviewDiv . appendChild ( createElement ( "a" , {
innerText : "Faction" , class : "a-link-button" , display : "inline-block" ,
tooltip : "Apply to the Bladeburner Faction, or go to the faction page if you are already a member" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
if ( bladeburnerFac . isMember ) {
Engine . loadFactionContent ( ) ;
displayFactionContent ( bladeburnersFactionName ) ;
} else {
2021-03-13 21:10:55 +01:00
if ( this . rank >= BladeburnerConstants . RankNeededForFaction ) {
2018-05-02 19:38:11 +02:00
joinFaction ( bladeburnerFac ) ;
dialogBoxCreate ( "Congratulations! You were accepted into the Bladeburners faction" ) ;
removeChildrenFromElement ( DomElems . overviewDiv ) ;
this . createOverviewContent ( ) ;
} else {
dialogBoxCreate ( "You need a rank of 25 to join the Bladeburners Faction!" )
}
}
return false ;
}
} ) ) ;
}
DomElems . overviewDiv . appendChild ( createElement ( "br" ) ) ;
DomElems . overviewDiv . appendChild ( createElement ( "br" ) ) ;
this . updateOverviewContent ( ) ;
}
Bladeburner . prototype . createActionAndSkillsContent = function ( ) {
if ( DomElems . currentTab == null ) { DomElems . currentTab = "general" ; }
removeChildrenFromElement ( DomElems . actionAndSkillsDiv ) ;
clearObject ( DomElems . generalActions ) ;
clearObject ( DomElems . contracts ) ;
clearObject ( DomElems . operations ) ;
clearObject ( DomElems . blackops ) ;
clearObject ( DomElems . skills ) ;
//Navigation buttons
var currTab = DomElems . currentTab . toLowerCase ( ) ;
var buttons = [ "General" , "Contracts" , "Operations" , "BlackOps" , "Skills" ] ;
for ( var i = 0 ; i < buttons . length ; ++ i ) {
( function ( buttons , i , inst , currTab ) {
DomElems . actionAndSkillsDiv . appendChild ( createElement ( "a" , {
innerText : buttons [ i ] ,
class : currTab === buttons [ i ] . toLowerCase ( ) ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
DomElems . currentTab = buttons [ i ] . toLowerCase ( ) ;
inst . createActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
} ) ( buttons , i , this , currTab ) ;
}
2019-04-13 03:22:46 +02:00
// General info/description for each action
2018-05-02 19:38:11 +02:00
DomElems . actionsAndSkillsDesc = createElement ( "p" , {
display : "block" , margin : "4px" , padding : "4px"
} ) ;
2019-04-13 03:22:46 +02:00
// List for actions/skills
2018-05-02 19:38:11 +02:00
removeChildrenFromElement ( DomElems . actionsAndSkillsList ) ;
DomElems . actionsAndSkillsList = createElement ( "ul" ) ;
switch ( currTab ) {
case "general" :
this . createGeneralActionsContent ( ) ;
break ;
case "contracts" :
this . createContractsContent ( ) ;
break ;
case "operations" :
this . createOperationsContent ( ) ;
break ;
case "blackops" :
this . createBlackOpsContent ( ) ;
break ;
case "skills" :
this . createSkillsContent ( ) ;
break ;
default :
throw new Error ( "Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent" ) ;
}
this . updateContent ( ) ;
DomElems . actionAndSkillsDiv . appendChild ( DomElems . actionsAndSkillsDesc ) ;
DomElems . actionAndSkillsDiv . appendChild ( DomElems . actionsAndSkillsList ) ;
}
Bladeburner . prototype . createGeneralActionsContent = function ( ) {
if ( DomElems . actionsAndSkillsList == null || DomElems . actionsAndSkillsDesc == null ) {
throw new Error ( "Bladeburner.createGeneralActionsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null" ) ;
}
DomElems . actionsAndSkillsDesc . innerText =
"These are generic actions that will assist you in your Bladeburner " +
"duties. They will not affect your Bladeburner rank in any way."
for ( var actionName in GeneralActions ) {
if ( GeneralActions . hasOwnProperty ( actionName ) ) {
DomElems . generalActions [ actionName ] = createElement ( "div" , {
class : "bladeburner-action" , name : actionName
} ) ;
DomElems . actionsAndSkillsList . appendChild ( DomElems . generalActions [ actionName ] ) ;
}
}
}
Bladeburner . prototype . createContractsContent = function ( ) {
if ( DomElems . actionsAndSkillsList == null || DomElems . actionsAndSkillsDesc == null ) {
throw new Error ( "Bladeburner.createContractsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null" ) ;
}
DomElems . actionsAndSkillsDesc . innerHTML =
2018-07-22 15:20:13 +02:00
"Complete contracts in order to increase your Bladeburner rank and earn money. " +
2018-05-02 19:38:11 +02:00
"Failing a contract will cause you to lose HP, which can lead to hospitalization.<br><br>" +
"You can unlock higher-level contracts by successfully completing them. " +
2018-05-23 02:09:04 +02:00
"Higher-level contracts are more difficult, but grant more rank, experience, and money." ;
2018-05-02 19:38:11 +02:00
for ( var contractName in this . contracts ) {
if ( this . contracts . hasOwnProperty ( contractName ) ) {
DomElems . contracts [ contractName ] = createElement ( "div" , {
class : "bladeburner-action" , name : contractName
} ) ;
DomElems . actionsAndSkillsList . appendChild ( DomElems . contracts [ contractName ] ) ;
}
}
}
Bladeburner . prototype . createOperationsContent = function ( ) {
if ( DomElems . actionsAndSkillsList == null || DomElems . actionsAndSkillsDesc == null ) {
throw new Error ( "Bladeburner.createOperationsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null" ) ;
}
DomElems . actionsAndSkillsDesc . innerHTML =
"Carry out operations for the Bladeburner division. " +
"Failing an operation will reduce your Bladeburner rank. It will also " +
"cause you to lose HP, which can lead to hospitalization. In general, " +
"operations are harder and more punishing than contracts, " +
"but are also more rewarding.<br><br>" +
"Operations can affect the chaos level and Synthoid population of your " +
"current city. The exact effects vary between different Operations.<br><br>" +
"For operations, you can use a team. You must first recruit team members. " +
"Having a larger team will improves your chances of success.<br><br>" +
"You can unlock higher-level operations by successfully completing them. " +
"Higher-level operations are more difficult, but grant more rank and experience." ;
for ( var operationName in this . operations ) {
if ( this . operations . hasOwnProperty ( operationName ) ) {
DomElems . operations [ operationName ] = createElement ( "div" , {
class : "bladeburner-action" , name : operationName
} ) ;
DomElems . actionsAndSkillsList . appendChild ( DomElems . operations [ operationName ] ) ;
}
}
}
Bladeburner . prototype . createBlackOpsContent = function ( ) {
2021-03-13 21:10:55 +01:00
2018-05-02 19:38:11 +02:00
if ( DomElems . actionsAndSkillsList == null || DomElems . actionsAndSkillsDesc == null ) {
throw new Error ( "Bladeburner.createBlackOpsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null" ) ;
}
2021-03-13 21:10:55 +01:00
2018-05-02 19:38:11 +02:00
DomElems . actionsAndSkillsDesc . innerHTML =
"Black Operations (Black Ops) are special, one-time covert operations. " +
"Each Black Op must be unlocked successively by completing " +
"the one before it.<br><br>" +
2018-11-19 10:03:38 +01:00
"<b>Your ultimate goal to climb through the ranks of Bladeburners is to complete " +
2018-12-07 11:54:26 +01:00
"all of the Black Ops.</b><br><br>" +
2018-05-02 19:38:11 +02:00
"Like normal operations, you may use a team for Black Ops. Failing " +
"a black op will incur heavy HP and rank losses." ;
2019-04-13 03:22:46 +02:00
// Put Black Operations in sequence of required rank
2018-05-02 19:38:11 +02:00
var blackops = [ ] ;
for ( var blackopName in BlackOperations ) {
if ( BlackOperations . hasOwnProperty ( blackopName ) ) {
blackops . push ( BlackOperations [ blackopName ] ) ;
}
}
blackops . sort ( function ( a , b ) {
return ( a . reqdRank - b . reqdRank ) ;
} ) ;
2019-03-13 18:51:34 +01:00
for ( var i = blackops . length - 1 ; i >= 0 ; -- i ) {
if ( this . blackops [ [ blackops [ i ] . name ] ] == null && i !== 0 && this . blackops [ [ blackops [ i - 1 ] . name ] ] == null ) { continue ; } // If this one nor the next are completed then this isn't unlocked yet.
2018-05-02 19:38:11 +02:00
DomElems . blackops [ blackops [ i ] . name ] = createElement ( "div" , {
class : "bladeburner-action" , name : blackops [ i ] . name
} ) ;
DomElems . actionsAndSkillsList . appendChild ( DomElems . blackops [ blackops [ i ] . name ] ) ;
}
}
Bladeburner . prototype . createSkillsContent = function ( ) {
if ( DomElems . actionsAndSkillsList == null || DomElems . actionsAndSkillsDesc == null ) {
throw new Error ( "Bladeburner.createSkillsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null" ) ;
}
2019-04-13 03:22:46 +02:00
// Display Current multipliers
2018-05-02 19:38:11 +02:00
DomElems . actionsAndSkillsDesc . innerHTML =
2021-03-13 21:10:55 +01:00
"You will gain one skill point every " + BladeburnerConstants . RanksPerSkillPoint + " ranks.<br><br>" +
2018-05-02 19:38:11 +02:00
"Note that when upgrading a skill, the benefit for that skill is additive. " +
"However, the effects of different skills with each other is multiplicative.<br><br>"
var multKeys = Object . keys ( this . skillMultipliers ) ;
for ( var i = 0 ; i < multKeys . length ; ++ i ) {
var mult = this . skillMultipliers [ multKeys [ i ] ] ;
if ( mult && mult !== 1 ) {
mult = formatNumber ( mult , 3 ) ;
switch ( multKeys [ i ] ) {
case "successChanceAll" :
DomElems . actionsAndSkillsDesc . innerHTML += "Total Success Chance: x" + mult + "<br>" ;
break ;
case "successChanceStealth" :
DomElems . actionsAndSkillsDesc . innerHTML += "Stealth Success Chance: x" + mult + "<br>" ;
break ;
case "successChanceKill" :
DomElems . actionsAndSkillsDesc . innerHTML += "Retirement Success Chance: x" + mult + "<br>" ;
break ;
case "successChanceContract" :
DomElems . actionsAndSkillsDesc . innerHTML += "Contract Success Chance: x" + mult + "<br>" ;
break ;
case "successChanceOperation" :
DomElems . actionsAndSkillsDesc . innerHTML += "Operation Success Chance: x" + mult + "<br>" ;
break ;
case "successChanceEstimate" :
DomElems . actionsAndSkillsDesc . innerHTML += "Synthoid Data Estimate: x" + mult + "<br>" ;
break ;
case "actionTime" :
DomElems . actionsAndSkillsDesc . innerHTML += "Action Time: x" + mult + "<br>" ;
break ;
case "effHack" :
DomElems . actionsAndSkillsDesc . innerHTML += "Hacking Skill: x" + mult + "<br>" ;
break ;
case "effStr" :
DomElems . actionsAndSkillsDesc . innerHTML += "Strength: x" + mult + "<br>" ;
break ;
case "effDef" :
DomElems . actionsAndSkillsDesc . innerHTML += "Defense: x" + mult + "<br>" ;
break ;
case "effDex" :
DomElems . actionsAndSkillsDesc . innerHTML += "Dexterity: x" + mult + "<br>" ;
break ;
case "effAgi" :
DomElems . actionsAndSkillsDesc . innerHTML += "Agility: x" + mult + "<br>" ;
break ;
case "effCha" :
DomElems . actionsAndSkillsDesc . innerHTML += "Charisma: x" + mult + "<br>" ;
break ;
case "effInt" :
DomElems . actionsAndSkillsDesc . innerHTML += "Intelligence: x" + mult + "<br>" ;
break ;
case "stamina" :
DomElems . actionsAndSkillsDesc . innerHTML += "Stamina: x" + mult + "<br>" ;
break ;
2019-02-11 09:21:14 +01:00
case "money" :
DomElems . actionsAndSkillsDesc . innerHTML += "Contract Money: x" + mult + "<br>" ;
break ;
case "expGain" :
DomElems . actionsAndSkillsDesc . innerHTML += "Exp Gain: x" + mult + "<br>" ;
break ;
2018-05-02 19:38:11 +02:00
default :
2021-03-08 04:46:50 +01:00
console . warn ( ` Unrecognized SkillMult Key: ${ multKeys [ i ] } ` ) ;
2018-05-02 19:38:11 +02:00
break ;
}
}
}
2019-04-13 03:22:46 +02:00
// Skill Points
2018-06-13 06:16:23 +02:00
DomElems . skillPointsDisplay = createElement ( "p" , {
2018-05-02 19:38:11 +02:00
innerHTML : "<br><strong>Skill Points: " + formatNumber ( this . skillPoints , 0 ) + "</strong>"
2018-06-13 06:16:23 +02:00
} ) ;
DomElems . actionAndSkillsDiv . appendChild ( DomElems . skillPointsDisplay ) ;
2018-05-02 19:38:11 +02:00
2019-04-13 03:22:46 +02:00
// UI Element for each skill
2018-05-02 19:38:11 +02:00
for ( var skillName in Skills ) {
if ( Skills . hasOwnProperty ( skillName ) ) {
DomElems . skills [ skillName ] = createElement ( "div" , {
class : "bladeburner-action" , name : skillName
} ) ;
DomElems . actionsAndSkillsList . appendChild ( DomElems . skills [ skillName ] ) ;
}
}
}
Bladeburner . prototype . updateContent = function ( ) {
this . updateOverviewContent ( ) ;
this . updateActionAndSkillsContent ( ) ;
}
Bladeburner . prototype . updateOverviewContent = function ( ) {
2018-07-20 03:51:18 +02:00
if ( ! routing . isOn ( Page . Bladeburner ) ) { return ; }
2018-05-02 19:38:11 +02:00
DomElems . overviewRank . childNodes [ 0 ] . nodeValue = "Rank: " + formatNumber ( this . rank , 2 ) ;
DomElems . overviewStamina . innerText = "Stamina: " + formatNumber ( this . stamina , 3 ) + " / " + formatNumber ( this . maxStamina , 3 ) ;
2021-03-31 06:45:21 +02:00
ReactDOM . render ( < >
Stamina Penalty : { formatNumber ( ( 1 - this . calculateStaminaPenalty ( ) ) * 100 , 1 ) } % < br / > < br / >
Team Size : { formatNumber ( this . teamSize , 0 ) } < br / >
Team Members Lost : { formatNumber ( this . teamLost , 0 ) } < br / > < br / >
2021-04-06 09:50:09 +02:00
Num Times Hospitalized : { this . numHosp } < br / >
2021-03-31 06:45:21 +02:00
Money Lost From Hospitalizations : { Money ( this . moneyLost ) } < br / > < br / >
Current City : { this . city } < br / >
< / > , DomElems . overviewGen1 ) ;
DomElems . overviewEstPop . childNodes [ 0 ] . nodeValue = "Est. Synthoid Population: " + numeralWrapper . formatPopulation ( this . getCurrentCity ( ) . popEst ) ;
2018-05-02 19:38:11 +02:00
DomElems . overviewEstComms . childNodes [ 0 ] . nodeValue = "Est. Synthoid Communities: " + formatNumber ( this . getCurrentCity ( ) . comms , 0 ) ;
DomElems . overviewChaos . childNodes [ 0 ] . nodeValue = "City Chaos: " + formatNumber ( this . getCurrentCity ( ) . chaos ) ;
DomElems . overviewSkillPoints . innerText = "Skill Points: " + formatNumber ( this . skillPoints , 0 ) ;
2021-03-13 21:10:55 +01:00
DomElems . overviewBonusTime . childNodes [ 0 ] . nodeValue = "Bonus time: " + convertTimeMsToTimeElapsedString ( this . storedCycles / BladeburnerConstants . CyclesPerSecond * 1000 ) ;
2021-03-17 18:40:31 +01:00
ReactDOM . render ( StatsTable ( [
[ "Aug. Success Chance mult: " , formatNumber ( Player . bladeburner _success _chance _mult * 100 , 1 ) + "%" ] ,
[ "Aug. Max Stamina mult: " , formatNumber ( Player . bladeburner _max _stamina _mult * 100 , 1 ) + "%" ] ,
[ "Aug. Stamina Gain mult: " , formatNumber ( Player . bladeburner _stamina _gain _mult * 100 , 1 ) + "%" ] ,
[ "Aug. Field Analysis mult: " , formatNumber ( Player . bladeburner _analysis _mult * 100 , 1 ) + "%" ] ,
] ) , DomElems . overviewAugMults ) ;
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . updateActionAndSkillsContent = function ( ) {
if ( DomElems . currentTab == null ) { DomElems . currentTab = "general" ; }
switch ( DomElems . currentTab . toLowerCase ( ) ) {
case "general" :
var actionElems = Object . keys ( DomElems . generalActions ) ;
for ( var i = 0 ; i < actionElems . length ; ++ i ) {
var actionElem = DomElems . generalActions [ actionElems [ i ] ] ;
var name = actionElem . name ;
var actionObj = GeneralActions [ name ] ;
if ( actionObj == null ) {
throw new Error ( "Could not find Object " + name + " in Bladeburner.updateActionAndSkillsContent()" ) ;
}
if ( this . action . type === ActionTypes [ name ] ) {
actionElem . classList . add ( ActiveActionCssClass ) ;
} else {
actionElem . classList . remove ( ActiveActionCssClass ) ;
}
this . updateGeneralActionsUIElement ( actionElem , actionObj ) ;
}
break ;
case "contracts" :
var contractElems = Object . keys ( DomElems . contracts ) ;
for ( var i = 0 ; i < contractElems . length ; ++ i ) {
var contractElem = DomElems . contracts [ contractElems [ i ] ] ;
var name = contractElem . name ;
if ( this . action . type === ActionTypes [ "Contract" ] && name === this . action . name ) {
contractElem . classList . add ( ActiveActionCssClass ) ;
} else {
contractElem . classList . remove ( ActiveActionCssClass ) ;
}
var contract = this . contracts [ name ] ;
if ( contract == null ) {
throw new Error ( "Could not find Contract " + name + " in Bladeburner.updateActionAndSkillsContent()" ) ;
}
this . updateContractsUIElement ( contractElem , contract ) ;
}
break ;
case "operations" :
var operationElems = Object . keys ( DomElems . operations ) ;
for ( var i = 0 ; i < operationElems . length ; ++ i ) {
var operationElem = DomElems . operations [ operationElems [ i ] ] ;
var name = operationElem . name ;
if ( this . action . type === ActionTypes [ "Operation" ] && name === this . action . name ) {
operationElem . classList . add ( ActiveActionCssClass ) ;
} else {
operationElem . classList . remove ( ActiveActionCssClass ) ;
}
var operation = this . operations [ name ] ;
if ( operation == null ) {
throw new Error ( "Could not find Operation " + name + " in Bladeburner.updateActionAndSkillsContent()" ) ;
}
this . updateOperationsUIElement ( operationElem , operation ) ;
}
break ;
case "blackops" :
var blackopsElems = Object . keys ( DomElems . blackops ) ;
for ( var i = 0 ; i < blackopsElems . length ; ++ i ) {
var blackopElem = DomElems . blackops [ blackopsElems [ i ] ] ;
var name = blackopElem . name ;
if ( this . action . type === ActionTypes [ "BlackOperation" ] && name === this . action . name ) {
blackopElem . classList . add ( ActiveActionCssClass ) ;
} else {
blackopElem . classList . remove ( ActiveActionCssClass ) ;
}
var blackop = BlackOperations [ name ] ;
if ( blackop == null ) {
throw new Error ( "Could not find BlackOperation " + name + " in Bladeburner.updateActionAndSkillsContent()" ) ;
}
this . updateBlackOpsUIElement ( blackopElem , blackop ) ;
}
break ;
case "skills" :
2018-06-13 06:16:23 +02:00
DomElems . skillPointsDisplay . innerHTML = "<br><strong>Skill Points: " + formatNumber ( this . skillPoints , 0 ) + "</strong>" ;
2018-05-02 19:38:11 +02:00
var skillElems = Object . keys ( DomElems . skills ) ;
for ( var i = 0 ; i < skillElems . length ; ++ i ) {
var skillElem = DomElems . skills [ skillElems [ i ] ] ;
var name = skillElem . name ;
var skill = Skills [ name ] ;
if ( skill == null ) {
throw new Error ( "Could not find Skill " + name + " in Bladeburner.updateActionAndSkillsContent()" ) ;
}
this . updateSkillsUIElement ( skillElem , skill ) ;
}
break ;
default :
throw new Error ( "Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent" ) ;
}
}
Bladeburner . prototype . updateGeneralActionsUIElement = function ( el , action ) {
removeChildrenFromElement ( el ) ;
var isActive = el . classList . contains ( ActiveActionCssClass ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "h2" , { // Header
2018-05-02 19:38:11 +02:00
innerText : isActive ? action . name + " (IN PROGRESS - " +
formatNumber ( this . actionTimeCurrent , 0 ) + " / " +
formatNumber ( this . actionTimeToComplete , 0 ) + ")"
: action . name ,
display : "inline-block" ,
} ) ) ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { // Progress bar if its active
2018-05-02 19:38:11 +02:00
var progress = this . actionTimeCurrent / this . actionTimeToComplete ;
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : createProgressBarText ( { progress : progress } )
} ) ) ;
} else {
2019-04-13 03:22:46 +02:00
// Start button
2018-05-02 19:38:11 +02:00
el . appendChild ( createElement ( "a" , {
innerText : "Start" , class : "a-link-button" ,
margin : "3px" , padding : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
this . action . type = ActionTypes [ action . name ] ;
this . action . name = action . name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
}
appendLineBreaks ( el , 2 ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "pre" , { // Info
2018-05-02 19:38:11 +02:00
innerHTML : action . desc , display : "inline-block"
} ) ) ;
}
Bladeburner . prototype . updateContractsUIElement = function ( el , action ) {
removeChildrenFromElement ( el ) ;
var isActive = el . classList . contains ( ActiveActionCssClass ) ;
var estimatedSuccessChance = action . getSuccessChance ( this , { est : true } ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "h2" , { // Header
2018-05-02 19:38:11 +02:00
innerText : isActive ? action . name + " (IN PROGRESS - " +
formatNumber ( this . actionTimeCurrent , 0 ) + " / " +
formatNumber ( this . actionTimeToComplete , 0 ) + ")"
: action . name ,
display : "inline-block"
} ) ) ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { // Progress bar if its active
2018-05-02 19:38:11 +02:00
var progress = this . actionTimeCurrent / this . actionTimeToComplete ;
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : createProgressBarText ( { progress : progress } )
} ) ) ;
2019-04-13 03:22:46 +02:00
} else { // Start button
2018-05-02 19:38:11 +02:00
el . appendChild ( createElement ( "a" , {
innerText : "Start" , class : "a-link-button" ,
padding : "3px" , margin : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
this . action . type = ActionTypes . Contract ;
this . action . name = action . name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
}
2019-04-13 03:22:46 +02:00
// Level and buttons to change level
2018-05-02 19:38:11 +02:00
var maxLevel = ( action . level >= action . maxLevel ) ;
appendLineBreaks ( el , 2 ) ;
el . appendChild ( createElement ( "pre" , {
display : "inline-block" ,
2018-06-08 17:51:48 +02:00
innerText : "Level: " + action . level + " / " + action . maxLevel ,
2021-03-13 21:10:55 +01:00
tooltip : action . getSuccessesNeededForNextLevel ( BladeburnerConstants . ContractSuccessesPerLevel ) + " successes " +
2018-06-08 17:51:48 +02:00
"needed for next level"
2018-05-02 19:38:11 +02:00
} ) ) ;
el . appendChild ( createElement ( "a" , {
class : maxLevel ? "a-link-button-inactive" : "a-link-button" , innerHTML : "↑" ,
padding : "2px" , margin : "2px" ,
tooltip : isActive ? "WARNING: changing the level will restart the contract" : "" ,
display : "inline" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
++ action . level ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { this . startAction ( this . action ) ; } // Restart Action
2018-05-02 19:38:11 +02:00
this . updateContractsUIElement ( el , action ) ;
return false ;
}
} ) ) ;
el . appendChild ( createElement ( "a" , {
class : ( action . level <= 1 ) ? "a-link-button-inactive" : "a-link-button" , innerHTML : "↓" ,
padding : "2px" , margin : "2px" ,
tooltip : isActive ? "WARNING: changing the level will restart the contract" : "" ,
display : "inline" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
-- action . level ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { this . startAction ( this . action ) ; } // Restart Action
2018-05-02 19:38:11 +02:00
this . updateContractsUIElement ( el , action ) ;
return false ;
}
} ) ) ;
var actionTime = action . getActionTime ( this ) ;
appendLineBreaks ( el , 2 ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "pre" , { // Info
2018-05-02 19:38:11 +02:00
display : "inline-block" ,
innerHTML : action . desc + "\n\n" +
2019-03-23 00:54:14 +01:00
` Estimated success chance: ${ formatNumber ( estimatedSuccessChance * 100 , 1 ) } % ${ action . isStealth ? stealthIcon : '' } ${ action . isKill ? killIcon : '' } \ n ` +
2021-03-31 06:45:21 +02:00
"Time Required: " + convertTimeMsToTimeElapsedString ( actionTime * 1000 ) + "\n" +
2018-05-02 19:38:11 +02:00
"Contracts remaining: " + Math . floor ( action . count ) + "\n" +
"Successes: " + action . successes + "\n" +
"Failures: " + action . failures ,
} ) ) ;
2019-04-13 03:22:46 +02:00
// Autolevel Checkbox
2018-05-02 19:38:11 +02:00
el . appendChild ( createElement ( "br" ) ) ;
var autolevelCheckboxId = "bladeburner-" + action . name + "-autolevel-checkbox" ;
el . appendChild ( createElement ( "label" , {
2021-03-12 21:56:25 +01:00
for : autolevelCheckboxId , innerText : "Autolevel: " , color : "white" ,
2018-05-02 19:38:11 +02:00
tooltip : "Automatically increase contract level when possible"
} ) ) ;
2019-03-23 03:20:07 +01:00
const checkboxInput = createElement ( "input" , {
type : "checkbox" ,
id : autolevelCheckboxId ,
checked : action . autoLevel ,
changeListener : ( ) => {
action . autoLevel = checkboxInput . checked ;
} ,
2018-05-02 19:38:11 +02:00
} ) ;
2019-03-23 03:20:07 +01:00
2021-03-12 21:56:25 +01:00
el . appendChild ( checkboxInput ) ;
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . updateOperationsUIElement = function ( el , action ) {
removeChildrenFromElement ( el ) ;
var isActive = el . classList . contains ( ActiveActionCssClass ) ;
var estimatedSuccessChance = action . getSuccessChance ( this , { est : true } ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "h2" , { // Header
2018-05-02 19:38:11 +02:00
innerText : isActive ? action . name + " (IN PROGRESS - " +
formatNumber ( this . actionTimeCurrent , 0 ) + " / " +
formatNumber ( this . actionTimeToComplete , 0 ) + ")"
: action . name ,
display : "inline-block"
} ) ) ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { // Progress bar if its active
2018-05-02 19:38:11 +02:00
var progress = this . actionTimeCurrent / this . actionTimeToComplete ;
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : createProgressBarText ( { progress : progress } )
} ) ) ;
2019-04-13 03:22:46 +02:00
} else { // Start button and set Team Size button
2018-05-02 19:38:11 +02:00
el . appendChild ( createElement ( "a" , {
innerText : "Start" , class : "a-link-button" ,
margin : "3px" , padding : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
this . action . type = ActionTypes . Operation ;
this . action . name = action . name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
el . appendChild ( createElement ( "a" , {
innerText : "Set Team Size (Curr Size: " + formatNumber ( action . teamCount , 0 ) + ")" , class : "a-link-button" ,
margin : "3px" , padding : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
var popupId = "bladeburner-operation-set-team-size-popup" ;
var txt = createElement ( "p" , {
innerText : "Enter the amount of team members you would like to take on these " +
"operations. If you do not have the specified number of team members, " +
"then as many as possible will be used. Note that team members may " +
"be lost during operations."
} ) ;
var input = createElement ( "input" , {
2021-03-12 20:38:49 +01:00
type : "number" , placeholder : "Team size" , class : "text-input" ,
2018-05-02 19:38:11 +02:00
} ) ;
var setBtn = createElement ( "a" , {
innerText : "Confirm" , class : "a-link-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
var num = Math . round ( parseFloat ( input . value ) ) ;
2021-04-21 14:20:26 +02:00
if ( isNaN ( num ) || num < 0 ) {
dialogBoxCreate ( "Invalid value entered for number of Team Members (must be numeric, positive)" )
2018-05-02 19:38:11 +02:00
} else {
action . teamCount = num ;
this . updateOperationsUIElement ( el , action ) ;
}
removeElementById ( popupId ) ;
return false ;
}
} ) ;
var cancelBtn = createElement ( "a" , {
innerText : "Cancel" , class : "a-link-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
removeElementById ( popupId ) ;
return false ;
}
} ) ;
createPopup ( popupId , [ txt , input , setBtn , cancelBtn ] ) ;
}
} ) ) ;
}
2019-04-13 03:22:46 +02:00
// Level and buttons to change level
2018-05-02 19:38:11 +02:00
var maxLevel = ( action . level >= action . maxLevel ) ;
appendLineBreaks ( el , 2 ) ;
el . appendChild ( createElement ( "pre" , {
display : "inline-block" ,
2018-06-08 17:51:48 +02:00
innerText : "Level: " + action . level + " / " + action . maxLevel ,
2021-03-13 21:10:55 +01:00
tooltip : action . getSuccessesNeededForNextLevel ( BladeburnerConstants . OperationSuccessesPerLevel ) + " successes " +
2018-06-08 17:51:48 +02:00
"needed for next level"
2018-05-02 19:38:11 +02:00
} ) ) ;
el . appendChild ( createElement ( "a" , {
class : maxLevel ? "a-link-button-inactive" : "a-link-button" , innerHTML : "↑" ,
padding : "2px" , margin : "2px" ,
tooltip : isActive ? "WARNING: changing the level will restart the Operation" : "" ,
display : "inline" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
++ action . level ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { this . startAction ( this . action ) ; } // Restart Action
2018-05-02 19:38:11 +02:00
this . updateOperationsUIElement ( el , action ) ;
return false ;
}
} ) ) ;
el . appendChild ( createElement ( "a" , {
class : ( action . level <= 1 ) ? "a-link-button-inactive" : "a-link-button" , innerHTML : "↓" ,
padding : "2px" , margin : "2px" ,
tooltip : isActive ? "WARNING: changing the level will restart the Operation" : "" ,
display : "inline" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
-- action . level ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { this . startAction ( this . action ) ; } // Restart Action
2018-05-02 19:38:11 +02:00
this . updateOperationsUIElement ( el , action ) ;
return false ;
}
} ) ) ;
2019-04-13 03:22:46 +02:00
// General Info
2018-05-02 19:38:11 +02:00
var difficulty = action . getDifficulty ( ) ;
var actionTime = action . getActionTime ( this ) ;
appendLineBreaks ( el , 2 ) ;
el . appendChild ( createElement ( "pre" , {
display : "inline-block" ,
innerHTML : action . desc + "\n\n" +
2019-03-23 00:54:14 +01:00
` Estimated success chance: ${ formatNumber ( estimatedSuccessChance * 100 , 1 ) } % ${ action . isStealth ? stealthIcon : '' } ${ action . isKill ? killIcon : '' } \ n ` +
2021-03-31 06:45:21 +02:00
"Time Required: " + convertTimeMsToTimeElapsedString ( actionTime * 1000 ) + "\n" +
2018-05-02 19:38:11 +02:00
"Operations remaining: " + Math . floor ( action . count ) + "\n" +
"Successes: " + action . successes + "\n" +
"Failures: " + action . failures ,
} ) ) ;
2019-04-13 03:22:46 +02:00
// Autolevel Checkbox
2018-05-02 19:38:11 +02:00
el . appendChild ( createElement ( "br" ) ) ;
var autolevelCheckboxId = "bladeburner-" + action . name + "-autolevel-checkbox" ;
el . appendChild ( createElement ( "label" , {
2021-03-12 21:56:25 +01:00
for : autolevelCheckboxId , innerText : "Autolevel: " , color : "white" ,
2018-05-02 19:38:11 +02:00
tooltip : "Automatically increase operation level when possible"
} ) ) ;
2019-03-23 03:21:05 +01:00
2019-03-23 03:20:07 +01:00
const checkboxInput = createElement ( "input" , {
type : "checkbox" ,
id : autolevelCheckboxId ,
checked : action . autoLevel ,
changeListener : ( ) => {
action . autoLevel = checkboxInput . checked ;
} ,
2018-05-02 19:38:11 +02:00
} ) ;
2019-03-23 03:20:07 +01:00
2021-03-12 21:56:25 +01:00
el . appendChild ( checkboxInput ) ;
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . updateBlackOpsUIElement = function ( el , action ) {
removeChildrenFromElement ( el ) ;
var isActive = el . classList . contains ( ActiveActionCssClass ) ;
var isCompleted = ( this . blackops [ action . name ] != null ) ;
var estimatedSuccessChance = action . getSuccessChance ( this , { est : true } ) ;
var difficulty = action . getDifficulty ( ) ;
var actionTime = action . getActionTime ( this ) ;
var hasReqdRank = this . rank >= action . reqdRank ;
2019-04-13 03:22:46 +02:00
// UI for Completed Black Op
2018-05-02 19:38:11 +02:00
if ( isCompleted ) {
el . appendChild ( createElement ( "h2" , {
innerText : action . name + " (COMPLETED)" , display : "block" ,
} ) ) ;
return ;
}
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "h2" , { // Header
2018-05-02 19:38:11 +02:00
innerText : isActive ? action . name + " (IN PROGRESS - " +
formatNumber ( this . actionTimeCurrent , 0 ) + " / " +
formatNumber ( this . actionTimeToComplete , 0 ) + ")"
: action . name ,
display : "inline-block" ,
} ) ) ;
2019-04-13 03:22:46 +02:00
if ( isActive ) { // Progress bar if its active
2018-05-02 19:38:11 +02:00
var progress = this . actionTimeCurrent / this . actionTimeToComplete ;
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : createProgressBarText ( { progress : progress } )
} ) ) ;
} else {
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "a" , { // Start button
2018-05-02 19:38:11 +02:00
innerText : "Start" , margin : "3px" , padding : "3px" ,
class : hasReqdRank ? "a-link-button" : "a-link-button-inactive" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
this . action . type = ActionTypes . BlackOperation ;
this . action . name = action . name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "a" , { // Set Team Size Button
2018-05-02 19:38:11 +02:00
innerText : "Set Team Size (Curr Size: " + formatNumber ( action . teamCount , 0 ) + ")" , class : "a-link-button" ,
margin : "3px" , padding : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
var popupId = "bladeburner-operation-set-team-size-popup" ;
var txt = createElement ( "p" , {
innerText : "Enter the amount of team members you would like to take on this " +
"BlackOp. If you do not have the specified number of team members, " +
"then as many as possible will be used. Note that team members may " +
"be lost during operations."
} ) ;
var input = createElement ( "input" , {
2021-03-12 20:38:49 +01:00
type : "number" , placeholder : "Team size" , class : "text-input" ,
2018-05-02 19:38:11 +02:00
} ) ;
var setBtn = createElement ( "a" , {
innerText : "Confirm" , class : "a-link-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
var num = Math . round ( parseFloat ( input . value ) ) ;
2021-04-21 14:20:26 +02:00
if ( isNaN ( num ) || num < 0 ) {
dialogBoxCreate ( "Invalid value entered for number of Team Members (must be numeric, positive)" )
2018-05-02 19:38:11 +02:00
} else {
action . teamCount = num ;
this . updateBlackOpsUIElement ( el , action ) ;
}
removeElementById ( popupId ) ;
return false ;
}
} ) ;
var cancelBtn = createElement ( "a" , {
innerText : "Cancel" , class : "a-link-button" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-05-02 19:38:11 +02:00
removeElementById ( popupId ) ;
return false ;
}
} ) ;
createPopup ( popupId , [ txt , input , setBtn , cancelBtn ] ) ;
}
} ) ) ;
}
2019-04-13 03:22:46 +02:00
// Info
2018-05-02 19:38:11 +02:00
appendLineBreaks ( el , 2 ) ;
el . appendChild ( createElement ( "p" , {
display : "inline-block" ,
innerHTML : "<br>" + action . desc + "<br><br>" ,
} ) ) ;
el . appendChild ( createElement ( "p" , {
display : "block" , color : hasReqdRank ? "white" : "red" ,
innerHTML : "Required Rank: " + formatNumber ( action . reqdRank , 0 ) + "<br>"
} ) ) ;
el . appendChild ( createElement ( "p" , {
display : "inline-block" ,
2019-03-23 00:58:40 +01:00
innerHTML : ` Estimated Success Chance: ${ formatNumber ( estimatedSuccessChance * 100 , 1 ) } % ${ action . isStealth ? stealthIcon : '' } ${ action . isKill ? killIcon : '' } \ n ` +
2021-03-31 06:45:21 +02:00
"Time Required: " + convertTimeMsToTimeElapsedString ( actionTime * 1000 ) ,
2018-05-02 19:38:11 +02:00
} ) )
}
Bladeburner . prototype . updateSkillsUIElement = function ( el , skill ) {
removeChildrenFromElement ( el ) ;
var skillName = skill . name ;
var currentLevel = 0 ;
if ( this . skills [ skillName ] && ! isNaN ( this . skills [ skillName ] ) ) {
currentLevel = this . skills [ skillName ] ;
}
2018-06-25 02:13:50 +02:00
var pointCost = skill . calculateCost ( currentLevel ) ;
2018-05-02 19:38:11 +02:00
2021-03-31 06:45:21 +02:00
const nameDiv = createElement ( "div" ) ;
ReactDOM . render ( React . createElement ( CopyableText , { value : skill . name } , null ) , nameDiv ) ;
el . appendChild ( nameDiv )
const h2 = createElement ( "h2" , { // Header
display : "inline-block" ,
} ) ;
h2 . appendChild ( nameDiv ) ;
el . appendChild ( h2 ) ;
2018-05-02 19:38:11 +02:00
var canLevel = this . skillPoints >= pointCost ;
var maxLvl = skill . maxLvl ? currentLevel >= skill . maxLvl : false ;
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "a" , { // Level up button
2018-05-02 19:38:11 +02:00
innerText : "Level" , display : "inline-block" ,
class : canLevel && ! maxLvl ? "a-link-button" : "a-link-button-inactive" ,
margin : "3px" , padding : "3px" ,
2021-03-31 06:45:21 +02:00
clickListener : ( ) => {
2018-06-08 17:51:48 +02:00
if ( this . skillPoints < pointCost ) { return ; }
2018-05-02 19:38:11 +02:00
this . skillPoints -= pointCost ;
this . upgradeSkill ( skill ) ;
this . createActionAndSkillsContent ( ) ;
return false ;
}
} ) ) ;
appendLineBreaks ( el , 2 ) ;
2021-03-31 06:45:21 +02:00
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : ` Level: ${ currentLevel } ` ,
} ) ) ;
2018-05-02 19:38:11 +02:00
if ( maxLvl ) {
el . appendChild ( createElement ( "p" , {
color : "red" , display : "block" ,
innerText : "MAX LEVEL"
} ) ) ;
} else {
el . appendChild ( createElement ( "p" , {
display : "block" ,
innerText : "Skill Points required: " + formatNumber ( pointCost , 0 ) ,
} ) ) ;
}
2019-04-13 03:22:46 +02:00
el . appendChild ( createElement ( "p" , { // Info/Description
2018-05-02 19:38:11 +02:00
innerHTML : skill . desc , display : "inline-block" ,
} ) ) ;
}
2019-04-13 03:22:46 +02:00
// Bladeburner Console Window
2018-11-04 23:39:30 +01:00
Bladeburner . prototype . postToConsole = function ( input , saveToLogs = true ) {
const MaxConsoleEntries = 100 ;
if ( saveToLogs === true ) {
this . consoleLogs . push ( input ) ;
if ( this . consoleLogs . length > MaxConsoleEntries ) {
this . consoleLogs . shift ( ) ;
}
}
2018-05-02 19:38:11 +02:00
if ( input == null || DomElems . consoleDiv == null ) { return ; }
2021-03-31 06:45:21 +02:00
$ ( "#bladeburner-console-input-row" ) . before ( '<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>' ) ;
2018-11-04 23:39:30 +01:00
if ( DomElems . consoleTable . childNodes . length > MaxConsoleEntries ) {
2018-05-02 19:38:11 +02:00
DomElems . consoleTable . removeChild ( DomElems . consoleTable . firstChild ) ;
}
2018-11-04 23:39:30 +01:00
2018-05-02 19:38:11 +02:00
this . updateConsoleScroll ( ) ;
}
Bladeburner . prototype . updateConsoleScroll = function ( ) {
DomElems . consoleDiv . scrollTop = DomElems . consoleDiv . scrollHeight ;
}
Bladeburner . prototype . resetConsoleInput = function ( ) {
DomElems . consoleInput . value = "" ;
}
Bladeburner . prototype . clearConsole = function ( ) {
while ( DomElems . consoleTable . childNodes . length > 1 ) {
DomElems . consoleTable . removeChild ( DomElems . consoleTable . firstChild ) ;
}
2018-11-04 23:39:30 +01:00
this . consoleLogs . length = 0 ;
2018-05-02 19:38:11 +02:00
}
Bladeburner . prototype . log = function ( input ) {
2019-04-13 03:22:46 +02:00
// Adds a timestamp and then just calls postToConsole
2018-09-15 04:27:29 +02:00
this . postToConsole ( ` [ ${ getTimestamp ( ) } ] ${ input } ` ) ;
2018-05-02 19:38:11 +02:00
}
2019-04-13 03:22:46 +02:00
// Handles a potential series of commands (comm1; comm2; comm3;)
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . executeConsoleCommands = function ( commands ) {
try {
2019-04-13 03:22:46 +02:00
// Console History
2018-11-01 19:18:32 +01:00
if ( this . consoleHistory [ this . consoleHistory . length - 1 ] != commands ) {
this . consoleHistory . push ( commands ) ;
if ( this . consoleHistory . length > 50 ) {
this . consoleHistory . splice ( 0 , 1 ) ;
2018-05-02 19:38:11 +02:00
}
}
2018-11-01 19:18:32 +01:00
consoleHistoryIndex = this . consoleHistory . length ;
2018-05-02 19:38:11 +02:00
2021-03-13 21:10:55 +01:00
const arrayOfCommands = commands . split ( ";" ) ;
for ( let i = 0 ; i < arrayOfCommands . length ; ++ i ) {
2018-05-02 19:38:11 +02:00
this . executeConsoleCommand ( arrayOfCommands [ i ] ) ;
}
} catch ( e ) {
exceptionAlert ( e ) ;
}
}
2019-04-13 03:22:46 +02:00
// Execute a single console command
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . executeConsoleCommand = function ( command ) {
command = command . trim ( ) ;
2019-04-13 03:22:46 +02:00
command = command . replace ( /\s\s+/g , ' ' ) ; // Replace all whitespace w/ a single space
2018-05-02 19:38:11 +02:00
var args = this . parseCommandArguments ( command ) ;
2019-04-13 03:22:46 +02:00
if ( args . length <= 0 ) { return ; } // Log an error?
2018-05-02 19:38:11 +02:00
switch ( args [ 0 ] . toLowerCase ( ) ) {
case "automate" :
this . executeAutomateConsoleCommand ( args ) ;
break ;
case "clear" :
case "cls" :
this . clearConsole ( ) ;
break ;
case "help" :
this . executeHelpConsoleCommand ( args ) ;
break ;
case "log" :
this . executeLogConsoleCommand ( args ) ;
break ;
case "skill" :
this . executeSkillConsoleCommand ( args ) ;
break ;
case "start" :
this . executeStartConsoleCommand ( args ) ;
break ;
case "stop" :
this . resetAction ( ) ;
break ;
default :
this . postToConsole ( "Invalid console command" ) ;
break ;
}
}
Bladeburner . prototype . parseCommandArguments = function ( command ) {
2019-04-13 03:22:46 +02:00
/ * *
* Returns an array with command and its arguments in each index .
* e . g . skill "blade's intuition" foo returns [ skill , blade ' s intuition , foo ]
* The input to this fn will be trimmed and will have all whitespace replaced w / a single space
* /
2019-02-05 07:45:04 +01:00
const args = [ ] ;
let start = 0 , i = 0 ;
2018-05-02 19:38:11 +02:00
while ( i < command . length ) {
2019-02-05 07:45:04 +01:00
const c = command . charAt ( i ) ;
if ( c === '"' ) { // Double quotes
const endQuote = command . indexOf ( '"' , i + 1 ) ;
if ( endQuote !== - 1 && ( endQuote === command . length - 1 || command . charAt ( endQuote + 1 ) === " " ) ) {
args . push ( command . substr ( i + 1 , ( endQuote - i - 1 ) ) ) ;
if ( endQuote === command . length - 1 ) {
start = i = endQuote + 1 ;
} else {
2019-04-13 03:22:46 +02:00
start = i = endQuote + 2 ; // Skip the space
2019-02-05 07:45:04 +01:00
}
continue ;
}
} else if ( c === "'" ) { // Single quotes, same thing as above
const endQuote = command . indexOf ( "'" , i + 1 ) ;
2018-05-02 19:38:11 +02:00
if ( endQuote !== - 1 && ( endQuote === command . length - 1 || command . charAt ( endQuote + 1 ) === " " ) ) {
args . push ( command . substr ( i + 1 , ( endQuote - i - 1 ) ) ) ;
if ( endQuote === command . length - 1 ) {
start = i = endQuote + 1 ;
} else {
2019-04-13 03:22:46 +02:00
start = i = endQuote + 2 ; // Skip the space
2018-05-02 19:38:11 +02:00
}
continue ;
}
} else if ( c === " " ) {
args . push ( command . substr ( start , i - start ) ) ;
start = i + 1 ;
}
++ i ;
}
if ( start !== i ) { args . push ( command . substr ( start , i - start ) ) ; }
return args ;
}
Bladeburner . prototype . executeAutomateConsoleCommand = function ( args ) {
if ( args . length !== 2 && args . length !== 4 ) {
this . postToConsole ( "Invalid use of 'automate' command: automate [var] [val] [hi/low]. Use 'help automate' for more info" ) ;
return ;
}
2019-04-13 03:22:46 +02:00
// Enable/Disable
2018-05-02 19:38:11 +02:00
if ( args . length === 2 ) {
var flag = args [ 1 ] ;
if ( flag . toLowerCase ( ) === "status" ) {
this . postToConsole ( "Automation: " + ( this . automateEnabled ? "enabled" : "disabled" ) ) ;
if ( this . automateEnabled ) {
this . postToConsole ( "When your stamina drops to " + formatNumber ( this . automateThreshLow , 0 ) +
", you will automatically switch to " + this . automateActionLow . name +
". When your stamina recovers to " +
formatNumber ( this . automateThreshHigh , 0 ) + ", you will automatically " +
"switch to " + this . automateActionHigh . name + "." ) ;
}
} else if ( flag . toLowerCase ( ) . includes ( "en" ) ) {
if ( ! ( this . automateActionLow instanceof ActionIdentifier ) ||
! ( this . automateActionHigh instanceof ActionIdentifier ) ) {
return this . log ( "Failed to enable automation. Actions were not set" ) ;
}
this . automateEnabled = true ;
this . log ( "Bladeburner automation enabled" ) ;
} else if ( flag . toLowerCase ( ) . includes ( "d" ) ) {
this . automateEnabled = false ;
this . log ( "Bladeburner automation disabled" ) ;
} else {
this . log ( "Invalid argument for 'automate' console command: " + args [ 1 ] ) ;
}
return ;
}
2019-04-13 03:22:46 +02:00
// Set variables
2018-05-02 19:38:11 +02:00
if ( args . length === 4 ) {
var variable = args [ 1 ] , val = args [ 2 ] ;
2019-04-13 03:22:46 +02:00
var highLow = false ; // True for high, false for low
2018-05-02 19:38:11 +02:00
if ( args [ 3 ] . toLowerCase ( ) . includes ( "hi" ) ) { highLow = true ; }
switch ( variable ) {
case "general" :
case "gen" :
if ( GeneralActions [ val ] != null ) {
var action = new ActionIdentifier ( {
type : ActionTypes [ val ] , name : val
} ) ;
if ( highLow ) {
this . automateActionHigh = action ;
} else {
this . automateActionLow = action ;
}
this . log ( "Automate (" + ( highLow ? "HIGH" : "LOW" ) + ") action set to " + val ) ;
} else {
this . postToConsole ( "Invalid action name specified: " + val ) ;
}
break ;
case "contract" :
case "contracts" :
if ( this . contracts [ val ] != null ) {
var action = new ActionIdentifier ( {
type : ActionTypes . Contract , name : val
} ) ;
if ( highLow ) {
this . automateActionHigh = action ;
} else {
this . automateActionLow = action ;
}
this . log ( "Automate (" + ( highLow ? "HIGH" : "LOW" ) + ") action set to " + val ) ;
} else {
this . postToConsole ( "Invalid contract name specified: " + val ) ;
}
break ;
case "ops" :
case "op" :
case "operations" :
case "operation" :
if ( this . operations [ val ] != null ) {
var action = new ActionIdentifier ( {
type : ActionTypes . Operation , name : val
} ) ;
if ( highLow ) {
this . automateActionHigh = action ;
} else {
this . automateActionLow = action ;
}
this . log ( "Automate (" + ( highLow ? "HIGH" : "LOW" ) + ") action set to " + val ) ;
} else {
this . postToConsole ( "Invalid Operation name specified: " + val ) ;
}
break ;
case "stamina" :
if ( isNaN ( val ) ) {
this . postToConsole ( "Invalid value specified for stamina threshold (must be numeric): " + val ) ;
} else {
if ( highLow ) {
this . automateThreshHigh = Number ( val ) ;
} else {
this . automateThreshLow = Number ( val ) ;
}
this . log ( "Automate (" + ( highLow ? "HIGH" : "LOW" ) + ") stamina threshold set to " + val ) ;
}
break ;
default :
break ;
}
return ;
}
}
Bladeburner . prototype . executeHelpConsoleCommand = function ( args ) {
if ( args . length === 1 ) {
2021-03-13 21:10:55 +01:00
for ( const line of ConsoleHelpText . helpList ) {
this . postToConsole ( line ) ;
}
2018-05-02 19:38:11 +02:00
} else {
for ( var i = 1 ; i < args . length ; ++ i ) {
2021-03-13 21:10:55 +01:00
const helpText = ConsoleHelpText [ args [ i ] ] ;
for ( const line of helpText ) {
this . postToConsole ( line ) ;
2018-05-02 19:38:11 +02:00
}
}
}
}
Bladeburner . prototype . executeLogConsoleCommand = function ( args ) {
if ( args . length < 3 ) {
this . postToConsole ( "Invalid usage of log command: log [enable/disable] [action/event]" ) ;
this . postToConsole ( "Use 'help log' for more details and examples" ) ;
return ;
}
var flag = true ;
2019-04-13 03:22:46 +02:00
if ( args [ 1 ] . toLowerCase ( ) . includes ( "d" ) ) { flag = false ; } // d for disable
2018-05-02 19:38:11 +02:00
switch ( args [ 2 ] . toLowerCase ( ) ) {
case "general" :
case "gen" :
this . logging . general = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for general actions" ) ;
break ;
case "contract" :
case "contracts" :
this . logging . contracts = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for Contracts" ) ;
break ;
case "ops" :
case "op" :
case "operations" :
case "operation" :
this . logging . ops = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for Operations" ) ;
break ;
case "blackops" :
case "blackop" :
case "black operations" :
case "black operation" :
this . logging . blackops = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for BlackOps" ) ;
break ;
case "event" :
case "events" :
this . logging . events = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for events" ) ;
break ;
case "all" :
this . logging . general = flag ;
this . logging . contracts = flag ;
this . logging . ops = flag ;
this . logging . blackops = flag ;
this . logging . events = flag ;
this . log ( "Logging " + ( flag ? "enabled" : "disabled" ) + " for everything" ) ;
break ;
default :
this . postToConsole ( "Invalid action/event type specified: " + args [ 2 ] ) ;
this . postToConsole ( "Examples of valid action/event identifiers are: [general, contracts, ops, blackops, events]" ) ;
break ;
}
}
Bladeburner . prototype . executeSkillConsoleCommand = function ( args ) {
switch ( args . length ) {
case 1 :
2019-04-13 03:22:46 +02:00
// Display Skill Help Command
2018-05-02 19:38:11 +02:00
this . postToConsole ( "Invalid usage of 'skill' console command: skill [action] [name]" ) ;
this . postToConsole ( "Use 'help skill' for more info" ) ;
break ;
case 2 :
if ( args [ 1 ] . toLowerCase ( ) === "list" ) {
2019-04-13 03:22:46 +02:00
// List all skills and their level
2018-05-02 19:38:11 +02:00
this . postToConsole ( "Skills: " ) ;
var skillNames = Object . keys ( Skills ) ;
for ( var i = 0 ; i < skillNames . length ; ++ i ) {
var skill = Skills [ skillNames [ i ] ] ;
var level = 0 ;
if ( this . skills [ skill . name ] != null ) { level = this . skills [ skill . name ] ; }
this . postToConsole ( skill . name + ": Level " + formatNumber ( level , 0 ) ) ;
}
this . postToConsole ( " " ) ;
this . postToConsole ( "Effects: " ) ;
var multKeys = Object . keys ( this . skillMultipliers ) ;
for ( var i = 0 ; i < multKeys . length ; ++ i ) {
var mult = this . skillMultipliers [ multKeys [ i ] ] ;
if ( mult && mult !== 1 ) {
mult = formatNumber ( mult , 3 ) ;
switch ( multKeys [ i ] ) {
case "successChanceAll" :
this . postToConsole ( "Total Success Chance: x" + mult ) ;
break ;
case "successChanceStealth" :
this . postToConsole ( "Stealth Success Chance: x" + mult ) ;
break ;
case "successChanceKill" :
this . postToConsole ( "Retirement Success Chance: x" + mult ) ;
break ;
case "successChanceContract" :
this . postToConsole ( "Contract Success Chance: x" + mult ) ;
break ;
case "successChanceOperation" :
this . postToConsole ( "Operation Success Chance: x" + mult ) ;
break ;
case "successChanceEstimate" :
this . postToConsole ( "Synthoid Data Estimate: x" + mult ) ;
break ;
case "actionTime" :
this . postToConsole ( "Action Time: x" + mult ) ;
break ;
case "effHack" :
this . postToConsole ( "Hacking Skill: x" + mult ) ;
break ;
case "effStr" :
this . postToConsole ( "Strength: x" + mult ) ;
break ;
case "effDef" :
this . postToConsole ( "Defense: x" + mult ) ;
break ;
case "effDex" :
this . postToConsole ( "Dexterity: x" + mult ) ;
break ;
case "effAgi" :
this . postToConsole ( "Agility: x" + mult ) ;
break ;
case "effCha" :
this . postToConsole ( "Charisma: x" + mult ) ;
break ;
case "effInt" :
this . postToConsole ( "Intelligence: x" + mult ) ;
break ;
case "stamina" :
this . postToConsole ( "Stamina: x" + mult ) ;
break ;
default :
2021-03-08 04:46:50 +01:00
console . warn ( ` Unrecognized SkillMult Key: ${ multKeys [ i ] } ` ) ;
2018-05-02 19:38:11 +02:00
break ;
}
}
}
} else {
this . postToConsole ( "Invalid usage of 'skill' console command: skill [action] [name]" ) ;
this . postToConsole ( "Use 'help skill' for more info" ) ;
}
break ;
case 3 :
var skillName = args [ 2 ] ;
var skill = Skills [ skillName ] ;
if ( skill == null || ! ( skill instanceof Skill ) ) {
return this . postToConsole ( "Invalid skill name (Note that this is case-sensitive): " + skillName ) ;
}
if ( args [ 1 ] . toLowerCase ( ) === "list" ) {
2021-03-13 21:10:55 +01:00
let level = 0 ;
if ( this . skills [ skill . name ] !== undefined ) {
level = this . skills [ skill . name ] ;
}
this . postToConsole ( skill . name + ": Level " + formatNumber ( level ) , 0 ) ;
2018-05-02 19:38:11 +02:00
} else if ( args [ 1 ] . toLowerCase ( ) === "level" ) {
var currentLevel = 0 ;
if ( this . skills [ skillName ] && ! isNaN ( this . skills [ skillName ] ) ) {
currentLevel = this . skills [ skillName ] ;
}
2018-06-25 02:13:50 +02:00
var pointCost = skill . calculateCost ( currentLevel ) ;
2018-05-02 19:38:11 +02:00
if ( this . skillPoints >= pointCost ) {
this . skillPoints -= pointCost ;
this . upgradeSkill ( skill ) ;
this . log ( skill . name + " upgraded to Level " + this . skills [ skillName ] ) ;
this . createActionAndSkillsContent ( ) ;
} else {
this . postToConsole ( "You do not have enough Skill Points to upgrade this. You need " + formatNumber ( pointCost , 0 ) ) ;
}
} else {
this . postToConsole ( "Invalid usage of 'skill' console command: skill [action] [name]" ) ;
this . postToConsole ( "Use 'help skill' for more info" ) ;
}
break ;
default :
this . postToConsole ( "Invalid usage of 'skill' console command: skill [action] [name]" ) ;
this . postToConsole ( "Use 'help skill' for more info" ) ;
break ;
}
}
Bladeburner . prototype . executeStartConsoleCommand = function ( args ) {
if ( args . length !== 3 ) {
this . postToConsole ( "Invalid usage of 'start' console command: start [type] [name]" ) ;
this . postToConsole ( "Use 'help start' for more info" ) ;
return ;
}
var name = args [ 2 ] ;
switch ( args [ 1 ] . toLowerCase ( ) ) {
case "general" :
case "gen" :
if ( GeneralActions [ name ] != null ) {
this . action . type = ActionTypes [ name ] ;
this . action . name = name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
} else {
this . postToConsole ( "Invalid action name specified: " + args [ 2 ] ) ;
}
break ;
case "contract" :
case "contracts" :
if ( this . contracts [ name ] != null ) {
this . action . type = ActionTypes . Contract ;
this . action . name = name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
} else {
this . postToConsole ( "Invalid contract name specified: " + args [ 2 ] ) ;
}
break ;
case "ops" :
case "op" :
case "operations" :
case "operation" :
if ( this . operations [ name ] != null ) {
this . action . type = ActionTypes . Operation ;
this . action . name = name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
} else {
this . postToConsole ( "Invalid Operation name specified: " + args [ 2 ] ) ;
}
break ;
case "blackops" :
case "blackop" :
case "black operations" :
case "black operation" :
if ( BlackOperations [ name ] != null ) {
this . action . type = ActionTypes . BlackOperation ;
this . action . name = name ;
this . startAction ( this . action ) ;
this . updateActionAndSkillsContent ( ) ;
} else {
this . postToConsole ( "Invalid BlackOp name specified: " + args [ 2 ] ) ;
}
break ;
default :
this . postToConsole ( "Invalid action/event type specified: " + args [ 1 ] ) ;
this . postToConsole ( "Examples of valid action/event identifiers are: [general, contract, op, blackop]" ) ;
break ;
}
}
2018-06-08 17:51:48 +02:00
Bladeburner . prototype . getActionIdFromTypeAndName = function ( type = "" , name = "" ) {
if ( type === "" || name === "" ) { return null ; }
var action = new ActionIdentifier ( ) ;
var convertedType = type . toLowerCase ( ) . trim ( ) ;
var convertedName = name . toLowerCase ( ) . trim ( ) ;
switch ( convertedType ) {
case "contract" :
case "contracts" :
2018-06-25 21:07:03 +02:00
case "contr" :
2018-06-08 17:51:48 +02:00
action . type = ActionTypes [ "Contract" ] ;
if ( this . contracts . hasOwnProperty ( name ) ) {
action . name = name ;
return action ;
} else {
return null ;
}
2018-07-28 02:00:57 +02:00
break ;
2018-06-08 17:51:48 +02:00
case "operation" :
case "operations" :
case "op" :
case "ops" :
action . type = ActionTypes [ "Operation" ] ;
if ( this . operations . hasOwnProperty ( name ) ) {
action . name = name ;
return action ;
} else {
return null ;
}
2018-07-28 02:00:57 +02:00
break ;
2018-06-08 17:51:48 +02:00
case "blackoperation" :
case "black operation" :
case "black operations" :
case "black op" :
case "black ops" :
case "blackop" :
case "blackops" :
action . type = ActionTypes [ "BlackOp" ] ;
if ( BlackOperations . hasOwnProperty ( name ) ) {
action . name = name ;
return action ;
} else {
return null ;
}
2018-07-28 02:00:57 +02:00
break ;
2018-06-08 17:51:48 +02:00
case "general" :
case "general action" :
case "gen" :
break ;
default :
return null ;
}
if ( convertedType . startsWith ( "gen" ) ) {
switch ( convertedName ) {
case "training" :
action . type = ActionTypes [ "Training" ] ;
2018-07-15 02:59:27 +02:00
action . name = "Training" ;
2018-06-08 17:51:48 +02:00
break ;
case "recruitment" :
case "recruit" :
action . type = ActionTypes [ "Recruitment" ] ;
2018-07-15 02:59:27 +02:00
action . name = "Recruitment" ;
2018-06-08 17:51:48 +02:00
break ;
case "field analysis" :
case "fieldanalysis" :
action . type = ActionTypes [ "Field Analysis" ] ;
2018-07-15 02:59:27 +02:00
action . name = "Field Analysis" ;
2018-06-08 17:51:48 +02:00
break ;
2019-02-22 22:26:30 +01:00
case "diplomacy" :
action . type = ActionTypes [ "Diplomacy" ] ;
action . name = "Diplomacy" ;
break ;
case "hyperbolic regeneration chamber" :
action . type = ActionTypes [ "Hyperbolic Regeneration Chamber" ] ;
action . name = "Hyperbolic Regeneration Chamber" ;
break ;
2018-06-08 17:51:48 +02:00
default :
return null ;
}
return action ;
}
}
2018-07-12 07:01:52 +02:00
Bladeburner . prototype . getTypeAndNameFromActionId = function ( actionId ) {
var res = { } ;
let types = Object . keys ( ActionTypes ) ;
for ( let i = 0 ; i < types . length ; ++ i ) {
if ( actionId . type === ActionTypes [ types [ i ] ] ) {
res . type = types [ i ] ;
break ;
}
}
if ( res . type == null ) { res . type = "Idle" ; }
res . name = actionId . name != null ? actionId . name : "Idle" ;
return res ;
}
2018-06-25 21:07:03 +02:00
Bladeburner . prototype . getContractNamesNetscriptFn = function ( ) {
2018-06-25 02:13:50 +02:00
return Object . keys ( this . contracts ) ;
2018-06-08 17:51:48 +02:00
}
2018-06-25 21:07:03 +02:00
Bladeburner . prototype . getOperationNamesNetscriptFn = function ( ) {
2018-06-25 02:13:50 +02:00
return Object . keys ( this . operations ) ;
2018-06-08 17:51:48 +02:00
}
2018-06-25 21:07:03 +02:00
Bladeburner . prototype . getBlackOpNamesNetscriptFn = function ( ) {
2018-06-25 02:13:50 +02:00
return Object . keys ( BlackOperations ) ;
2018-06-08 17:51:48 +02:00
}
2018-06-25 21:07:03 +02:00
Bladeburner . prototype . getGeneralActionNamesNetscriptFn = function ( ) {
2018-06-25 02:13:50 +02:00
return Object . keys ( GeneralActions ) ;
2018-06-08 17:51:48 +02:00
}
2018-06-25 21:07:03 +02:00
Bladeburner . prototype . getSkillNamesNetscriptFn = function ( ) {
2018-06-25 02:13:50 +02:00
return Object . keys ( Skills ) ;
2018-06-08 17:51:48 +02:00
}
Bladeburner . prototype . startActionNetscriptFn = function ( type , name , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' ` ;
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return false ;
}
2018-10-23 20:55:42 +02:00
// Special logic for Black Ops
if ( actionId . type === ActionTypes [ "BlackOp" ] ) {
// Can't start a BlackOp if you don't have the required rank
let action = this . getActionObject ( actionId ) ;
if ( action . reqdRank > this . rank ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , ` Insufficient rank to start Black Op ' ${ actionId . name } '. ` ) ;
2018-10-23 20:55:42 +02:00
return false ;
}
2018-11-19 10:03:38 +01:00
// Can't start a BlackOp if its already been done
if ( this . blackops [ actionId . name ] != null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , ` Black Op ${ actionId . name } has already been completed. ` ) ;
2018-11-19 10:03:38 +01:00
return false ;
}
2018-10-23 20:55:42 +02:00
// Can't start a BlackOp if you haven't done the one before it
var blackops = [ ] ;
for ( const nm in BlackOperations ) {
if ( BlackOperations . hasOwnProperty ( nm ) ) {
blackops . push ( nm ) ;
}
}
blackops . sort ( function ( a , b ) {
return ( BlackOperations [ a ] . reqdRank - BlackOperations [ b ] . reqdRank ) ; // Sort black ops in intended order
} ) ;
let i = blackops . indexOf ( actionId . name ) ;
if ( i === - 1 ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , ` Invalid Black Op: ' ${ name } ' ` ) ;
2018-10-23 20:55:42 +02:00
return false ;
}
if ( i > 0 && this . blackops [ blackops [ i - 1 ] ] == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , ` Preceding Black Op must be completed before starting ' ${ actionId . name } '. ` ) ;
2018-10-23 20:55:42 +02:00
return false ;
}
}
2018-06-08 17:51:48 +02:00
try {
this . startAction ( actionId ) ;
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , ` Starting bladeburner action with type ' ${ type } ' and name ${ name } " ` ) ;
2018-06-08 17:51:48 +02:00
return true ;
} catch ( e ) {
this . resetAction ( ) ;
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.startAction" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return false ;
}
}
Bladeburner . prototype . getActionTimeNetscriptFn = function ( type , name , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' `
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionTime" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const actionObj = this . getActionObject ( actionId ) ;
2018-06-08 17:51:48 +02:00
if ( actionObj == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionTime" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
switch ( actionId . type ) {
case ActionTypes [ "Contract" ] :
case ActionTypes [ "Operation" ] :
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
return actionObj . getActionTime ( this ) ;
case ActionTypes [ "Training" ] :
case ActionTypes [ "Field Analysis" ] :
case ActionTypes [ "FieldAnalysis" ] :
return 30 ;
case ActionTypes [ "Recruitment" ] :
return this . getRecruitmentTime ( ) ;
default :
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionTime" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
}
Bladeburner . prototype . getActionEstimatedSuccessChanceNetscriptFn = function ( type , name , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' `
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionEstimatedSuccessChance" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const actionObj = this . getActionObject ( actionId ) ;
2018-06-08 17:51:48 +02:00
if ( actionObj == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionEstimatedSuccessChance" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
switch ( actionId . type ) {
case ActionTypes [ "Contract" ] :
case ActionTypes [ "Operation" ] :
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
2018-06-25 02:13:50 +02:00
return actionObj . getSuccessChance ( this , { est : true } ) ;
2018-06-08 17:51:48 +02:00
case ActionTypes [ "Training" ] :
case ActionTypes [ "Field Analysis" ] :
case ActionTypes [ "FieldAnalysis" ] :
return 1 ;
case ActionTypes [ "Recruitment" ] :
return this . getRecruitmentSuccessChance ( ) ;
default :
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionEstimatedSuccessChance" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
}
Bladeburner . prototype . getActionCountRemainingNetscriptFn = function ( type , name , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' ` ;
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionCountRemaining" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const actionObj = this . getActionObject ( actionId ) ;
2018-06-08 17:51:48 +02:00
if ( actionObj == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionCountRemaining" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
switch ( actionId . type ) {
case ActionTypes [ "Contract" ] :
case ActionTypes [ "Operation" ] :
2018-10-31 01:32:30 +01:00
return Math . floor ( actionObj . count ) ;
2018-06-08 17:51:48 +02:00
case ActionTypes [ "BlackOp" ] :
case ActionTypes [ "BlackOperation" ] :
2018-10-23 20:55:42 +02:00
if ( this . blackops [ name ] != null ) {
return 0 ;
} else {
return 1 ;
}
2018-06-08 17:51:48 +02:00
case ActionTypes [ "Training" ] :
case ActionTypes [ "Field Analysis" ] :
case ActionTypes [ "FieldAnalysis" ] :
return Infinity ;
default :
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getActionCountRemaining" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
}
Bladeburner . prototype . getSkillLevelNetscriptFn = function ( skillName , workerScript ) {
2021-03-12 08:51:56 +01:00
if ( skillName === "" || ! Skills . hasOwnProperty ( skillName ) ) {
workerScript . log ( "bladeburner.getSkillLevel" , ` Invalid skill: ' ${ skillName } ' ` ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2018-06-25 05:07:05 +02:00
if ( this . skills [ skillName ] == null ) {
return 0 ;
} else {
return this . skills [ skillName ] ;
}
2018-06-08 17:51:48 +02:00
}
2018-08-12 03:05:32 +02:00
Bladeburner . prototype . getSkillUpgradeCostNetscriptFn = function ( skillName , workerScript ) {
2021-03-12 08:51:56 +01:00
if ( skillName === "" || ! Skills . hasOwnProperty ( skillName ) ) {
workerScript . log ( "bladeburner.getSkillUpgradeCost" , ` Invalid skill: ' ${ skillName } ' ` ) ;
2018-08-12 03:05:32 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const skill = Skills [ skillName ] ;
2018-08-12 03:05:32 +02:00
if ( this . skills [ skillName ] == null ) {
return skill . calculateCost ( 0 ) ;
} else {
return skill . calculateCost ( this . skills [ skillName ] ) ;
}
}
2018-06-08 17:51:48 +02:00
Bladeburner . prototype . upgradeSkillNetscriptFn = function ( skillName , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid skill: ' ${ skillName } ' ` ;
2018-06-08 17:51:48 +02:00
if ( ! Skills . hasOwnProperty ( skillName ) ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.upgradeSkill" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return false ;
}
2021-03-12 08:51:56 +01:00
const skill = Skills [ skillName ] ;
let currentLevel = 0 ;
2018-06-08 17:51:48 +02:00
if ( this . skills [ skillName ] && ! isNaN ( this . skills [ skillName ] ) ) {
currentLevel = this . skills [ skillName ] ;
}
2021-03-12 08:51:56 +01:00
const cost = skill . calculateCost ( currentLevel ) ;
2018-06-08 17:51:48 +02:00
2018-07-24 03:34:26 +02:00
if ( skill . maxLvl && currentLevel >= skill . maxLvl ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.upgradeSkill" , ` Skill ' ${ skillName } ' is already maxed. ` ) ;
2018-07-24 03:34:26 +02:00
return false ;
}
2018-06-08 17:51:48 +02:00
if ( this . skillPoints < cost ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.upgradeSkill" , ` You do not have enough skill points to upgrade ${ skillName } (You have ${ this . skillPoints } , you need ${ cost } ) ` ) ;
2018-06-08 17:51:48 +02:00
return false ;
}
this . skillPoints -= cost ;
this . upgradeSkill ( skill ) ;
2018-07-20 03:51:18 +02:00
if ( routing . isOn ( Page . Bladeburner ) && DomElems . currentTab . toLowerCase ( ) === "skills" ) {
2018-06-08 17:51:48 +02:00
this . createActionAndSkillsContent ( ) ;
}
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.upgradeSkill" , ` ' ${ skillName } ' upgraded to level ${ this . skills [ skillName ] } ` ) ;
2018-06-08 17:51:48 +02:00
return true ;
}
Bladeburner . prototype . getTeamSizeNetscriptFn = function ( type , name , workerScript ) {
if ( type === "" && name === "" ) {
return this . teamSize ;
}
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' ` ;
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getTeamSize" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const actionObj = this . getActionObject ( actionId ) ;
2018-06-08 17:51:48 +02:00
if ( actionObj == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.getTeamSize" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
if ( actionId . type === ActionTypes [ "Operation" ] ||
actionId . type === ActionTypes [ "BlackOp" ] ||
actionId . type === ActionTypes [ "BlackOperation" ] ) {
return actionObj . teamCount ;
} else {
return 0 ;
}
}
Bladeburner . prototype . setTeamSizeNetscriptFn = function ( type , name , size , workerScript ) {
2021-03-12 08:51:56 +01:00
const errorLogText = ` Invalid action: type=' ${ type } ' name=' ${ name } ' ` ;
const actionId = this . getActionIdFromTypeAndName ( type , name ) ;
2018-06-08 17:51:48 +02:00
if ( actionId == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.setTeamSize" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2018-06-25 21:07:03 +02:00
if ( actionId . type !== ActionTypes [ "Operation" ] &&
actionId . type !== ActionTypes [ "BlackOp" ] &&
2018-06-08 17:51:48 +02:00
actionId . type !== ActionTypes [ "BlackOperation" ] ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.setTeamSize" , "Only valid for 'Operations' and 'BlackOps'" ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const actionObj = this . getActionObject ( actionId ) ;
2018-06-08 17:51:48 +02:00
if ( actionObj == null ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.setTeamSize" , errorLogText ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
2021-03-12 08:51:56 +01:00
const sanitizedSize = Math . round ( size ) ;
2021-04-21 14:20:26 +02:00
if ( isNaN ( sanitizedSize ) || sanitizedSize < 0 ) {
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.setTeamSize" , ` Invalid size: ${ size } ` ) ;
2018-06-08 17:51:48 +02:00
return - 1 ;
}
if ( this . teamSize < sanitizedSize ) { sanitizedSize = this . teamSize ; }
actionObj . teamCount = sanitizedSize ;
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.setTeamSize" , ` Team size for ' ${ name } ' set to ${ sanitizedSize } . ` ) ;
2018-06-08 17:51:48 +02:00
return sanitizedSize ;
}
Bladeburner . prototype . joinBladeburnerFactionNetscriptFn = function ( workerScript ) {
2018-06-13 06:16:23 +02:00
var bladeburnerFac = Factions [ "Bladeburners" ] ;
2018-06-08 17:51:48 +02:00
if ( bladeburnerFac . isMember ) {
return true ;
2021-03-13 21:10:55 +01:00
} else if ( this . rank >= BladeburnerConstants . RankNeededForFaction ) {
2018-06-08 17:51:48 +02:00
joinFaction ( bladeburnerFac ) ;
2021-03-12 08:51:56 +01:00
workerScript . log ( "bladeburner.joinBladeburnerFaction" , "Joined Bladeburners faction." ) ;
2018-07-20 03:51:18 +02:00
if ( routing . isOn ( Page . Bladeburner ) ) {
2018-06-08 17:51:48 +02:00
removeChildrenFromElement ( DomElems . overviewDiv ) ;
this . createOverviewContent ( ) ;
}
return true ;
} else {
2021-03-13 21:10:55 +01:00
workerScript . log ( "bladeburner.joinBladeburnerFaction" , ` You do not have the required rank ( ${ this . rank } / ${ BladeburnerConstants . RankNeededForFaction } ). ` ) ;
2018-06-08 17:51:48 +02:00
return false ;
}
}
2018-05-23 02:09:04 +02:00
2018-05-02 19:38:11 +02:00
Bladeburner . prototype . toJSON = function ( ) {
return Generic _toJSON ( "Bladeburner" , this ) ;
}
Bladeburner . fromJSON = function ( value ) {
return Generic _fromJSON ( Bladeburner , value . data ) ;
}
Reviver . constructors . Bladeburner = Bladeburner ;
2019-04-13 03:22:46 +02:00
export { Bladeburner } ;