import {CONSTANTS} from "./Constants.js"; import {Engine} from "./engine.js"; import {Faction, Factions, factionExists, joinFaction, displayFactionContent} from "./Faction.js"; import {Locations} from "./Location.js"; import {Player} from "./Player.js"; import {hackWorldDaemon} from "./RedPill.js"; import {KEY} from "./Terminal.js"; import {dialogBoxCreate} from "../utils/DialogBox.js"; import {getRandomInt, addOffset, clearObject, createElement, removeChildrenFromElement, exceptionAlert, createPopup, appendLineBreaks, removeElementById, removeElement, createProgressBarText} from "../utils/HelperFunctions.js"; import {Reviver, Generic_toJSON, Generic_fromJSON} from "../utils/JSONReviver.js"; import numeral from "numeral/min/numeral.min"; import {formatNumber} from "../utils/StringHelperFunctions.js"; var CityNames = ["Aevum", "Chongqing", "Sector-12", "New Tokyo", "Ishima", "Volhaven"]; var CyclesPerSecond = 5; //Game cycle is 200 ms var StaminaGainPerSecond = 0.0085; var BaseStaminaLoss = 0.285; //Base stamina loss per action. Increased based on difficulty var MaxStaminaToGainFactor = 70000; //Max Stamina is divided by this to get bonus stamina gain var DifficultyToTimeFactor = 10; //Action Difficulty divided by this to get base action time //The difficulty multiplier affects stamina loss and hp loss of an action. Its formula is: //difficulty ^ exponentialFactor + difficulty / linearFactor var DiffMultExponentialFactor = 0.28; var DiffMultLinearFactor = 670; var BaseRecruitmentTimeNeeded = 300; //Base time needed (s) to complete a Recruitment action var PopulationThreshold = 1e9; //Population at which success rates start being affected var ChaosThreshold = 50; //City chaos level after which it starts making tasks harder var BaseStatGain = 1; //Base stat gain per second var BaseIntGain = 0.001; //Base intelligence stat gain var ActionCountGrowthPeriod = 300; //Time (s) it takes for action count to grow by its specified value var RankToFactionRepFactor = 2; //Delta Faction Rep = this * Delta Rank var ContractSuccessesPerLevel = 15; //How many successes you need to level up a contract var OperationSuccessesPerLevel = 10; //How many successes you need to level up an op var RanksPerSkillPoint = 4; //How many ranks needed to get 1 Skill Point var ContractBaseMoneyGain = 5e3; //Base Money Gained per contract //DOM related variables var ActiveActionCssClass = "bladeburner-active-action"; //Console related stuff var consoleHistory = []; //Console command history var consoleHistoryIndex = 0; var consoleHelpText = { helpList:"Use 'help [command]' to get more information about a particular Bladeburner console command.

" + "automate [var] [val] [hi/low] Configure simple automation for Bladeburner tasks
" + "clear/cls Clear the console
" + "help [cmd] Display this help text, or help text for a specific command
" + "log [en/dis] [type] Enable or disable logging for events and actions
" + "skill [action] [name] Level or display info about your Bladeburner skills
" + "start [type] [name] Start a Bladeburner action/task
" + "stop Stops your current Bladeburner action/task
", automate:"automate [var] [val] [hi/low]

" + "A simple way to automate your Bladeburner actions. This console command can be used " + "to automatically start an action when your stamina rises above a certain threshold, and " + "automatically switch to another action when your stamina drops below another threshold.

" + "automate status - Check the current status of your automation and get a brief description of what it'll do
" + "automate en - Enable the automation feature
" + "automate dis - Disable the automation feature

" + "There are four properties that must be set for this automation to work properly. Here is how to set them:

" + "automate stamina 100 high
" + "automate contract Tracking high
" + "automate stamina 50 low
" + 'automate general "Field Analysis" low

' + "Using the four console commands above will set the automation to perform Tracking contracts " + "if your stamina is 100 or higher, and then switch to Field Analysis if your stamina drops below " + "50. Note that when setting the action, the name of the action is CASE-SENSITIVE. It must " + "exactly match whatever the name is in the UI.", clear:"clear

Clears the console", cls:"cls

Clears the console", help:"help [command]

" + "Running 'help' with no arguments displays the general help text, which lists all console commands " + "and a brief description of what they do. A command can be specified to get more specific help text " + "about that particular command. For example:

" + "help automate

" + "will display specific information about using the automate console command", log:"log [en/dis] [type]

" + "Enable or disable logging. By default, the results of completing actions such as contracts/operations are logged " + "in the console. There are also random events that are logged in the console as well. The five categories of " + "things that get logged are:

" + "[general, contracts, ops, blackops, events]

" + "The logging for these categories can be enabled or disabled like so:

" + "log dis contracts - Disables logging that occurs when contracts are completed
" + "log en contracts - Enables logging that occurs when contracts are completed
" + "log dis events - Disables logging for Bladeburner random events

" + "Logging can be universally enabled/disabled using the 'all' keyword:

" + "log dis all
" + "log en all", skill:"skill [action] [name]

" + "Level or display information about your skills.

" + "To display information about all of your skills and your multipliers, use:

" + "skill list

" + "To display information about a specific skill, specify the name of the skill afterwards. " + "Note that the name of the skill is case-sensitive. Enter it exactly as seen in the UI. If " + "the name of the skill has whitespace, enclose the name of the skill in double quotation marks:

" + "skill list Reaper
" + 'skill list "Digital Observer"

' + "This console command can also be used to level up skills:

" + "skill level [skill name]", start:"start [type] [name]

" + "Start an action. An action is specified by its type and its name. The " + "name is case-sensitive. It must appear exactly as it does in the UI. If " + "the name of the action has whitespace, enclose it in double quotation marks. " + "Valid action types include:

" + "[general, contract, op, blackop]

" + "Examples:

" + 'start contract Tracking
' + 'start op "Undercover Operation"
', stop:"stop

" + "Stop your current action and go idle", } //Keypresses for Console $(document).keydown(function(event) { if (Engine.currentPage === Engine.Page.Bladeburner) { //if (DomElems.consoleInput && !event.ctrlKey && !event.shiftKey && !event.altKey) { // DomElems.consoleInput.focus(); //} if (!(Player.bladeburner instanceof Bladeburner)) {return;} //NOTE: Keycodes imported from Terminal.js 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; setTimeout(function(){DomElems.consoleInput.selectionStart = DomElems.consoleInput.selectionEnd = 10000; }, 0); } 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; } //Latest command, put nothing if (i == len || i == len-1) { consoleHistoryIndex = len; DomElems.consoleInput.value = ""; } else { ++consoleHistoryIndex; var prevCommand = consoleHistory[consoleHistoryIndex]; DomElems.consoleInput.value = prevCommand; } } } }); function City(params={}) { this.name = params.name ? params.name : Locations.Sector12; //Synthoid population and estimate this.pop = params.pop ? params.pop : getRandomInt(800e6, 1.2*PopulationThreshold); this.popEst = this.pop * (Math.random() + 0.5); //Number of Synthoid communities population and estimate this.comms = params.comms ? params.comms : getRandomInt(1, 40); this.commsEst = this.comms + getRandomInt(-2, 2); if (this.commsEst < 0) {this.commsEst = 0;} this.chaos = 0; } City.prototype.improvePopulationEstimateByCount = function(n) { if (isNaN(n)) {throw new Error("NaN passeed into City.improvePopulationEstimateByCount()");} if (this.popEst < this.pop) { this.popEst += n; if (this.popEst > this.pop) {this.popEst = this.pop;} } else if (this.popEst > this.pop) { this.popEst -= n; if (this.popEst < this.pop) {this.popEst = this.pop;} } } //@p is the percentage, not the multiplier. e.g. pass in p = 5 for 5% City.prototype.improvePopulationEstimateByPercentage = function(p, skillMult=1) { p = p*skillMult; if (isNaN(p)) {throw new Error("NaN passed into City.improvePopulationEstimateByPercentage()");} if (this.popEst < this.pop) { ++this.popEst; //In case estimate is 0 this.popEst *= (1 + (p/100)); if (this.popEst > this.pop) {this.popEst = this.pop;} } else if (this.popEst > this.pop) { this.popEst *= (1 - (p/100)); if (this.popEst < this.pop) {this.popEst = this.pop;} } } City.prototype.improveCommunityEstimate = function(n=1) { if (isNaN(n)) {throw new Error("NaN passed into City.improveCommunityEstimate()");} if (this.commsEst < this.comms) { this.commsEst += n; if (this.commsEst > this.comms) {this.commsEst = this.comms;} } else if (this.commsEst > this.comms) { this.commsEst -= n; if (this.commsEst < this.comms) {this.commsEst = this.comms;} } } //@params options: // estChange(int): How much the estimate should change by // estOffset(int): Add offset to estimate (offset by percentage) City.prototype.changePopulationByCount = function(n, params={}) { if (isNaN(n)) {throw new Error("NaN passed into City.changePopulationByCount()");} this.pop += n; if (params.estChange && !isNaN(params.estChange)) {this.popEst += params.estChange;} if (params.estOffset) { this.popEst = addOffset(this.popEst, params.estOffset); } this.popEst = Math.max(this.popEst, 0); } //@p is the percentage, not the multiplier. e.g. pass in p = 5 for 5% //@params options: // changeEstEqually(bool) - Change the population estimate by an equal amount // nonZero (bool) - Set to true to ensure that population always changes by at least 1 City.prototype.changePopulationByPercentage = function(p, params={}) { if (isNaN(p)) {throw new Error("NaN passed into City.changePopulationByPercentage()");} if (p === 0) {return;} var change = Math.round(this.pop * (p/100)); //Population always changes by at least 1 if (params.nonZero && change === 0) { p > 0 ? change = 1 : change = -1; } this.pop += change; if (params.changeEstEqually) { this.popEst += change; if (this.popEst < 0) {this.popEst = 0;} } return change; } City.prototype.changeChaosByCount = function(n) { if (isNaN(n)) {throw new Error("NaN passed into City.changeChaosByCount()");} if (n === 0) {return;} this.chaos += n; if (this.chaos < 0) {this.chaos = 0;} } //@p is the percentage, not the multiplier (e.g. pass in p = 5 for 5%) City.prototype.changeChaosByPercentage = function(p) { if (isNaN(p)) {throw new Error("NaN passed into City.chaosChaosByPercentage()");} if (p === 0) {return;} var change = this.chaos * (p/100); this.chaos += change; if (this.chaos < 0) {this.chaos = 0;} } City.prototype.toJSON = function() { return Generic_toJSON("City", this); } City.fromJSON = function(value) { return Generic_fromJSON(City, value.data); } Reviver.constructors.City = City; function Skill(params={name:"foo", desc:"foo"}) { if (params.name) { this.name = params.name; } else { throw new Error("Failed to initialize Bladeburner Skill. No name was specified in ctor"); } if (params.desc) { this.desc = params.desc; } else { throw new Error("Failed to initialize Bladeburner Skills. No desc was specified in ctor"); } this.baseCost = params.baseCost ? params.baseCost : 1; //Cost is in Skill Points this.costInc = params.costInc ? params.costInc : 1; //Additive cost increase per level if (params.maxLvl) {this.maxLvl = params.maxLvl;} //These benefits are additive. So total multiplier will be level (handled externally) times the //effects below if (params.successChanceAll) {this.successChanceAll = params.successChanceAll;} if (params.successChanceStealth) {this.successChanceStealth = params.successChanceStealth;} if (params.successChanceKill) {this.successChanceKill = params.successChanceKill;} if (params.successChanceContract) {this.successChanceContract = params.successChanceContract;} if (params.successChanceOperation) {this.successChanceOperation = params.successChanceOperation;} //This multiplier affects everything that increases synthoid population/community estimate //e.g. Field analysis, Investigation Op, Undercover Op if (params.successChanceEstimate) {this.successChanceEstimate = params.successChanceEstimate;} if (params.actionTime) {this.actionTime = params.actionTime;} if (params.effHack) {this.effHack = params.effHack;} if (params.effStr) {this.effStr = params.effStr;} if (params.effDef) {this.effDef = params.effDef;} if (params.effDex) {this.effDex = params.effDex;} if (params.effAgi) {this.effAgi = params.effAgi;} if (params.effCha) {this.effCha = params.effCha;} if (params.stamina) {this.stamina = params.stamina;} //Equipment if (params.weaponAbility) {this.weaponAbility = params.weaponAbility;} if (params.gunAbility) {this.gunAbility = params.gunAbility;} } var Skills = {}; var SkillNames = { BladesIntuition: "Blade's Intuition", Reaper: "Reaper", Cloak: "Cloak", Marksman: "Marksman", WeaponProficiency: "Weapon Proficiency", Overclock: "Overclock", EvasiveSystem: "Evasive System", ShortCircuit: "Short-Circuit", DigitalObserver: "Digital Observer", Datamancer: "Datamancer", Tracer: "Tracer", CybersEdge: "Cyber's Edge" } //Base Class for Contracts, Operations, and BlackOps function Action(params={}) { this.name = params.name ? params.name : ""; this.desc = params.desc ? params.desc : ""; //Difficulty scales with level //Exact formula is not set in stone //Initial design: baseDifficulty * (difficultyFac ^ level)? //difficulty Fac is slightly greater than 1 this.level = 1; this.maxLevel = 1; this.autoLevel = true; this.baseDifficulty = params.baseDifficulty ? addOffset(params.baseDifficulty, 10) : 100; this.difficultyFac = params.difficultyFac ? params.difficultyFac : 1.01; //Rank increase/decrease is affected by this exponent this.rewardFac = params.rewardFac ? params.rewardFac : 1.02; this.successes = 0; this.failures = 0; //All of these scale with level/difficulty this.rankGain = params.rankGain ? params.rankGain : 0; if (params.rankLoss) {this.rankLoss = params.rankLoss;} if (params.hpLoss) { this.hpLoss = params.hpLoss; this.hpLost = 0; } //Action Category. Current categories are stealth and kill this.isStealth = params.isStealth ? true : false; this.isKill = params.isKill ? true : false; //Number of this contract remaining, and its growth rate //Growth rate is an integer and the count will increase by that integer every "cycle" this.count = params.count ? params.count : getRandomInt(1e3, 25e3); this.countGrowth = params.countGrowth ? params.countGrowth : getRandomInt(1, 5); //Weighting of each stat in determining action success rate var defaultWeights = {hack:1/7,str:1/7,def:1/7,dex:1/7,agi:1/7,cha:1/7,int:1/7}; this.weights = params.weights ? params.weights : defaultWeights; //Check to make sure weights are summed properly var sum = 0; for (var weight in this.weights) { if (this.weights.hasOwnProperty(weight)) { sum += this.weights[weight]; } } if (sum - 1 >= 10 * Number.EPSILON) { throw new Error("Invalid weights when constructing Action " + this.name + ". The weights should sum up to 1. They sum up to :" + 1); } //Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1) var defaultDecays = {hack:0.9,str:0.9,def:0.9,dex:0.9,agi:0.9,cha:0.9,int:0.9}; this.decays = params.decays ? params.decays : defaultDecays; for (var decay in this.decays) { if (this.decays.hasOwnProperty(decay)) { if (this.decays[decay] > 1) { throw new Error("Invalid decays when constructing " + "Action " + this.name + ". " + "Decay value cannot be greater than 1"); } } } } Action.prototype.getDifficulty = function() { var difficulty = this.baseDifficulty * Math.pow(this.difficultyFac, this.level-1); if (isNaN(difficulty)) {throw new Error("Calculated NaN in Action.getDifficulty()");} return difficulty; } //@inst - Bladeburner Object //@params - options: // est (bool): Get success chance estimate instead of real success chance Action.prototype.getSuccessChance = function(inst, params={}) { if (inst == null) {throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");} var difficulty = this.getDifficulty(); var competence = 0; for (var stat in this.weights) { if (this.weights.hasOwnProperty(stat)) { var playerStatLvl = Player.queryStatFromString(stat); var key = "eff" + stat.charAt(0).toUpperCase() + stat.slice(1); var effMultiplier = inst.skillMultipliers[key]; if (effMultiplier == null) { console.log("ERROR: Failed to find Bladeburner Skill multiplier for: " + stat); effMultiplier = 1; } competence += (this.weights[stat] * Math.pow(effMultiplier*playerStatLvl, this.decays[stat])); } } competence *= inst.calculateStaminaPenalty(); //For Operations, factor in team members if (this instanceof Operation || this instanceof BlackOperation) { if (this.teamCount && this.teamCount > 0) { this.teamCount = Math.min(this.teamCount, inst.teamSize); var teamMultiplier = Math.pow(this.teamCount, 0.05); competence *= teamMultiplier; } } //Lower city population results in lower chances if (!(this instanceof BlackOperation)) { var city = inst.getCurrentCity(); if (params.est) { competence *= (city.popEst / PopulationThreshold); } else { competence *= (city.pop / PopulationThreshold); } //Too high of a chaos results in lower chances if (city.chaos > ChaosThreshold) { var diff = 1 + (city.chaos - ChaosThreshold); var mult = Math.pow(diff, 0.1); difficulty *= mult; } //For Raid Operations, no communities = fail if (this instanceof Operation && this.name === "Raid") { if (city.comms <= 0) {return 0;} } } //Factor skill multipliers into success chance competence *= inst.skillMultipliers.successChanceAll; if (this instanceof Operation || this instanceof BlackOperation) { competence *= inst.skillMultipliers.successChanceOperation; } if (this instanceof Contract) { competence *= inst.skillMultipliers.successChanceContract; } if (this.isStealth) { competence *= inst.skillMultipliers.successChanceStealth; } if (this.isKill) { competence *= inst.skillMultipliers.successChanceKill; } //Augmentation multiplier competence *= Player.bladeburner_success_chance_mult; if (isNaN(competence)) {throw new Error("Competence calculated as NaN in Action.getSuccessChance()");} return Math.min(1, competence / difficulty); } //Tests for success. Should be called when an action has completed // @inst - Bladeburner Object Action.prototype.attempt = function(inst) { return (Math.random() < this.getSuccessChance(inst)); } Action.prototype.getActionTime = function(inst) { var difficulty = this.getDifficulty(); var baseTime = difficulty / DifficultyToTimeFactor; var skillFac = inst.skillMultipliers.actionTime; //Always < 1 var effAgility = Player.agility * inst.skillMultipliers.effAgi; var effDexterity = Player.dexterity * inst.skillMultipliers.effDex; var statFac = 0.5 * (Math.pow(effAgility, 0.03) + Math.pow(effDexterity, 0.03)); //Always > 1 baseTime = Math.max(1, baseTime * skillFac / statFac); if (this instanceof Contract) { return Math.ceil(baseTime); } else if (this instanceof Operation) { return Math.ceil(baseTime); } else if (this instanceof BlackOperation) { return Math.ceil(baseTime * 1.5); } else { throw new Error("Unrecognized Action Type in Action.getActionTime(this). Must be either Contract, Operation, or BlackOperation"); } } Action.prototype.toJSON = function() { return Generic_toJSON("Action", this); } Action.fromJSON = function(value) { return Generic_fromJSON(Action, value.data); } Reviver.constructors.Action = Action; var GeneralActions = {}; //Training, Field Analysis, Recruitment, etc. //Action Identifier var ActionTypes = Object.freeze({ "Idle": 1, "Contract": 2, "Operation": 3, "BlackOp": 4, "BlackOperation": 4, "Training": 5, "Recruitment": 6, "FieldAnalysis": 7, "Field Analysis": 7 }); function ActionIdentifier(params={}) { if (params.name) {this.name = params.name;} if (params.type) {this.type = params.type;} } ActionIdentifier.prototype.toJSON = function() { return Generic_toJSON("ActionIdentifier", this); } ActionIdentifier.fromJSON = function(value) { return Generic_fromJSON(ActionIdentifier, value.data); } Reviver.constructors.ActionIdentifier = ActionIdentifier; //Contracts function Contract(params={}) { Action.call(this, params); } Contract.prototype = Object.create(Action.prototype); Contract.prototype.toJSON = function() { return Generic_toJSON("Contract", this); } Contract.fromJSON = function(value) { return Generic_fromJSON(Contract, value.data); } Reviver.constructors.Contract = Contract; //Operations function Operation(params={}) { Action.call(this, params); this.reqdRank = params.reqdRank ? params.reqdRank : 100; this.teamCount = params.teamCount ? params.teamCount : 0; //# of team members to use } Operation.prototype = Object.create(Action.prototype); Operation.prototype.toJSON = function() { return Generic_toJSON("Operation", this); } Operation.fromJSON = function(value) { return Generic_fromJSON(Operation, value.data); } Reviver.constructors.Operation = Operation; //Black Operations function BlackOperation(params={}) { Operation.call(this, params); //Black ops are one time missions this.count = 1; this.countGrowth = 0; } BlackOperation.prototype = Object.create(Action.prototype); BlackOperation.prototype.toJSON = function() { return Generic_toJSON("BlackOperation", this); } BlackOperation.fromJSON = function(value) { return Generic_fromJSON(BlackOperation, value.data); } Reviver.constructors.BlackOperation = BlackOperation; var BlackOperations = {}; function Bladeburner(params={}) { this.numHosp = 0; //Number of hospitalizations this.moneyLost = 0; //Money lost due to hospitalizations this.rank = 0; this.maxRank = 0; //Used to determine skill points this.skillPoints = 0; this.totalSkillPoints = 0; this.teamSize = 0; //Number of team members this.teamLost = 0; //Number of team members lost this.storedCycles = 0; this.randomEventCounter = getRandomInt(300, 600); //5-10 minutes //These times are in seconds this.actionTimeToComplete = 0; //0 or -1 is an infinite running action (like training) this.actionTimeCurrent = 0; //ActionIdentifier Object var idleActionType = ActionTypes["Idle"]; this.action = new ActionIdentifier({type:idleActionType}); this.cities = {}; for (var i = 0; i < CityNames.length; ++i) { this.cities[CityNames[i]] = new City({name:CityNames[i]}); } this.city = Locations.Sector12; //Map of SkillNames -> level this.skills = {}; this.skillMultipliers = {}; this.updateSkillMultipliers(); //Calls resetSkillMultipliers() //Max Stamina is based on stats and Bladeburner-specific bonuses this.staminaBonus = 0; //Gained from training this.maxStamina = 0; this.calculateMaxStamina(); this.stamina = this.maxStamina; //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. this.contracts = {}; this.operations = {}; //Object that contains name of all Black Operations that have been completed this.blackops = {}; //Flags for whether these actions should be logged to console this.logging = { general:true, contracts:true, ops:true, blackops:true, events:true, } //Simple automation values this.automateEnabled = false; this.automateActionHigh = 0; this.automateThreshHigh = 0; //Stamina Threshold this.automateActionLow = 0; this.automateThreshLow = 0; //Stamina Threshold //Initialization initBladeburner(); this.initializeDomElementRefs(); if (params.new) {this.create();} } 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.

" + "Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for " + "whatever city you are currently in.", baseDifficulty:125,difficultyFac:1.02,rewardFac:1.041, rankGain:0.3, hpLoss:0.5, count:getRandomInt(300, 800), countGrowth:getRandomInt(1, 5), 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.

" + "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, count:getRandomInt(200, 750), countGrowth:getRandomInt(1, 3), 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.

" + "Successfully copmleting a Retirement contract will lower the population in your current " + "city, and will also increase its chaos level.", baseDifficulty:200, difficultyFac:1.03, rewardFac:1.065, rankGain:0.6, hpLoss:1, count:getRandomInt(300, 900), countGrowth:getRandomInt(1,4), 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.

Successful " + "Investigation ops will increase the accuracy of your " + "synthoid data.

" + "You will NOT lose HP from failed Investigation ops.", baseDifficulty:400, difficultyFac:1.03,rewardFac:1.07,reqdRank:25, rankGain:2, rankLoss:0.2, count:getRandomInt(50, 400), countGrowth:1, 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.

" + "Successful Undercover ops will increase the accuracy of your synthoid " + "data.", baseDifficulty:500, difficultyFac:1.04, rewardFac:1.09, reqdRank:100, rankGain:4, rankLoss:0.4, hpLoss:2, count:getRandomInt(50, 300), countGrowth:1, 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, rankGain:5, rankLoss:0.5, hpLoss:2.5, count:getRandomInt(25,400), countGrowth:0.75, 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, rankGain:50,rankLoss:2.5,hpLoss:50, count:getRandomInt(25, 150), countGrowth:0.2, 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:20e3, rankGain:20, rankLoss:2, hpLoss:10, count:getRandomInt(25, 250), countGrowth:0.1, 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:50e3, rankGain:40, rankLoss:4, hpLoss:5, count:getRandomInt(25, 200), countGrowth:0.1, 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; } Bladeburner.prototype.process = function() { //If the Player starts doing some other actions, set action to idle and alert if (Player.isWorking) { if (this.action.type !== ActionTypes["Idle"]) { dialogBoxCreate("Your Bladeburner action was cancelled because you started " + "doing something else"); } this.resetAction(); } //A 'tick' for this mechanic is one second (= 5 game cycles) if (this.storedCycles >= CyclesPerSecond) { var seconds = Math.floor(this.storedCycles / CyclesPerSecond); seconds = Math.min(seconds, 5); //Max of 5 'ticks' this.storedCycles -= seconds * CyclesPerSecond; //Stamina this.calculateMaxStamina(); this.stamina += (this.calculateStaminaGainPerSecond() * seconds); this.stamina = Math.min(this.maxStamina, this.stamina); //Count increase for contracts/operations for (var contractName in this.contracts) { if (this.contracts.hasOwnProperty(contractName)) { var contract = this.contracts[contractName]; contract.count += (seconds * contract.countGrowth/ActionCountGrowthPeriod); } } for (var operationName in this.operations) { if (this.operations.hasOwnProperty(operationName)) { var op = this.operations[operationName]; op.count += (seconds * op.countGrowth/ActionCountGrowthPeriod); } } //Chaos goes down very slowly for (var i = 0; i < CityNames.length; ++i) { var city = this.cities[CityNames[i]]; 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); } //Random Events this.randomEventCounter -= seconds; if (this.randomEventCounter <= 0) { this.randomEvent(); this.randomEventCounter = getRandomInt(300, 600); } this.processAction(seconds); //Automation if (this.automateEnabled) { if (this.stamina <= this.automateThreshLow) { if (this.action.name !== this.automateActionLow.name || this.action.type !== this.automateActionLow.type) { this.action = this.automateActionLow; this.startAction(this.action); } } else if (this.stamina >= this.automateThreshHigh) { if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) { this.action = this.automateActionHigh; this.startAction(this.action); } } } if (Engine.currentPage === Engine.Page.Bladeburner) { this.updateContent(); } } } Bladeburner.prototype.calculateMaxStamina = function() { var effAgility = Player.agility * this.skillMultipliers.effAgi; var maxStamina = (Math.pow(effAgility, 0.8) + this.staminaBonus); maxStamina *= this.skillMultipliers.stamina; maxStamina *= Player.bladeburner_max_stamina_mult; if (isNaN(maxStamina)) {throw new Error("Max Stamina calculated to be NaN in Bladeburner.calculateMaxStamina()");} this.maxStamina = maxStamina; } Bladeburner.prototype.calculateStaminaGainPerSecond = function() { var effAgility = Player.agility * this.skillMultipliers.effAgi; var maxStaminaBonus = this.maxStamina / MaxStaminaToGainFactor; var gain = (StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17); 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) { bladeburnerFac.playerReputation += (RankToFactionRepFactor * change * Player.faction_rep_mult); } } //Gain skill points. You get 1 every 4 ranks var rankNeededForSp = (this.totalSkillPoints+1) * RanksPerSkillPoint; if (this.maxRank >= rankNeededForSp) { //Calculate how many skill points to gain var gainedSkillPoints = Math.floor((this.maxRank - rankNeededForSp) / RanksPerSkillPoint + 1); 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 = { 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, weaponAbility:1, gunAbility:1, }; } 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) { //This does NOT handle deduction of skill points 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) { //Given an ActionIdentifier object, returns the corresponding //Contract, Operation, or BlackOperation object switch (actionId.type) { case ActionTypes["Contract"]: return this.contracts[actionId.name]; break; case ActionTypes["Operation"]: return this.operations[actionId.name]; break; case ActionTypes["BlackOp"]: case ActionTypes["BlackOperation"]: return BlackOperations[actionId.name]; break; default: return null; console.log("WARNING: Bladeburner.getActionObject() called with an unexpected " + "ActionIdentifier type: " + actionId.type); } } //Sets the player to the "IDLE" action Bladeburner.prototype.resetAction = function() { this.action = new ActionIdentifier({type:ActionTypes.Idle}); } Bladeburner.prototype.startAction = function(actionId) { 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();} this.actionTimeToComplete = action.getActionTime(this); } catch(e) { exceptionAlert(e); } break; case ActionTypes["BlackOp"]: case ActionTypes["BlackOperation"]: try { 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["Training"]: this.actionTimeToComplete = 30; break; case ActionTypes["Recruitment"]: var effCharisma = Player.charisma * this.skillMultipliers.effCha; var charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90; var time = Math.max(10, Math.round(BaseRecruitmentTimeNeeded - charismaFactor)); this.actionTimeToComplete = time; break; case ActionTypes["FieldAnalysis"]: case ActionTypes["Field Analysis"]: this.actionTimeToComplete = 30; break; 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) { console.log("action.type: " + this.action.type); throw new Error("Invalid actionTimeToComplete value: " + this.actionTimeToComplete); } 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(); var difficultyMultiplier = Math.pow(difficulty, DiffMultExponentialFactor) + difficulty / DiffMultLinearFactor; var rewardMultiplier = Math.pow(action.rewardFac, action.level-1); //Stamina loss is based on difficulty this.stamina -= (BaseStaminaLoss * difficultyMultiplier); if (this.stamina < 0) {this.stamina = 0;} //Process Contract/Operation success/failure if (action.attempt(this)) { this.gainActionStats(action, true); ++action.successes; --action.count; //Earn money for contracts var moneyGain = 0; if (!isOperation) { moneyGain = ContractBaseMoneyGain * rewardMultiplier; Player.gainMoney(moneyGain); } if (isOperation) { action.maxLevel = Math.floor(action.successes / OperationSuccessesPerLevel) + 1; } else { action.maxLevel = Math.floor(action.successes / ContractSuccessesPerLevel) + 1; } if (action.rankGain) { var gain = addOffset(action.rankGain * rewardMultiplier, 10); 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) { this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeral(moneyGain).format("$0.000a")); } } 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; if (Player.takeDamage(damage)) { ++this.numHosp; this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); } } 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); } if (action.autoLevel) {action.level = action.maxLevel;} //Autolevel this.startAction(this.action); //Repeat action } 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(); var difficultyMultiplier = Math.pow(difficulty, DiffMultExponentialFactor) + difficulty / DiffMultLinearFactor; //Stamina loss is based on difficulty this.stamina -= (BaseStaminaLoss * difficultyMultiplier); if (this.stamina < 0) {this.stamina = 0;} //Team loss variables 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) { rankGain = addOffset(action.rankGain, 10); this.changeRank(rankGain); } teamLossMax = Math.ceil(teamCount/2); //Operation Daedalus if (action.name === "Operation Daedalus") { this.resetAction(); return hackWorldDaemon(Player.bitNodeN); } if (Engine.currentPage === Engine.Page.Bladeburner) { this.createActionAndSkillsContent(); } 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)); if (Player.takeDamage(damage)) { ++this.numHosp; this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); } } teamLossMax = Math.floor(teamCount); if (this.logging.blackops) { this.log(action.name + " failed! Lost " + formatNumber(rankLoss, 1) + " rank and took" + formatNumber(damage, 0) + " damage"); } } this.resetAction(); //Stop regardless of success or fail //Calculate team lossses 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"]: this.stamina -= (0.5 * BaseStaminaLoss); 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, 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"); } this.startAction(this.action); //Repeat action break; case ActionTypes["FieldAnalysis"]: case ActionTypes["Field Analysis"]: //Does not use stamina. Effectiveness depends on hacking, int, and cha 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); Player.gainIntelligenceExp(BaseIntGain); Player.gainCharismaExp(charismaExpGain); this.changeRank(0.1); console.log("DEBUG: Field Analysis effectiveness is " + (eff * this.skillMultipliers.successChanceEstimate)); this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate); if (this.logging.general) { this.log("Field analysis completed. Gained 0.1 rank, " + formatNumber(hackingExpGain, 1) + " hacking exp, and " + formatNumber(charismaExpGain, 1) + " charisma exp"); } this.startAction(this.action); //Repeat action break; case ActionTypes["Recruitment"]: var successChance = Math.pow(Player.charisma, 0.45) / (this.teamSize + 1); console.log("Bladeburner recruitment success chance: " + successChance); if (Math.random() < successChance) { var expGain = 2 * BaseStatGain * this.actionTimeToComplete; Player.gainCharismaExp(expGain); ++this.teamSize; if (this.logging.general) { this.log("Successfully recruited a team member! Gained " + formatNumber(expGain, 1) + " charisma exp"); } } else { var expGain = BaseStatGain * this.actionTimeToComplete; Player.gainCharismaExp(expGain); if (this.logging.general) { this.log("Failed to recruit a team member. Gained " + formatNumber(expGain, 1) + " charisma exp"); } } this.startAction(this.action); //Repeat action break; default: 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": //Increase estimate accuracy by a relatively small amount city.improvePopulationEstimateByCount(getRandomInt(100, 1e3)); 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); } //Calculate team losses 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(); if (this.logging.ops) { if (success) { this.log(action.name + " completed successfully! ") } else { } } 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 { var change = getRandomInt(-3, -1); 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); } } //Process stat gains from Contracts, Operations, and Black Operations //@action(Action obj) - Derived action class //@success(bool) - Whether action was successful Bladeburner.prototype.gainActionStats = function(action, success) { var difficulty = action.getDifficulty(); //Gain multiplier based on difficulty. If this changes then the //same variable calculated in completeAction() needs to change too var difficultyMult = Math.pow(difficulty, 0.21); var time = this.actionTimeToComplete; var successMult = success ? 1 : 0.5; var unweightedGain = time * BaseStatGain * successMult * difficultyMult; var unweightedIntGain = time * BaseIntGain * successMult * difficultyMult; Player.gainHackingExp(unweightedGain * action.weights.hack * Player.hacking_exp_mult); Player.gainStrengthExp(unweightedGain * action.weights.str * Player.strength_exp_mult); Player.gainDefenseExp(unweightedGain * action.weights.def * Player.defense_exp_mult); Player.gainDexterityExp(unweightedGain * action.weights.dex * Player.dexterity_exp_mult); Player.gainAgilityExp(unweightedGain * action.weights.agi * Player.agility_exp_mult); Player.gainCharismaExp(unweightedGain * action.weights.cha * Player.charisma_exp_mult); Player.gainIntelligenceExp(unweightedIntGain * action.weights.int); } Bladeburner.prototype.randomEvent = function() { var chance = Math.random(); //Choose random source/destination city for events var sourceCityName = CityNames[getRandomInt(0, 5)]; var sourceCity = this.cities[sourceCityName]; if (!(sourceCity instanceof City)) { throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()"); } var destCityName = CityNames[getRandomInt(0, 5)]; while (destCityName === sourceCityName) { destCityName = CityNames[getRandomInt(0, 5)]; } var destCity = this.cities[destCityName]; if (!(sourceCity instanceof City) || !(destCity instanceof City)) { throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()"); } if (chance <= 0.05) { //New Synthoid Community, 5% ++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) { //Synthoid Community Migration, 5% if (sourceCity.comms <= 0) { //If no comms in source city, then instead trigger a new Synthoid community event ++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; //Change pop 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) { //New Synthoids (non community), 20% 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) { //Synthoid migration (non community) 20% 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) { //Synthoid Riots (+chaos), 20% sourceCity.chaos += 1; sourceCity.chaos *= (1 + getRandomInt(5, 10) / 100); if (this.logging.events) { this.log("Tensions between Synthoids and humans lead to riots in " + sourceCityName + "! Chaos increased"); } } else if (chance <= 0.9) { //Less Synthoids, 20% var percentage = getRandomInt(5, 20) / 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"); } } //20% chance of nothing happening } Bladeburner.prototype.triggerPotentialMigration = function(sourceCityName, chance) { if (chance == null || isNaN(chance)) { console.log("ERROR: Invalid 'chance' parameter passed into Bladeburner.triggerPotentialMigration()"); } if (chance > 1) {chance /= 100;} if (Math.random() < chance) {this.triggerMigration(sourceCityName);} } Bladeburner.prototype.triggerMigration = function(sourceCityName) { var destCityName = CityNames[getRandomInt(0, 5)]; while (destCityName === sourceCityName) { destCityName = CityNames[getRandomInt(0, 5)]; } 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; if (rand < 0.05 && sourceCity.comms > 0) { //5% chance for community migration percentage *= getRandomInt(2, 4); //Migration increases population change --sourceCity.comms; ++destCity.comms; } var count = Math.round(sourceCity.pop * percentage); sourceCity.pop -= count; destCity.pop += count; } var DomElems = {}; Bladeburner.prototype.initializeDomElementRefs = function() { DomElems = { bladeburnerDiv: null, //Main Divs overviewConsoleParentDiv: null, 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 consoleDiv: null, consoleTable: null, consoleInputRow: null, //tr consoleInputCell: null, //td consoleInputHeader: null, //"> " consoleInput: null, //Actual input element //Overview Content overviewRank: null, overviewStamina: null, overviewStaminaHelpTip: null, overviewGen1: null, //Stamina Penalty, Team, Hospitalized stats, current city overviewEstPop: null, overviewEstPopHelpTip: null, overviewEstComms: null, overviewChaos: null, overviewSkillPoints: null, overviewAugSuccessMult: null, overviewAugMaxStaminaMult: null, overviewAugStaminaGainMult: null, overviewAugAnalysisMult: null, //Actions and Skills Content actionsAndSkillsDesc: null, actionsAndSkillsList: null, //ul element of all UI elements in this panel generalActions: {}, contracts: {}, operations: {}, blackops: {}, skills: {}, }; } Bladeburner.prototype.createContent = function() { DomElems.bladeburnerDiv = createElement("div", { id:"bladeburner-container", position:"fixed", class:"generic-menupage-container", }); //Parent Div for Overview and Console DomElems.overviewConsoleParentDiv = createElement("div", { height:"60%", display:"block", position:"relative", }); //Overview and Action/Skill pane 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(); //Console DomElems.consoleDiv = createElement("div", { class:"bladeburner-console-div", clickListener:()=>{ if (DomElems.consoleInput instanceof Element) { DomElems.consoleInput.focus(); } return false; } }); DomElems.consoleTable = createElement("table", {class:"bladeburner-console-table"}); DomElems.consoleInputRow = createElement("tr", {class:"bladeburner-console-input-row", id:"bladeubrner-console-input-row"}); 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, onfocus:()=>{DomElems.consoleInput.value = DomElems.consoleInput.value} }); 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); document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv); this.postToConsole("Bladeburner Console BETA"); 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", { innerText:"?", class:"help-tip", clickListener:()=>{ dialogBoxCreate("Performing actions will use up your stamina.

" + "Your max stamina is determined primarily by your agility stat.

" + "Your stamina gain rate is determined by both your agility and your " + "max stamina. Higher max stamina leads to a higher gain rate.

" + "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).

" + "Your max stamina and stamina gain rate can also be increased by " + "training, or through skills and Augmentation upgrades."); } }); 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", clickListener:()=>{ 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.

" + "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.

" + "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." }); DomElems.overviewSkillPoints = createElement("p", {display:"block"}); DomElems.overviewAugSuccessMult = createElement("p", {display:"block"}); DomElems.overviewAugMaxStaminaMult = createElement("p", {display:"block"}); DomElems.overviewAugStaminaGainMult = createElement("p", {display:"block"}); DomElems.overviewAugAnalysisMult = createElement("p", {display:"block"}); 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); DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints); appendLineBreaks(DomElems.overviewDiv, 1); DomElems.overviewDiv.appendChild(DomElems.overviewAugSuccessMult); DomElems.overviewDiv.appendChild(DomElems.overviewAugMaxStaminaMult); DomElems.overviewDiv.appendChild(DomElems.overviewAugStaminaGainMult); DomElems.overviewDiv.appendChild(DomElems.overviewAugAnalysisMult); //Travel to new city button appendLineBreaks(DomElems.overviewDiv, 1); DomElems.overviewDiv.appendChild(createElement("a", { innerHTML:"Travel", class:"a-link-button", display:"inline-block", clickListener:()=>{ var popupId = "bladeburner-travel-popup-cancel-btn"; var popupArguments = []; popupArguments.push(createElement("a", { //Cancel Button innerText:"Cancel", class:"a-link-button", clickListener:()=>{ removeElementById(popupId); return false; } })) popupArguments.push(createElement("p", { //Info Text 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", })); for (var i = 0; i < CityNames.length; ++i) { (function(inst, i) { popupArguments.push(createElement("div", { //Reusing this css class...it adds a border and makes it //so that background color changes when you hover class:"cmpy-mgmt-find-employee-option", innerText:CityNames[i], clickListener:()=>{ inst.city = CityNames[i]; removeElementById(popupId); inst.updateOverviewContent(); return false; } })); })(this, i); } createPopup(popupId, popupArguments); } })); //Faction button 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"); } 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", clickListener:()=>{ if (bladeburnerFac.isMember) { Engine.loadFactionContent(); displayFactionContent(bladeburnersFactionName); } else { if (this.rank >= 25) { 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", clickListener:()=>{ DomElems.currentTab = buttons[i].toLowerCase(); inst.createActionAndSkillsContent(); return false; } })); }) (buttons, i, this, currTab); } //General info/description for each action DomElems.actionsAndSkillsDesc = createElement("p", { display:"block", margin:"4px", padding:"4px" }); //List for actions/skills 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 = "Complete contracts in order to increase your Bitburner rank and earn money. " + "Failing a contract will cause you to lose HP, which can lead to hospitalization.

" + "You can unlock higher-level contracts by successfully completing them. " + "Higher-level contracts are more difficult, but grant more rank, experience, and money."; 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.

" + "Operations can affect the chaos level and Synthoid population of your " + "current city. The exact effects vary between different Operations.

" + "For operations, you can use a team. You must first recruit team members. " + "Having a larger team will improves your chances of success.

" + "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() { if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) { throw new Error("Bladeburner.createBlackOpsContent called with either " + "DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null"); } 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.

" + "Like normal operations, you may use a team for Black Ops. Failing " + "a black op will incur heavy HP and rank losses."; //Put Black Operations in sequence of required rank 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); }); for (var i = 0; i < blackops.length; ++i) { DomElems.blackops[blackops[i].name] = createElement("div", { class:"bladeburner-action", name:blackops[i].name }); DomElems.actionsAndSkillsList.appendChild(DomElems.blackops[blackops[i].name]); if (this.blackops[[blackops[i].name]] == null) {break;} //Can't be found in completed blackops } } 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"); } //Display Current multipliers DomElems.actionsAndSkillsDesc.innerHTML = "You will gain one skill point every " + RanksPerSkillPoint + " ranks.

" + "Note that when upgrading a skill, the benefit for that skill is additive. " + "However, the effects of different skills with each other is multiplicative.

" 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 + "
"; break; case "successChanceStealth": DomElems.actionsAndSkillsDesc.innerHTML += "Stealth Success Chance: x" + mult + "
"; break; case "successChanceKill": DomElems.actionsAndSkillsDesc.innerHTML += "Retirement Success Chance: x" + mult + "
"; break; case "successChanceContract": DomElems.actionsAndSkillsDesc.innerHTML += "Contract Success Chance: x" + mult + "
"; break; case "successChanceOperation": DomElems.actionsAndSkillsDesc.innerHTML += "Operation Success Chance: x" + mult + "
"; break; case "successChanceEstimate": DomElems.actionsAndSkillsDesc.innerHTML += "Synthoid Data Estimate: x" + mult + "
"; break; case "actionTime": DomElems.actionsAndSkillsDesc.innerHTML += "Action Time: x" + mult + "
"; break; case "effHack": DomElems.actionsAndSkillsDesc.innerHTML += "Hacking Skill: x" + mult + "
"; break; case "effStr": DomElems.actionsAndSkillsDesc.innerHTML += "Strength: x" + mult + "
"; break; case "effDef": DomElems.actionsAndSkillsDesc.innerHTML += "Defense: x" + mult + "
"; break; case "effDex": DomElems.actionsAndSkillsDesc.innerHTML += "Dexterity: x" + mult + "
"; break; case "effAgi": DomElems.actionsAndSkillsDesc.innerHTML += "Agility: x" + mult + "
"; break; case "effCha": DomElems.actionsAndSkillsDesc.innerHTML += "Charisma: x" + mult + "
"; break; case "effInt": DomElems.actionsAndSkillsDesc.innerHTML += "Intelligence: x" + mult + "
"; break; case "stamina": DomElems.actionsAndSkillsDesc.innerHTML += "Stamina: x" + mult + "
"; break; case "weaponAbility": //DomElems.actionsAndSkillsDesc.innerHTML += break; case "gunAbility": //DomElems.actionsAndSkillsDesc.innerHTML break; default: console.log("Warning: Unrecognized SkillMult Key: " + multKeys[i]); break; } } } //Skill Points DomElems.actionAndSkillsDiv.appendChild(createElement("p", { innerHTML:"
Skill Points: " + formatNumber(this.skillPoints, 0) + "" })); //UI Element for each skill 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() { if (Engine.currentPage !== Engine.Page.Bladeburner) {return;} DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2); DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3); DomElems.overviewGen1.innerHTML = "Stamina Penalty: " + formatNumber((1-this.calculateStaminaPenalty())*100, 1) + "%

" + "Team Size: " + formatNumber(this.teamSize, 0) + "
" + "Team Members Lost: " + formatNumber(this.teamLost, 0) + "

" + "Num Times Hospitalized: " + this.numHosp + "
" + "Money Lost From Hospitalizations: " + numeral(this.moneyLost).format("$0.000a") + "

" + "Current City: " + this.city + "
"; DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeral(this.getCurrentCity().popEst).format("0.000a"); 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); DomElems.overviewAugSuccessMult.innerText = "Aug. Success Chance Mult: " + formatNumber(Player.bladeburner_success_chance_mult*100, 1) + "%"; DomElems.overviewAugMaxStaminaMult.innerText = "Aug. Max Stamina Mult: " + formatNumber(Player.bladeburner_max_stamina_mult*100, 1) + "%"; DomElems.overviewAugStaminaGainMult.innerText = "Aug. Stamina Gain Mult: " + formatNumber(Player.bladeburner_stamina_gain_mult*100, 1) + "%"; DomElems.overviewAugAnalysisMult.innerText = "Aug. Field Analysis Mult: " + formatNumber(Player.bladeburner_analysis_mult*100, 1) + "%"; } 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": 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); el.appendChild(createElement("h2", { //Header innerText:isActive ? action.name + " (IN PROGRESS - " + formatNumber(this.actionTimeCurrent, 0) + " / " + formatNumber(this.actionTimeToComplete, 0) + ")" : action.name, display:"inline-block", })); if (isActive) { //Progress bar if its active var progress = this.actionTimeCurrent / this.actionTimeToComplete; el.appendChild(createElement("p", { display:"block", innerText:createProgressBarText({progress:progress}) })); } else { //Start button el.appendChild(createElement("a", { innerText:"Start", class: "a-link-button", margin:"3px", padding:"3px", clickListener:()=>{ this.action.type = ActionTypes[action.name]; this.action.name = action.name; this.startAction(this.action); this.updateActionAndSkillsContent(); return false; } })); } appendLineBreaks(el, 2); el.appendChild(createElement("pre", { //Info 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}); el.appendChild(createElement("h2", { //Header innerText:isActive ? action.name + " (IN PROGRESS - " + formatNumber(this.actionTimeCurrent, 0) + " / " + formatNumber(this.actionTimeToComplete, 0) + ")" : action.name, display:"inline-block" })); if (isActive) { //Progress bar if its active var progress = this.actionTimeCurrent / this.actionTimeToComplete; el.appendChild(createElement("p", { display:"block", innerText:createProgressBarText({progress:progress}) })); } else { //Start button el.appendChild(createElement("a", { innerText:"Start", class: "a-link-button", padding:"3px", margin:"3px", clickListener:()=>{ this.action.type = ActionTypes.Contract; this.action.name = action.name; this.startAction(this.action); this.updateActionAndSkillsContent(); return false; } })); } //Level and buttons to change level var maxLevel = (action.level >= action.maxLevel); appendLineBreaks(el, 2); el.appendChild(createElement("pre", { display:"inline-block", innerText:"Level: " + action.level + " / " + action.maxLevel })); 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", clickListener:()=>{ ++action.level; if (isActive) {this.startAction(this.action);} //Restart Action 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", clickListener:()=>{ --action.level; if (isActive) {this.startAction(this.action);} //Restart Action this.updateContractsUIElement(el, action); return false; } })); var actionTime = action.getActionTime(this); appendLineBreaks(el, 2); el.appendChild(createElement("pre", { //Info display:"inline-block", innerHTML:action.desc + "\n\n" + "Estimated success chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" + "Time Required (s): " + formatNumber(actionTime, 0) + "\n" + "Contracts remaining: " + Math.floor(action.count) + "\n" + "Successes: " + action.successes + "\n" + "Failures: " + action.failures, })); //Autolevel Checkbox el.appendChild(createElement("br")); var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox"; el.appendChild(createElement("label", { for:autolevelCheckboxId, innerText:"Autolevel",color:"white", tooltip:"Automatically increase contract level when possible" })); var autolevelCheckbox = createElement("input", { type:"checkbox", id:autolevelCheckboxId, margin:"4px", checked:action.autoLevel, changeListener:()=>{ action.autoLevel = autolevelCheckbox.checked; } }); el.appendChild(autolevelCheckbox); } Bladeburner.prototype.updateOperationsUIElement = function(el, action) { removeChildrenFromElement(el); var isActive = el.classList.contains(ActiveActionCssClass); var estimatedSuccessChance = action.getSuccessChance(this, {est:true}); el.appendChild(createElement("h2", { //Header innerText:isActive ? action.name + " (IN PROGRESS - " + formatNumber(this.actionTimeCurrent, 0) + " / " + formatNumber(this.actionTimeToComplete, 0) + ")" : action.name, display:"inline-block" })); if (isActive) { //Progress bar if its active var progress = this.actionTimeCurrent / this.actionTimeToComplete; el.appendChild(createElement("p", { display:"block", innerText:createProgressBarText({progress:progress}) })); } else { //Start button and set Team Size button el.appendChild(createElement("a", { innerText:"Start", class: "a-link-button", margin:"3px", padding:"3px", clickListener:()=>{ 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", clickListener:()=>{ 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", { type:"number", placeholder: "Team Members" }); var setBtn = createElement("a", { innerText:"Confirm", class:"a-link-button", clickListener:()=>{ var num = Math.round(parseFloat(input.value)); if (isNaN(num)) { dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)") } else { action.teamCount = num; this.updateOperationsUIElement(el, action); } removeElementById(popupId); return false; } }); var cancelBtn = createElement("a", { innerText:"Cancel", class:"a-link-button", clickListener:()=>{ removeElementById(popupId); return false; } }); createPopup(popupId, [txt, input, setBtn, cancelBtn]); } })); } //Level and buttons to change level var maxLevel = (action.level >= action.maxLevel); appendLineBreaks(el, 2); el.appendChild(createElement("pre", { display:"inline-block", innerText:"Level: " + action.level + " / " + action.maxLevel })); 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", clickListener:()=>{ ++action.level; if (isActive) {this.startAction(this.action);} //Restart Action 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", clickListener:()=>{ --action.level; if (isActive) {this.startAction(this.action);} //Restart Action this.updateOperationsUIElement(el, action); return false; } })); //General Info var difficulty = action.getDifficulty(); var actionTime = action.getActionTime(this); appendLineBreaks(el, 2); el.appendChild(createElement("pre", { display:"inline-block", innerHTML:action.desc + "\n\n" + "Estimated success chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" + "Time Required(s): " + formatNumber(actionTime, 1) + "\n" + "Operations remaining: " + Math.floor(action.count) + "\n" + "Successes: " + action.successes + "\n" + "Failures: " + action.failures, })); //Autolevel Checkbox el.appendChild(createElement("br")); var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox"; el.appendChild(createElement("label", { for:autolevelCheckboxId, innerText:"Autolevel",color:"white", tooltip:"Automatically increase operation level when possible" })); var autolevelCheckbox = createElement("input", { type:"checkbox", id:autolevelCheckboxId, margin:"4px", checked:action.autoLevel, changeListener:()=>{ action.autoLevel = autolevelCheckbox.checked; } }); el.appendChild(autolevelCheckbox); } 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; //UI for Completed Black Op if (isCompleted) { el.appendChild(createElement("h2", { innerText:action.name + " (COMPLETED)", display:"block", })); return; } el.appendChild(createElement("h2", { //Header innerText:isActive ? action.name + " (IN PROGRESS - " + formatNumber(this.actionTimeCurrent, 0) + " / " + formatNumber(this.actionTimeToComplete, 0) + ")" : action.name, display:"inline-block", })); if (isActive) { //Progress bar if its active var progress = this.actionTimeCurrent / this.actionTimeToComplete; el.appendChild(createElement("p", { display:"block", innerText:createProgressBarText({progress:progress}) })); } else { el.appendChild(createElement("a", { //Start button innerText:"Start", margin:"3px", padding:"3px", class:hasReqdRank ? "a-link-button" : "a-link-button-inactive", clickListener:()=>{ this.action.type = ActionTypes.BlackOperation; this.action.name = action.name; this.startAction(this.action); this.updateActionAndSkillsContent(); return false; } })); el.appendChild(createElement("a", { //Set Team Size Button innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button", margin:"3px", padding:"3px", clickListener:()=>{ 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", { type:"number", placeholder: "Team Members" }); var setBtn = createElement("a", { innerText:"Confirm", class:"a-link-button", clickListener:()=>{ var num = Math.round(parseFloat(input.value)); if (isNaN(num)) { dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric)") } else { action.teamCount = num; this.updateBlackOpsUIElement(el, action); } removeElementById(popupId); return false; } }); var cancelBtn = createElement("a", { innerText:"Cancel", class:"a-link-button", clickListener:()=>{ removeElementById(popupId); return false; } }); createPopup(popupId, [txt, input, setBtn, cancelBtn]); } })); } //Info appendLineBreaks(el, 2); el.appendChild(createElement("p", { display:"inline-block", innerHTML:"
" + action.desc + "

", })); el.appendChild(createElement("p", { display:"block", color:hasReqdRank ? "white" : "red", innerHTML:"Required Rank: " + formatNumber(action.reqdRank, 0) + "
" })); el.appendChild(createElement("p", { display:"inline-block", innerHTML:"Estimated Success Chance: " + formatNumber(estimatedSuccessChance*100, 1) + "%\n" + "Time Required(s): " + formatNumber(actionTime, 1), })) } 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]; } var pointCost = skill.baseCost + (currentLevel * skill.costInc); el.appendChild(createElement("h2", { //Header innerText:skill.name + " (Lvl " + currentLevel + ")", display:"inline-block" })); var canLevel = this.skillPoints >= pointCost; var maxLvl = skill.maxLvl ? currentLevel >= skill.maxLvl : false; el.appendChild(createElement("a", { //Level up button innerText:"Level", display:"inline-block", class: canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive", margin:"3px", padding:"3px", clickListener:()=>{ this.skillPoints -= pointCost; this.upgradeSkill(skill); this.createActionAndSkillsContent(); return false; } })); appendLineBreaks(el, 2); 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), })); } el.appendChild(createElement("p", { //Info/Description innerHTML:skill.desc, display:"inline-block", })); } //Bladeburner Console Window Bladeburner.prototype.postToConsole = function(input) { if (input == null || DomElems.consoleDiv == null) {return;} $("#bladeubrner-console-input-row").before('' + input + ''); if (DomElems.consoleTable.childNodes.length > 200) { DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); } 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); } } Bladeburner.prototype.log = function(input) { //Adds a timestamp and then just calls postToConsole var d = new Date(); var timestamp = d.getMonth() + "/" + d.getDay() + " " + d.getHours() + ":" + d.getMinutes(); this.postToConsole("[" + timestamp + "] " + input); } //Handles a potential series of commands (comm1; comm2; comm3;) Bladeburner.prototype.executeConsoleCommands = function(commands) { try { //Console History if (consoleHistory[consoleHistory.length-1] != commands) { consoleHistory.push(commands); if (consoleHistory.length > 50) { consoleHistory.splice(0, 1); } } consoleHistoryIndex = consoleHistory.length; var arrayOfCommands = commands.split(";"); for (var i = 0; i < arrayOfCommands.length; ++i) { this.executeConsoleCommand(arrayOfCommands[i]); } } catch(e) { exceptionAlert(e); } } //A single command Bladeburner.prototype.executeConsoleCommand = function(command) { command = command.trim(); command = command.replace(/\s\s+/g, ' '); //Replace all whitespace w/ a single space var args = this.parseCommandArguments(command); if (args.length <= 0) {return;} //Log an error? 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) { //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 var args = []; var start = 0, i = 0; while (i < command.length) { var c = command.charAt(i); if (c === '"') { var 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 { start = i = endQuote+2; //Skip the space } 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));} console.log("Bladeburner.parseCommandArguments returned: " + args); 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; } //Enable/Disable 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; } //Set variables if (args.length === 4) { var variable = args[1], val = args[2]; var highLow = false; //True for high, false for low 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) { this.postToConsole(consoleHelpText.helpList); } else { for (var i = 1; i < args.length; ++i) { var commandText = consoleHelpText[args[i]]; if (commandText != null) { this.postToConsole(commandText); this.postToConsole("
"); } } } } 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; if (args[1].toLowerCase().includes("d")) {flag = false;} //d for disable 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: //Display Skill Help Command 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") { //List all skills and their level 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; case "weaponAbility": //DomElems.actionsAndSkillsDesc.innerHTML += break; case "gunAbility": //DomElems.actionsAndSkillsDesc.innerHTML break; default: console.log("Warning: Unrecognized SkillMult Key: " + multKeys[i]); 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") { this.postToConsole(skill.name + ": Level " + formatNumber(this.skills[skill.name]), 0); } else if (args[1].toLowerCase() === "level") { var currentLevel = 0; if (this.skills[skillName] && !isNaN(this.skills[skillName])) { currentLevel = this.skills[skillName]; } var pointCost = skill.baseCost + (currentLevel * skill.costInc); 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; } } Bladeburner.prototype.toJSON = function() { return Generic_toJSON("Bladeburner", this); } Bladeburner.fromJSON = function(value) { return Generic_fromJSON(Bladeburner, value.data); } Reviver.constructors.Bladeburner = Bladeburner; //This initialized Bladeburner-related data that is NOT saved/loaded // eg: Skill Objects, BLack Operations //Any data that is saved/loaded should go in Bladeburner object // eg: contracts, operations function initBladeburner() { //Skills Skills[SkillNames.BladesIntuition] = new Skill({ name:SkillNames.BladesIntuition, desc:"Each level of this skill increases your success chance " + "for all contracts and operations by 3%", baseCost:5, costInc:2, successChanceAll:3 }); Skills[SkillNames.Reaper] = new Skill({ name:SkillNames.Reaper, desc:"Each level of this skill increases your " + "effective combat stats for Bladeburner actions by 3%", baseCost:3, costInc:2, effStr:3, effDef:3, effDex:3, effAgi:3 }); Skills[SkillNames.Cloak] = new Skill({ name:SkillNames.Cloak, desc:"Each level of this skill increases your " + "success chance in stealth-related contracts and operations by 5.5%", baseCost:3, costInc:1, successChanceStealth:5.5 }); //TODO Marksman //TODO Weapon Proficiency Skills[SkillNames.Overclock] = new Skill({ name:SkillNames.Overclock, desc:"Each level of this skill decreases the time it takes " + "to attempt a contract or operation by 1% (Max Level: 95)", baseCost:5, costInc:1, maxLvl:95, actionTime:1 }); Skills[SkillNames.EvasiveSystem] = new Skill({ name:SkillNames.EvasiveSystem, desc:"Each level of this skill increases your effective " + "dexterity and agility for Bladeburner actions by 5%", baseCost:2, costInc: 1, effDex:5, effAgi:5 }); Skills[SkillNames.ShortCircuit] = new Skill({ name:SkillNames.ShortCircuit, desc:"Each level of this skill increases your success chance " + "in contracts and operations that involve retirement by 5.5%", baseCost:3, costInc:2, successChanceKill:5.5 }); Skills[SkillNames.DigitalObserver] = new Skill({ name:SkillNames.DigitalObserver, desc:"Each level of this skill increases your success chance in " + "all operations by 4%", baseCost:5, costInc:2, successChanceOperation:4 }); Skills[SkillNames.Datamancer] = new Skill({ name:SkillNames.Datamancer, desc:"Each level of this skill increases your effectiveness in " + "synthoid population analysis and investigation by 5%. " + "This affects all actions that can potentially increase " + "the accuracy of your synthoid population/community estimates.", baseCost:3,costInc:1, successChanceEstimate:5 }); Skills[SkillNames.Tracer] = new Skill({ name:SkillNames.Tracer, desc:"Each level of this skill increases your success chance in " + "all contracts by 4%", baseCost:3, costInc:2, successChanceContract:4 }); Skills[SkillNames.CybersEdge] = new Skill({ name:SkillNames.CybersEdge, desc:"Each level of this skill increases your max " + "stamina by 2%", baseCost:1, costInc:3, stamina:2 }); //General Actions var actionName = "Training"; GeneralActions[actionName] = new Action({ name:actionName, desc:"Improve your abilities at the Bladeburner unit's specialized training " + "center. Doing this gives experience for all combat stats and also " + "increases your max stamina." }); var actionName = "Field Analysis"; GeneralActions[actionName] = new Action({ name:actionName, desc:"Mine and analyze Synthoid-related data. This improve the " + "Bladeburner's unit intelligence on Synthoid locations and " + "activities. Completing this action will improve the accuracy " + "of your Synthoid population estimated in the current city.

" + "Does NOT require stamina." }); var actionName = "Recruitment"; GeneralActions[actionName] = new Action({ name:actionName, desc:"Attempt to recruit members for your Bladeburner team. These members " + "can help you conduct operations.

" + "Does NOT require stamina." }); //Black Operations BlackOperations["Operation Typhoon"] = new BlackOperation({ name:"Operation Typhoon", desc:"Obadiah Zenyatta is the leader of a RedWater PMC. It has long " + "been known among the intelligence community that Zenyatta, along " + "with the rest of the PMC, is a Synthoid.

" + "The goal of Operation Typhoon is to find and eliminate " + "Zenyatta and RedWater by any means necessary. After the task " + "is completed, the actions must be covered up from the general public.", baseDifficulty:2000, reqdRank:2.5e3, rankGain:25, rankLoss:10, hpLoss:100, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true }); BlackOperations["Operation Zero"] = new BlackOperation({ name:"Operation Zero", desc:"AeroCorp is one of the world's largest defense contractors. " + "It's leader, Steve Watataki, is thought to be a supporter of " + "Synthoid rights. He must be removed.

" + "The goal of Operation Zero is to covertly infiltrate AeroCorp and " + "uncover any incriminating evidence or " + "information against Watataki that will cause him to be removed " + "from his position at AeroCorp. Incriminating evidence can be " + "fabricated as a last resort. Be warned that AeroCorp has some of " + "the most advanced security measures in the world.", baseDifficulty:2500, reqdRank:5e3, rankGain:30, rankLoss:15, hpLoss:50, weights:{hack:0.2,str:0.15,def:0.15,dex:0.2,agi:0.2,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.75}, isStealth:true }); BlackOperations["Operation X"] = new BlackOperation({ name:"Operation X", desc:"We have recently discovered an underground publication " + "group called Samizdat. Even though most of their publications " + "are nonsensical conspiracy theories, the average human is " + "gullible enough to believe them. Many of their works discuss " + "Synthoids and pose a threat to society. The publications are spreading " + "rapidly in China and other Eastern countries.

" + "Samizdat has done a good job of keeping hidden and anonymous. " + "However, we've just received intelligence that their base of " + "operations is in Ishima's underground sewer systems. Your task is to " + "investigate the sewer systems, and eliminate Samizdat. They must " + "never publish anything again.", baseDifficulty:3000, reqdRank:7.5e3, rankGain:30, rankLoss:15, hpLoss:100, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true }); BlackOperations["Operation Titan"] = new BlackOperation({ name:"Operation Titan", desc:"Several months ago Titan Laboratories' Bioengineering department " + "was infiltrated by Synthoids. As far as we know, Titan Laboratories' " + "management has no knowledge about this. We don't know what the " + "Synthoids are up to, but the research that they could " + "be conducting using Titan Laboraties' vast resources is potentially " + "very dangerous.

" + "Your goal is to enter and destroy the Bioengineering department's " + "facility in Aevum. The task is not just to retire the Synthoids there, but " + "also to destroy any information or research at the facility that " + "is relevant to the Synthoids and their goals.", baseDifficulty:4000, reqdRank:10e3, rankGain:40, rankLoss:20, hpLoss:100, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true }); BlackOperations["Operation Ares"] = new BlackOperation({ name:"Operation Ares", desc:"One of our undercover agents, Agent Carter, has informed us of a " + "massive weapons deal going down in Dubai between rogue Russian " + "militants and a radical Synthoid community. These weapons are next-gen " + "plasma and energy weapons. It is critical for the safety of humanity " + "that this deal does not happen.

" + "Your task is to intercept the deal. Leave no survivors.", baseDifficulty:5000, reqdRank:12.5e3, rankGain:40, rankLoss:20, hpLoss:200, weights:{hack:0,str:0.25,def:0.25,dex:0.25,agi:0.25,cha:0, int:0}, decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true }); BlackOperations["Operation Archangel"] = new BlackOperation({ name:"Operation Archangel", desc:"Our analysts have discovered that the popular Red Rabbit brothel in " + "Amsterdam is run and 'staffed' by MK-VI Synthoids. Intelligence " + "suggests that the profit from this brothel is used to fund a large " + "black market arms trafficking operation.

" + "The goal of this operation is to take out the leaders that are running " + "the Red Rabbit brothel. Try to limit the number of other casualties, " + "but do what you must to complete the mission.", baseDifficulty:7500, reqdRank:15e3, rankGain:50, rankLoss:20, hpLoss:25, weights:{hack:0,str:0.2,def:0.2,dex:0.3,agi:0.3,cha:0, int:0}, decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true, }); BlackOperations["Operation Juggernaut"] = new BlackOperation({ name:"Operation Juggernaut", desc:"The CIA has just encountered a new security threat. A new " + "criminal group, lead by a shadowy operative who calls himself " + "Juggernaut, has been smuggling drugs and weapons (including " + "suspected bioweapons) into Sector-12. We also have reason " + "to believe the tried to break into one of Universal Energy's " + "facilities in order to cause a city-wide blackout. The CIA " + "suspects that Juggernaut is a heavily-augmented Synthoid, and " + "have thus enlisted our help.

" + "Your mission is to eradicate Juggernaut and his followers.", baseDifficulty:10e3, reqdRank:20e3, rankGain:75, rankLoss:40, hpLoss:300, weights:{hack:0,str:0.25,def:0.25,dex:0.25,agi:0.25,cha:0, int:0}, decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true, }); BlackOperations["Operation Red Dragon"] = new BlackOperation({ name:"Operation Red Dragon", desc:"The Tetrads criminal organization is suspected of " + "reverse-engineering the MK-VI Synthoid design. We believe " + "they altered and possibly improved the design and began " + "manufacturing their own Synthoid models in order to bolster " + "their criminal activities.

" + "Your task is to infiltrate and destroy the Tetrads' base of operations " + "in Los Angeles. Intelligence tells us that their base houses " + "one of their Synthoid manufacturing units.", baseDifficulty:12.5e3, reqdRank:25e3, rankGain:100, rankLoss:50, hpLoss:500, weights:{hack:0.05,str:0.2,def:0.2,dex:0.25,agi:0.25,cha:0, int:0.05}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true, }); BlackOperations["Operation K"] = new BlackOperation({ name:"Operation K", desc:"CODE RED SITUATION. Our intelligence tells us that VitaLife " + "has discovered a new android cloning technology. This technology " + "is supposedly capable of cloning Synthoid, not only physically " + "but also their advanced AI modules. We do not believe that " + "VitaLife is trying to use this technology illegally or " + "maliciously, but if any Synthoids were able to infiltrate the " + "corporation and take advantage of this technology then the " + "results would be catastrophic.

" + "We do not have the power or jurisdiction to shutdown this down " + "through legal or political means, so we must resort to a covert " + "operation. Your goal is to destroy this technology and eliminate" + "anyone who was involved in its creation.", baseDifficulty:15e3, reqdRank:30e3, rankGain:120, rankLoss:60, hpLoss:1000, weights:{hack:0.05,str:0.2,def:0.2,dex:0.25,agi:0.25,cha:0, int:0.05}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true }); BlackOperations["Operation Deckard"] = new BlackOperation({ name:"Operation Deckard", desc:"Despite your success in eliminating VitaLife's new android-replicating " + "technology in Operation K, we've discovered that a small group of " + "MK-VI Synthoids were able to make off with the schematics and design " + "of the technology before the Operation. It is almost a certainty that " + "these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising." + "The goal of Operation Deckard is to hunt down these Synthoids and retire " + "them. I don't need to tell you how critical this mission is.", baseDifficulty:20e3, reqdRank:40e3, rankGain:150, rankLoss:75, hpLoss:200, weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04}, decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true, }); BlackOperations["Operation Tyrell"] = new BlackOperation({ name:"Operation Tyrell", desc:"A week ago Blade Industries reported a small break-in at one " + "of their Aevum Augmentation storage facitilities. We figured out " + "that The Dark Army was behind the heist, and didn't think any more " + "of it. However, we've just discovered that several known MK-VI Synthoids " + "were part of that break-in group.

" + "We cannot have Synthoids upgrading their already-enhanced abilities " + "with Augmentations. Your task is to hunt down the associated Dark Army " + "members and eliminate them.", baseDifficulty:25e3, reqdRank:50e3, rankGain:200, rankLoss:100, hpLoss:500, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true, }); BlackOperations["Operation Wallace"] = new BlackOperation({ name:"Operation Wallace", desc:"Based on information gathered from Operation Tyrell, we've discovered " + "that The Dark Army was well aware that there were Synthoids amongst " + "their ranks. Even worse, we believe that The Dark Army is working " + "together with other criminal organizations such as The Syndicate and " + "that they are planning some sort of large-scale takeover of multiple major " + "cities, most notably Aevum. We suspect that Synthoids have infiltrated " + "the ranks of these criminal factions and are trying to stage another " + "Synthoid uprising.

" + "The best way to deal with this is to prevent it before it even happens. " + "The goal of Operation Wallace is to destroy the Dark Army and " + "Syndicate factions in Aevum immediately. Leave no survivors.", baseDifficulty:30e3, reqdRank:75e3, rankGain:500, rankLoss:150, hpLoss:1500, weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true }); BlackOperations["Operation Shoulder of Orion"] = new BlackOperation({ name:"Operation Shoulder of Orion", desc:"China's Solaris Space Systems is secretly launching the first " + "manned spacecraft in over a decade using Synthoids. We believe " + "China is trying to establish the first off-world colonies.

" + "The mission is to prevent this launch without instigating an " + "international conflict. When you accept this mission you will be " + "officially disavowed by the NSA and the national government until after you " + "successfully return. In the event of failure, all of the operation's " + "team members must not let themselves be captured alive.", baseDifficulty:35e3, reqdRank:100e3, rankGain:1e3, rankLoss:500, hpLoss:1500, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isStealth:true }); BlackOperations["Operation Hyron"] = new BlackOperation({ name:"Operation Hyron", desc:"Our intelligence tells us that Fulcrum Technologies is developing " + "a quantum supercomputer using human brains as core " + "processors. This supercomputer " + "is rumored to be able to store vast amounts of data and " + "perform computations unmatched by any other supercomputer on the " + "planet. But more importantly, the use of organic human brains " + "means that the supercomputer may be able to reason abstractly " + "and become self-aware.

" + "I do not need to remind you why sentient-level AIs pose a serious " + "thread to all of mankind.

" + "The research for this project is being conducted at one of Fulcrum " + "Technologies secret facilities in Aevum, codenamed 'Alpha Ranch'. " + "Infiltrate the compound, delete and destroy the work, and then find and kill the " + "project lead.", baseDifficulty:40e3, reqdRank:125e3, rankGain:2e3, rankLoss:1e3, hpLoss:500, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true }); BlackOperations["Operation Morpheus"] = new BlackOperation({ name:"Operation Morpheus", desc:"DreamSense Technologies is an advertising company that uses " + "special technology to transmit their ads into the peoples " + "dreams and subconcious. They do this using broadcast transmitter " + "towers. Based on information from our agents and informants in " + "Chonqging, we have reason to believe that one of the broadcast " + "towers there has been compromised by Synthoids and is being used " + "to spread pro-Synthoid propaganda.

" + "The mission is to destroy this broadcast tower. Speed and " + "stealth are of the upmost important for this.", baseDifficulty:45e3, reqdRank:150e3, rankGain:5e3, rankLoss:1e3, hpLoss:100, weights:{hack:0.05,str:0.15,def:0.15,dex:0.3,agi:0.3,cha:0, int:0.05}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isStealth:true }); BlackOperations["Operation Ion Storm"] = new BlackOperation({ name:"Operation Ion Storm", desc:"Our analysts have uncovered a gathering of MK-VI Synthoids " + "that have taken up residence in the Sector-12 Slums. We " + "don't know if they are rogue Synthoids from the Uprising, " + "but we do know that they have been stockpiling " + "weapons, money, and other resources. This makes them dangerous.

" + "This is a full-scale assault operation to find and retire all of these " + "Synthoids in the Sector-12 Slums.", baseDifficulty:50e3, reqdRank:175e3, rankGain:5e3, rankLoss:1e3, hpLoss:5000, weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true }); BlackOperations["Operation Annihilus"] = new BlackOperation({ name:"Operation Annihilus", desc:"Our superiors have ordered us to eradicate everything and everyone " + "in an underground facility located in Aevum. They tell us " + "that the facility houses many dangerous Synthoids and " + "belongs to a terrorist organization called " + "'The Covenant'. We have no prior intelligence about this " + "organization, so you are going in blind.", baseDifficulty:55e3, reqdRank:200e3, rankGain:5e3, rankLoss:1e3, hpLoss:10e3, weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04}, decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75}, isKill:true }); BlackOperations["Operation Ultron"] = new BlackOperation({ name:"Operation Ultron", desc:"OmniTek Incorporated, the original designer and manufacturer of Synthoids, " + "has notified us of a malfunction in their AI design. This malfunction, " + "when triggered, causes MK-VI Synthoids to become radicalized and seek out " + "the destruction of humanity. They say that this bug affects all MK-VI Synthoids, " + "not just the rogue ones from the Uprising.

" + "OmniTek has also told us they they believe someone has triggered this " + "malfunction in a large group of MK-VI Synthoids, and that these newly-radicalized Synthoids " + "are now amassing in Volhaven to form a terrorist group called Ultron.

" + "Intelligence suggests Ultron is heavily armed and that their members are " + "augmented. We believe Ultron is making moves to take control of " + "and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).

" + "Your task is to find and destroy Ultron.", baseDifficulty:60e3, reqdRank:250e3, rankGain:10e3, rankLoss:2e3, hpLoss:10e3, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, isKill:true }); BlackOperations["Operation Centurion"] = new BlackOperation({ name:"Operation Centurion", desc:"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)

" + "Throughout all of humanity's history, we have relied on " + "technology to survive, conquer, and progress. Its advancement became our primary goal. " + "And at the peak of human civilization technology turned into " + "power. Global, absolute power.

" + "It seems that the universe is not without a sense of irony.

" + "D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)", baseDifficulty:70e3, reqdRank:300e3, rankGain:15e3, rankLoss:5e3, hpLoss:10e3, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, }); BlackOperations["Operation Vindictus"] = new BlackOperation({ name:"Operation Vindictus", desc:"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)

" + "The bits are all around us. The daemons that hold the Node " + "together can manifest themselves in many different ways.

" + "D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)", baseDifficulty:75e3, reqdRank:350e3, rankGain:20e3, rankLoss:20e3, hpLoss:20e3, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, }); BlackOperations["Operation Daedalus"] = new BlackOperation({ name:"Operation Daedalus", desc:"Yesterday we obeyed kings and bent our neck to emperors. " + "Today we kneel only to truth.", baseDifficulty:80e3, reqdRank:400e3, rankGain:40e3, rankLoss:10e3, hpLoss:100e3, weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,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.75}, }); } export {Bladeburner};