Fixed merge conflicts with dev

This commit is contained in:
danielyxie
2018-11-15 19:45:03 -08:00
29 changed files with 4879 additions and 4314 deletions
+2 -2
View File
@@ -265,7 +265,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.CorporationValuation = 0.5;
break;
case 6: //Bladeburner
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.ServerMaxMoney = 0.5;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
@@ -282,7 +282,7 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.BladeburnerRank = 0.6;
BitNodeMultipliers.BladeburnerSkillCost = 2;
BitNodeMultipliers.AugmentationMoneyCost = 3;
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
BitNodeMultipliers.ServerMaxMoney = 0.5;
BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ServerStartingSecurity = 1.5;
+40 -12
View File
@@ -74,7 +74,6 @@ var ContractBaseMoneyGain = 50e3; //Base Money Gained per contract
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.<br><br>" +
@@ -153,6 +152,7 @@ $(document).keydown(function(event) {
//}
if (!(Player.bladeburner instanceof Bladeburner)) {return;}
let consoleHistory = Player.bladeburner.consoleHistory;
//NOTE: Keycodes imported from Terminal.js
if (event.keyCode === KEY.ENTER) {
@@ -712,6 +712,10 @@ function Bladeburner(params={}) {
this.automateActionLow = 0;
this.automateThreshLow = 0; //Stamina Threshold
//Console command history
this.consoleHistory = [];
this.consoleLogs = [];
//Initialization
initBladeburner();
this.initializeDomElementRefs();
@@ -864,6 +868,12 @@ Bladeburner.prototype.process = function() {
this.resetAction();
}
// If the Player has no Stamina, set action to idle
if (this.stamina <= 0) {
this.log("Your Bladeburner action was cancelled because your stamina hit 0");
this.resetAction();
}
//A 'tick' for this mechanic is one second (= 5 game cycles)
if (this.storedCycles >= CyclesPerSecond) {
var seconds = Math.floor(this.storedCycles / CyclesPerSecond);
@@ -1345,7 +1355,6 @@ Bladeburner.prototype.completeAction = function() {
Player.gainIntelligenceExp(BaseIntGain);
Player.gainCharismaExp(charismaExpGain);
this.changeRank(0.1 * BitNodeMultipliers.BladeburnerRank);
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");
@@ -1738,8 +1747,15 @@ Bladeburner.prototype.createContent = function() {
document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv);
this.postToConsole("Bladeburner Console BETA");
this.postToConsole("Type 'help' to see console commands");
if (this.consoleLogs.length === 0) {
this.postToConsole("Bladeburner Console BETA");
this.postToConsole("Type 'help' to see console commands");
} else {
for (let i = 0; i < this.consoleLogs.length; ++i) {
this.postToConsole(this.consoleLogs[i], false);
}
}
DomElems.consoleInput.focus();
}
@@ -2732,12 +2748,22 @@ Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
}
//Bladeburner Console Window
Bladeburner.prototype.postToConsole = function(input) {
Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
const MaxConsoleEntries = 100;
if (saveToLogs === true) {
this.consoleLogs.push(input);
if (this.consoleLogs.length > MaxConsoleEntries) {
this.consoleLogs.shift();
}
}
if (input == null || DomElems.consoleDiv == null) {return;}
$("#bladeubrner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>');
if (DomElems.consoleTable.childNodes.length > 200) {
if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
}
this.updateConsoleScroll();
}
@@ -2753,6 +2779,8 @@ Bladeburner.prototype.clearConsole = function() {
while (DomElems.consoleTable.childNodes.length > 1) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
}
this.consoleLogs.length = 0;
}
Bladeburner.prototype.log = function(input) {
@@ -2764,13 +2792,13 @@ Bladeburner.prototype.log = function(input) {
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);
if (this.consoleHistory[this.consoleHistory.length-1] != commands) {
this.consoleHistory.push(commands);
if (this.consoleHistory.length > 50) {
this.consoleHistory.splice(0, 1);
}
}
consoleHistoryIndex = consoleHistory.length;
consoleHistoryIndex = this.consoleHistory.length;
var arrayOfCommands = commands.split(";");
for (var i = 0; i < arrayOfCommands.length; ++i) {
@@ -3469,7 +3497,7 @@ Bladeburner.prototype.getActionCountRemainingNetscriptFn = function(type, name,
switch (actionId.type) {
case ActionTypes["Contract"]:
case ActionTypes["Operation"]:
return actionObj.count;
return Math.floor( actionObj.count );
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
if (this.blackops[name] != null) {
+6 -6
View File
@@ -185,7 +185,7 @@ export const companyPositionMetadata: IConstructorParams[] = [
baseSalary: 410,
charismaEffectiveness: 20,
charismaExpGain: 0.1,
hackingEffectiveness: 0.80,
hackingEffectiveness: 80,
hackingExpGain: 0.5,
reqdCharisma: 76,
reqdHacking: 251,
@@ -419,7 +419,7 @@ export const companyPositionMetadata: IConstructorParams[] = [
},
{
name: posNames.AgentCompanyPositions[0], // Field Agent
nextPosition: posNames.AgentCompanyPositions[0], // Secret Agent
nextPosition: posNames.AgentCompanyPositions[1], // Secret Agent
baseSalary: 330,
hackingEffectiveness: 10,
strengthEffectiveness: 15,
@@ -443,8 +443,8 @@ export const companyPositionMetadata: IConstructorParams[] = [
repMultiplier: 1,
},
{
name: posNames.AgentCompanyPositions[0], // Secret Agent
nextPosition: posNames.AgentCompanyPositions[0], // Special Operative
name: posNames.AgentCompanyPositions[1], // Secret Agent
nextPosition: posNames.AgentCompanyPositions[2], // Special Operative
baseSalary: 990,
hackingEffectiveness: 15,
strengthEffectiveness: 15,
@@ -468,7 +468,7 @@ export const companyPositionMetadata: IConstructorParams[] = [
repMultiplier: 1.25,
},
{
name: posNames.AgentCompanyPositions[0], // Special Operative
name: posNames.AgentCompanyPositions[2], // Special Operative
nextPosition: null,
baseSalary: 2000,
hackingEffectiveness: 15,
@@ -558,7 +558,7 @@ export const companyPositionMetadata: IConstructorParams[] = [
repMultiplier: 1,
},
{
name: posNames.BusinessConsultantCompanyPositions[0], // Senior Business Consultant
name: posNames.BusinessConsultantCompanyPositions[1], // Senior Business Consultant
nextPosition: null,
baseSalary: 525,
hackingEffectiveness: 15,
+7 -10
View File
@@ -1,7 +1,7 @@
import {IMap} from "./types";
export let CONSTANTS: IMap<any> = {
Version: "0.41.0",
Version: "0.41.2",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -504,16 +504,13 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate:
`
v0.41.1
* Stock Market changes:
*** Stocks now have "maximum prices"
*** If a stock reaches its "maximum price", it will most likely drop in value (although it might still rise)
*** Each stock has its own, unique maximum price
*** Maximum price for each stock are randomly generated and change during each 'reset'
*** Stock Market cycles are now accumulated/stored, much like it is for Gangs and Bladeburners
*** Accumulated/stored cycles cause stock prices to update up to 50% faster (from every 6 seconds to 4 seconds)
****** This means that after coming back from being offline, stock prices will update faster to make up for offline time
v0.41.2
* IMPORTANT - Netscript Changes:
** rm() now takes an optional parameter that lets you specify on which server to delete the file
* Gang Changes:
** UI now displays your chance to win a clash with other gangs
** Added getChanceToWinClash() function to the Gang API
`
}
+80
View File
@@ -5,7 +5,12 @@ import {Factions} from "./Faction";
import {Player} from "./Player";
import {AllServers} from "./Server";
import {hackWorldDaemon} from "./RedPill";
import {StockMarket,
SymbolToStockMap} from "./StockMarket";
import {Stock} from "./Stock";
import {Terminal} from "./Terminal";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {createElement} from "../utils/uiHelpers/createElement";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
@@ -322,6 +327,7 @@ export function createDevMenu() {
const bladeburnerGainRankInput = createElement("input", {
class: "text-input",
margin: "5px",
placeholder: "Rank to gain (or negative to lose rank)",
type: "number",
});
@@ -344,6 +350,7 @@ export function createDevMenu() {
const gangStoredCyclesInput = createElement("input", {
class: "text-input",
margin: "5px",
placeholder: "# Cycles to add",
type: "number",
});
@@ -372,6 +379,73 @@ export function createDevMenu() {
innerText: "Generate Random Contract",
});
// Stock Market
const stockmarketHeader = createElement("h2", {innerText: "Stock Market"});
const stockInput = createElement("input", {
class: "text-input",
display: "block",
placeholder: "Stock symbol(s), or 'all'",
});
function processStocks(cb) {
const input = stockInput.value.toString().replace(/\s/g, '');
// Empty input, or "all", will process all stocks
if (input === "" || input.toLowerCase() === "all") {
for (const name in StockMarket) {
if (StockMarket.hasOwnProperty(name)) {
const stock = StockMarket[name];
if (stock instanceof Stock) {
cb(stock);
}
}
}
return;
}
const stockSymbols = input.split(",");
for (let i = 0; i < stockSymbols.length; ++i) {
const stock = SymbolToStockMap[stockSymbols];
if (stock instanceof Stock) {
cb(stock);
}
}
}
const stockPriceChangeInput = createElement("input", {
class: "text-input",
margin: "5px",
placeholder: "Price to change stock(s) to",
type: "number",
});
const stockPriceChangeBtn = createElement("button", {
class: "std-button",
clickListener: () => {
const price = parseInt(stockPriceChangeInput.value);
if (isNaN(price)) { return; }
processStocks((stock) => {
stock.price = price;
});
dialogBoxCreate(`Stock Prices changed to ${price}`);
},
innerText: "Change Stock Price(s)",
});
const stockViewPriceCapBtn = createElement("button", {
class: "std-button",
clickListener: () => {
let text = "";
processStocks((stock) => {
text += `${stock.symbol}: ${numeralWrapper.format(stock.cap, '$0.000a')}<br>`;
});
dialogBoxCreate(text);
},
innerText: "View Stock Price Caps",
});
// Add everything to container, then append to main menu
const devMenuContainer = createElement("div", {
class: "generic-menupage-container",
@@ -433,6 +507,12 @@ export function createDevMenu() {
devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(contractsHeader);
devMenuContainer.appendChild(generateRandomContractBtn);
devMenuContainer.appendChild(stockmarketHeader);
devMenuContainer.appendChild(stockInput);
devMenuContainer.appendChild(stockPriceChangeInput);
devMenuContainer.appendChild(stockPriceChangeBtn);
devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(stockViewPriceCapBtn);
const entireGameContainer = document.getElementById("entire-game-container");
if (entireGameContainer == null) {
-1
View File
@@ -394,7 +394,6 @@ function displayFactionContent(factionName) {
var hacking = false;
if (factionName === "NiteSec" || factionName === "The Black Hand") {hacking = true;}
Player.startGang(factionName, hacking);
document.getElementById("gang-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
Engine.loadGangContent();
+12 -7
View File
@@ -1521,9 +1521,10 @@ Gang.prototype.updateGangContent = function() {
// Update territory information
UIElems.gangTerritoryInfoText.innerHTML = "";
for (var gangname in AllGangs) {
const playerPower = AllGangs[this.facName].power;
for (const gangname in AllGangs) {
if (AllGangs.hasOwnProperty(gangname)) {
var gangTerritoryInfo = AllGangs[gangname];
const gangTerritoryInfo = AllGangs[gangname];
let territory = gangTerritoryInfo.territory * 100;
//Fix some rounding issues graphically
@@ -1536,12 +1537,16 @@ Gang.prototype.updateGangContent = function() {
displayNumber = formatNumber(territory, 2);
}
if (gangname == this.facName) {
UIElems.gangTerritoryInfoText.innerHTML += ("<b>" + gangname + "</b><br>(Power: " + formatNumber(gangTerritoryInfo.power, 6) + "): " +
displayNumber + "%<br><br>");
if (gangname === this.facName) {
let newHTML = `<b><u>${gangname}</u></b><br>Power: ${formatNumber(gangTerritoryInfo.power, 6)}<br>`;
newHTML += `Territory: ${displayNumber}%<br><br>`;
UIElems.gangTerritoryInfoText.innerHTML += newHTML;
} else {
UIElems.gangTerritoryInfoText.innerHTML += (gangname + "<br>(Power: " + formatNumber(gangTerritoryInfo.power, 6) + "): " +
displayNumber + "%<br><br>");
const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower);
let newHTML = `<u>${gangname}</u><br>Power: ${formatNumber(gangTerritoryInfo.power, 6)}<br>`;
newHTML += `Territory: ${displayNumber}%<br>`;
newHTML += `Chance to win clash with this gang: ${numeralWrapper.format(clashVictoryChance, "0.000%")}<br><br>`;
UIElems.gangTerritoryInfoText.innerHTML += newHTML;
}
}
}
-2
View File
@@ -1955,7 +1955,6 @@ function initLocationButtons() {
name:companyName,
});
displayLocationContent();
document.getElementById("corporation-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
dialogBoxCreate("Congratulations! You just started your own corporation. You can visit " +
@@ -1985,7 +1984,6 @@ function initLocationButtons() {
Player.bladeburner = new Bladeburner({new:true});
dialogBoxCreate("You have been accepted into the Bladeburner division!");
displayLocationContent();
document.getElementById("bladeburner-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
} else {
+121 -9
View File
@@ -108,11 +108,14 @@ var possibleLogs = {
getServerGrowth: true,
getServerNumPortsRequired: true,
getServerRam: true,
// TIX API
buyStock: true,
sellStock: true,
shortStock: true,
sellShort: true,
purchase4SMarketData: true,
purchase4SMarketDataTixApi: true,
// Singularity Functions
purchaseServer: true,
@@ -547,8 +550,30 @@ function NetscriptFunctions(workerScript) {
}
return workerScript.disableLogs[fn] ? false : true;
},
getScriptLogs : function() {
getScriptLogs : function(fn, ip) {
if (workerScript.checkingRam) {return 0;}
if (fn != null && typeof fn === 'string') {
// Get Logs of another script
if (ip == null) { ip = workerScript.serverIp; }
const server = getServer(ip);
if (server == null) {
workerScript.log(`getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
throw makeRuntimeRejectMsg(workerScript, `getScriptLogs() failed. Invalid IP or hostname passed in: ${ip}`);
}
let argsForTarget = [];
for (let i = 2; i < arguments.length; ++i) {
argsForTarget.push(arguments[i]);
}
const runningScriptObj = findRunningScript(fn, argsForTarget, server);
if (runningScriptObj == null) {
workerScript.scriptRef.log(`getScriptLogs() failed. No such script ${fn} on ${server.hostname} with args: ${arrayToString(argsForTarget)}`);
return "";
}
return runningScriptObj.logs.slice();
}
return workerScript.scriptRef.logs.slice();
},
nuke : function(ip){
@@ -802,7 +827,7 @@ function NetscriptFunctions(workerScript) {
}
NetscriptFunctions(workerScript).exit();
},
kill : function(filename,ip) {
kill : function(filename, ip) {
if (workerScript.checkingRam) {
return updateStaticRam("kill", CONSTANTS.ScriptKillRamCost);
}
@@ -1698,6 +1723,68 @@ function NetscriptFunctions(workerScript) {
stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag;
return forecast / 100; //Convert from percentage to decimal
},
purchase4SMarketData : function() {
if (workerScript.checkingRam) {
return updateStaticRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("purchase4SMarketData", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use purchase4SMarketData()");
}
if (Player.has4SData) {
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Already purchased 4S Market Data");
}
return true;
}
if (Player.money.lt(CONSTANTS.MarketData4SCost)) {
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Failed to purchase 4S Market Data - Not enough money");
}
return false;
}
Player.has4SData = true;
Player.loseMoney(CONSTANTS.MarketData4SCost);
if (workerScript.shouldLog("purchase4SMarketData")) {
workerScript.log("Purchased 4S Market Data");
}
return true;
},
purchase4SMarketDataTixApi : function() {
if (workerScript.checkingRam) {
return updateStaticRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost);
}
updateDynamicRam("purchase4SMarketDataTixApi", CONSTANTS.ScriptBuySellStockRamCost);
if (!Player.hasTixApiAccess) {
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use purchase4SMarketDataTixApi()");
}
if (Player.has4SDataTixApi) {
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Already purchased 4S Market Data TIX API");
}
return true;
}
if (Player.money.lt(CONSTANTS.MarketDataTixApi4SCost)) {
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Failed to purchase 4S Market Data TIX API - Not enough money");
}
return false;
}
Player.has4SDataTixApi = true;
Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost);
if (workerScript.shouldLog("purchase4SMarketDataTixApi")) {
workerScript.log("Purchased 4S Market Data TIX API");
}
return true;
},
getPurchasedServerLimit : function() {
if (workerScript.checkingRam) {
return updateStaticRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit);
@@ -1725,7 +1812,7 @@ function NetscriptFunctions(workerScript) {
cost = getPurchaseServerRamCostGuard(ram);
} catch (e) {
workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' " + e.message);
return "";
return Infinity;
}
return cost;
@@ -2053,14 +2140,18 @@ function NetscriptFunctions(workerScript) {
}
return port;
},
rm : function(fn) {
rm : function(fn, ip) {
if (workerScript.checkingRam) {
return updateStaticRam("rm", CONSTANTS.ScriptReadWriteRamCost);
}
updateDynamicRam("rm", CONSTANTS.ScriptReadWriteRamCost);
var s = getServer(workerScript.serverIp);
if (ip == null || ip === "") {
ip = workerScript.serverIp;
}
var s = getServer(ip);
if (s == null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in clear(). This is a bug please contact game dev");
throw makeRuntimeRejectMsg(workerScript, `Invalid server specified for rm(): ${ip}`);
}
if (fn.includes(".exe")) {
@@ -3784,7 +3875,7 @@ function NetscriptFunctions(workerScript) {
nsGang.checkGangApiAccess(workerScript, "purchaseEquipment");
try {
for (const member in Player.gang.members) {
for (const member of Player.gang.members) {
if (member.name === memberName) {
const res = member.buyUpgrade(equipName, Player, Player.gang);
if (workerScript.shouldLog("purchaseEquipment")) {
@@ -3813,13 +3904,13 @@ function NetscriptFunctions(workerScript) {
nsGang.checkGangApiAccess(workerScript, "ascendMember");
try {
for (const member in Player.gang.members) {
for (const member of Player.gang.members) {
if (member.name === name) {
return Player.gang.ascendMember(member, workerScript);
}
}
workerScript.log(`Invalid argument passed to gang.ascendMember(). No gang member could be found with name ${memberName}`);
workerScript.log(`Invalid argument passed to gang.ascendMember(). No gang member could be found with name ${name}`);
return false;
} catch(e) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("ascendMember", e));
@@ -3848,6 +3939,27 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("setTerritoryWarfare", e));
}
},
getChanceToWinClash : function(otherGang) {
if (workerScript.checkingRam) {
return updateStaticRam("getChanceToWinClash", CONSTANTS.ScriptGangApiBaseRamCost);
}
updateDynamicRam("getChanceToWinClash", CONSTANTS.ScriptGangApiBaseRamCost);
nsGang.checkGangApiAccess(workerScript, "getChanceToWinClash");
try {
if (AllGangs[otherGang] == null) {
workerScript.log(`Invalid gang specified in gang.getChanceToWinClash() : ${otherGang}`);
return 0;
}
const playerPower = AllGangs[Player.gang.facName].power;
const otherPower = AllGangs[otherGang].power;
return playerPower / (otherPower + playerPower);
} catch(e) {
throw makeRuntimeRejectMsg(workerScript, nsGang.unknownGangApiExceptionMessage("getChanceToWinClash", e));
}
},
getBonusTime : function() {
if (workerScript.checkingRam) { return 0; }
nsGang.checkGangApiAccess(workerScript, "getBonusTime");
+34 -46
View File
@@ -8,6 +8,7 @@ import {Companies} from "./Company/Companies";
import {getNextCompanyPosition} from "./Company/GetNextCompanyPosition";
import {getJobRequirementText} from "./Company/GetJobRequirementText";
import {CompanyPositions} from "./Company/CompanyPositions";
import * as posNames from "./Company/data/CompanyPositionNames";
import {CONSTANTS} from "./Constants";
import {Corporation} from "./CompanyManagement";
import {Programs} from "./CreateProgram";
@@ -193,7 +194,6 @@ function PlayerObject() {
//Flags for determining whether certain "thresholds" have been achieved
this.firstFacInvRecvd = false;
this.firstAugPurchased = false;
this.firstJobRecvd = false;
this.firstTimeTraveled = false;
this.firstProgramAvailable = false;
@@ -1749,6 +1749,9 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) {
this.companyName = company.name;
this.companyPosition = pos.name;
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (leaveCompany) {
if (sing) { return true; }
dialogBoxCreate([`Congratulations! You were offered a new job at ${this.companyName} as a ${pos.name}!`,
@@ -1794,21 +1797,21 @@ PlayerObject.prototype.getNextCompanyPosition = function(company, entryPosType)
}
PlayerObject.prototype.applyForSoftwareJob = function(sing=false) {
return this.applyForJob(CompanyPositions.SoftwareIntern, sing);
return this.applyForJob(CompanyPositions[posNames.SoftwareCompanyPositions[0]], sing);
}
PlayerObject.prototype.applyForSoftwareConsultantJob = function(sing=false) {
return this.applyForJob(CompanyPositions.SoftwareConsultant, sing);
return this.applyForJob(CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]], sing);
}
PlayerObject.prototype.applyForItJob = function(sing=false) {
return this.applyForJob(CompanyPositions.ITIntern, sing);
return this.applyForJob(CompanyPositions[posNames.ITCompanyPositions[0]], sing);
}
PlayerObject.prototype.applyForSecurityEngineerJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.SecurityEngineer)) {
return this.applyForJob(CompanyPositions.SecurityEngineer, sing);
if (this.isQualified(company, CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]])) {
return this.applyForJob(CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]], sing);
} else {
if (sing) {return false;}
dialogBoxCreate("Unforunately, you do not qualify for this position");
@@ -1817,8 +1820,8 @@ PlayerObject.prototype.applyForSecurityEngineerJob = function(sing=false) {
PlayerObject.prototype.applyForNetworkEngineerJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.NetworkEngineer)) {
return this.applyForJob(CompanyPositions.NetworkEngineer, sing);
if (this.isQualified(company, CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]])) {
return this.applyForJob(CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]], sing);
} else {
if (sing) {return false;}
dialogBoxCreate("Unforunately, you do not qualify for this position");
@@ -1826,22 +1829,23 @@ PlayerObject.prototype.applyForNetworkEngineerJob = function(sing=false) {
}
PlayerObject.prototype.applyForBusinessJob = function(sing=false) {
return this.applyForJob(CompanyPositions.BusinessIntern, sing);
return this.applyForJob(CompanyPositions[posNames.BusinessCompanyPositions[0]], sing);
}
PlayerObject.prototype.applyForBusinessConsultantJob = function(sing=false) {
return this.applyForJob(CompanyPositions.BusinessConsultant, sing);
return this.applyForJob(CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]], sing);
}
PlayerObject.prototype.applyForSecurityJob = function(sing=false) {
//TODO If case for POlice departments
return this.applyForJob(CompanyPositions.SecurityGuard, sing);
// TODO Police Jobs
// Indexing starts at 2 because 0 is for police officer
return this.applyForJob(CompanyPositions[posNames.SecurityCompanyPositions[2]], sing);
}
PlayerObject.prototype.applyForAgentJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.FieldAgent)) {
return this.applyForJob(CompanyPositions.FieldAgent, sing);
if (this.isQualified(company, CompanyPositions[posNames.AgentCompanyPositions[0]])) {
return this.applyForJob(CompanyPositions[posNames.AgentCompanyPositions[0]], sing);
} else {
if (sing) {return false;}
dialogBoxCreate("Unforunately, you do not qualify for this position");
@@ -1850,15 +1854,11 @@ PlayerObject.prototype.applyForAgentJob = function(sing=false) {
PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.Employee)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[1]])) {
this.companyName = company.companyName;
this.companyPosition = CompanyPositions.Employee;
this.companyPosition = CompanyPositions[posNames.MiscCompanyPositions[1]];
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed at " + this.companyName);
Engine.loadLocationContent();
@@ -1870,15 +1870,11 @@ PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.PartTimeEmployee)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) {
this.companyName = company.companyName;
this.companyPosition = CompanyPositions.PartTimeEmployee;
this.companyPosition = CompanyPositions[posNames.PartTimeCompanyPositions[1]];
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName);
Engine.loadLocationContent();
@@ -1890,15 +1886,11 @@ PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.Waiter)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[0]])) {
this.companyName = company.companyName;
this.companyPosition = CompanyPositions.Waiter;
this.companyPosition = CompanyPositions[posNames.MiscCompanyPositions[0]];
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName);
Engine.loadLocationContent();
@@ -1910,15 +1902,11 @@ PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) {
var company = Companies[this.location]; //Company being applied to
if (this.isQualified(company, CompanyPositions.PartTimeWaiter)) {
if (this.firstJobRecvd === false) {
this.firstJobRecvd = true;
document.getElementById("job-tab").style.display = "list-item";
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
}
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[0]])) {
this.companyName = company.companyName;
this.companyPosition = CompanyPositions.PartTimeWaiter;
this.companyPosition = CompanyPositions[posNames.PartTimeCompanyPositions[0]];
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
if (sing) {return true;}
dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName);
Engine.loadLocationContent();
+8
View File
@@ -148,6 +148,10 @@ function prestigeAugmentation() {
Terminal.resetTerminalInput();
Engine.loadTerminalContent();
// Refresh Main Menu (the 'World' menu, specifically)
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Red Pill
if (augmentationExists(AugmentationNames.TheRedPill) &&
Augmentations[AugmentationNames.TheRedPill].owned) {
@@ -319,6 +323,10 @@ function prestigeSourceFile() {
Player.corporation = null;
Player.bladeburner = null;
// Refresh Main Menu (the 'World' menu, specifically)
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Gain int exp
Player.gainIntelligenceExp(5);
}
+28 -49
View File
@@ -110,6 +110,31 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
createStatusText("Game saved!");
}
// Makes necessary changes to the loaded/imported data to ensure
// the game stills works with new versions
function evaluateVersionCompatibility(ver) {
// This version refactored the Company/job-related code
if (ver <= "0.41.2") {
// Player's company position is now a string
if (Player.companyPosition != null && typeof Player.companyPosition !== "string") {
console.log("Changed Player.companyPosition value to be compatible with v0.41.2");
Player.companyPosition = Player.companyPosition.positionName;
if (Player.companyPosition == null) {
Player.companyPosition = "";
}
}
// The "companyName" property of all Companies is renamed to "name"
for (var companyName in Companies) {
const company = Companies[companyName];
if (company.name == null && company.companyName != null) {
console.log("Changed company name property to be compatible with v0.41.2");
company.name = company.companyName;
}
}
}
}
function loadGame(saveString) {
if (saveString === "" || saveString == null || saveString === undefined) {
if (!window.localStorage.getItem("bitburnerSave")) {
@@ -187,23 +212,8 @@ function loadGame(saveString) {
if (saveObj.hasOwnProperty("VersionSave")) {
try {
var ver = JSON.parse(saveObj.VersionSave, Reviver);
// This version refactored the Company/job-related code
if (ver < "0.41.2") {
// Player's company position is now a string
if (Player.companyPosition !== "" || Player.companyPosition instanceof CompanyPosition) {
console.log("Changed Player.companyPosition value to be compatible with v0.41.2");
Player.companyPosition = Player.companyPosition.positionName;
}
evaluateVersionCompatibility(ver);
// The "companyName" property of all Companies is renamed to "name"
for (var companyName in Companies) {
const company = Companies[companyName];
if (company.name == null && company.companyName != null) {
console.log("Changed company name property to be compatible with v0.41.2");
company.name = company.companyName;
}
}
}
if (window.location.href.toLowerCase().includes("bitburner-beta")) {
//Beta branch, always show changes
createBetaUpdateText();
@@ -301,23 +311,7 @@ function loadImportedGame(saveObj, saveString) {
if (tempSaveObj.hasOwnProperty("VersionSave")) {
try {
var ver = JSON.parse(tempSaveObj.VersionSave, Reviver);
// This version refactored the Company/job-related code
if (ver < "0.41.2") {
// Player's company position is now a string
if (Player.companyPosition !== "" || Player.companyPosition instanceof CompanyPosition) {
console.log("Changed Player.companyPosition value to be compatible with v0.41.2");
Player.companyPosition = Player.companyPosition.positionName;
}
// The "companyName" property of all Companies is renamed to "name"
for (var companyName in Companies) {
const company = Companies[companyName];
if (company.name == null && company.companyName != null) {
console.log("Changed company name property to be compatible with v0.41.2");
company.name = company.companyName;
}
}
}
evaluateVersionCompatibility(ver);
} catch(e) {
console.error("Parsing Version save failed: " + e);
}
@@ -400,23 +394,8 @@ function loadImportedGame(saveObj, saveString) {
if (saveObj.hasOwnProperty("VersionSave")) {
try {
var ver = JSON.parse(saveObj.VersionSave, Reviver);
// This version refactored the Company/job-related code
if (ver < "0.41.2") {
// Player's company position is now a string
if (Player.companyPosition !== "" || Player.companyPosition instanceof CompanyPosition) {
console.log("Changed Player.companyPosition value to be compatible with v0.41.2");
Player.companyPosition = Player.companyPosition.positionName;
}
evaluateVersionCompatibility(ver);
// The "companyName" property of all Companies is renamed to "name"
for (var companyName in Companies) {
const company = Companies[companyName];
if (company.name == null && company.companyName != null) {
console.log("Changed company name property to be compatible with v0.41.2");
company.name = company.companyName;
}
}
}
if (ver != CONSTANTS.Version) {
createNewUpdateText();
}
+1 -1
View File
@@ -466,7 +466,7 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
const nextModule = parseQueue.shift();
let code;
if (nextModule.startsWith("https://")) {
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) {
try {
const module = await eval('import(nextModule)');
code = "";
+3
View File
@@ -344,12 +344,15 @@ function processSingleServerGrowth(server, numCycles) {
}
function prestigeHomeComputer(homeComp) {
const hasBitflume = homeComp.programs.includes(Programs.BitFlume.name);
homeComp.programs.length = 0; //Remove programs
homeComp.runningScripts = [];
homeComp.serversOnNetwork = [];
homeComp.isConnectedTo = true;
homeComp.ramUsed = 0;
homeComp.programs.push(Programs.NukeProgram.name);
if (hasBitflume) { homeComp.programs.push(Programs.BitFlume.name); }
//Update RAM usage on all scripts
homeComp.scripts.forEach(function(script) {
+6 -3
View File
@@ -547,7 +547,7 @@ function sellShort(stock, shares, workerScript=null) {
}
function processStockPrices(numCycles=1) {
if (isNaN(StockMarket.storedCycles)) { StockMarket.storedCycles = 0; }
if (StockMarket.storedCycles == null || isNaN(StockMarket.storedCycles)) { StockMarket.storedCycles = 0; }
StockMarket.storedCycles += numCycles;
// Stock Prices updated every 6 seconds on average. But if there are stored
@@ -572,10 +572,10 @@ function processStockPrices(numCycles=1) {
var chc = 50;
if (stock.b) {
chc = (chc + stock.otlkMag)/100;
chc = (chc + stock.otlkMag) / 100;
if (isNaN(chc)) {chc = 0.5;}
} else {
chc = (chc - stock.otlkMag)/100;
chc = (chc - stock.otlkMag) / 100;
if (isNaN(chc)) {chc = 0.5;}
}
if (stock.price >= stock.cap) {
@@ -743,6 +743,7 @@ function displayStockMarketContent() {
"Buy 4S Market Data Access - " + numeralWrapper.format(CONSTANTS.MarketData4SCost, '($0.000a)'),
"4S Market Data - Purchased");
marketDataButton.addEventListener("click", function() {
if (Player.money.lt(CONSTANTS.MarketData4SCost)) { return false; }
Player.has4SData = true;
Player.loseMoney(CONSTANTS.MarketData4SCost);
displayStockMarketContent();
@@ -782,6 +783,7 @@ function displayStockMarketContent() {
"4S Market Data TIX API - Purchased");
if (Player.hasTixApiAccess) {
marketDataTixButton.addEventListener("click", function() {
if (Player.money.lt(CONSTANTS.MarketDataTixApi4SCost)) { return false; }
Player.has4SDataTixApi = true;
Player.loseMoney(CONSTANTS.MarketDataTixApi4SCost);
displayStockMarketContent();
@@ -893,6 +895,7 @@ function displayStockMarketContent() {
//Switch to Portfolio Mode Button
if (modeBtn) {
stockMarketPortfolioMode = false;
modeBtn.innerHTML = "Switch to 'Portfolio' Mode" +
"<span class='tooltiptext'>Displays only the stocks for which you have shares or orders</span>";
modeBtn.addEventListener("click", switchToPortfolioMode);
+7 -7
View File
@@ -105,7 +105,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
" 2 + 2\n",
" 2 + 1 + 1\n",
" 1 + 1 + 1 + 1\n\n",
`How many different ways can ${n} be written as a sum of at least`,
`How many different ways can the number ${n} be written as a sum of at least`,
"two positive integers?"].join(" ");
},
difficulty: 1.5,
@@ -387,8 +387,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
{
desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element",
"represents the stock price on day i:\n\n",
return ["You are given the following array of stock prices (which are numbers)",
"where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`,
"Determine the maximum possible profit you can earn using at most",
"one transaction (i.e. you can only buy and sell the stock once). If no profit can be made",
@@ -421,8 +421,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
{
desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element",
"represents the stock price on day i:\n\n",
return ["You are given the following array of stock prices (which are numbers)",
"where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`,
"Determine the maximum possible profit you can earn using as many",
"transactions as you'd like. A transaction is defined as buying",
@@ -455,8 +455,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
{
desc: (data: number[]) => {
return ["You are given the following array of stock prices where the i-th element",
"represents the stock price on day i:\n\n",
return ["You are given the following array of stock prices (which are numbers)",
"where the i-th element represents the stock price on day i:\n\n",
`${data}\n\n`,
"Determine the maximum possible profit you can earn using at most",
"two transactions. A transaction is defined as buying",
+44 -17
View File
@@ -79,6 +79,7 @@ import 'normalize.css';
import "../css/styles.scss";
import "../css/buttons.scss";
import "../css/mainmenu.scss";
import "../css/characteroverview.scss";
import "../css/terminal.scss";
import "../css/menupages.scss";
import "../css/workinprogress.scss";
@@ -173,6 +174,7 @@ const Engine = {
worldMainMenuButton: null,
travelMainMenuButton: null,
jobMainMenuButton: null,
stockmarketMainMenuButton: null,
createProgramMainMenuButton: null,
factionsMainMenuButton: null,
augmentationsMainMenuButton: null,
@@ -1187,6 +1189,7 @@ const Engine = {
var city = document.getElementById("city-tab");
var travel = document.getElementById("travel-tab");
var job = document.getElementById("job-tab");
var stockmarket = document.getElementById("stock-market-tab");
var bladeburner = document.getElementById("bladeburner-tab");
var corp = document.getElementById("corporation-tab");
var gang = document.getElementById("gang-tab");
@@ -1243,6 +1246,11 @@ const Engine = {
//Passive faction rep gain offline
processPassiveFactionRepGain(numCyclesOffline);
// Stock Market offline progress
if (Player.hasWseAccount) {
processStockPrices(numCyclesOffline);
}
//Gang progress for BitNode 2
if (Player.bitNodeN != null && Player.bitNodeN === 2 && Player.inGang()) {
Player.gang.process(numCyclesOffline, Player);
@@ -1275,12 +1283,14 @@ const Engine = {
else {factions.style.display = "none";}
if (Player.firstAugPurchased) {visibleMenuTabs.push(augmentations);}
else {augmentations.style.display = "none";}
if (Player.firstJobRecvd) {visibleMenuTabs.push(job);}
if (Player.companyPosition !== "") {visibleMenuTabs.push(job);}
else {job.style.display = "none";}
if (Player.firstTimeTraveled) {visibleMenuTabs.push(travel);}
else {travel.style.display = "none";}
if (Player.firstProgramAvailable) {visibleMenuTabs.push(createProgram);}
else {createProgram.style.display = "none";}
if (Player.hasWseAccount) {visibleMenuTabs.push(stockmarket);}
else {stockmarket.style.display = "none";}
if(Player.bladeburner instanceof Bladeburner) {visibleMenuTabs.push(bladeburner);}
else {bladeburner.style.display = "none";}
if(Player.corporation instanceof Corporation) {visibleMenuTabs.push(corp);}
@@ -1323,6 +1333,7 @@ const Engine = {
factions.style.display = "none";
augmentations.style.display = "none";
job.style.display = "none";
stockmarket.style.display = "none";
travel.style.display = "none";
createProgram.style.display = "none";
bladeburner.style.display = "none";
@@ -1539,28 +1550,38 @@ const Engine = {
}
worldHdr.onclick = function() {
var city = document.getElementById("city-tab");
var cityLink = document.getElementById("city-menu-link");
var travel = document.getElementById("travel-tab");
var travelLink = document.getElementById("travel-menu-link");
var job = document.getElementById("job-tab");
var jobLink = document.getElementById("job-menu-link");
var bladeburner = document.getElementById("bladeburner-tab");
var bladeburnerLink = document.getElementById("bladeburner-menu-link");
var corporation = document.getElementById("corporation-tab");
var corporationLink = document.getElementById("corporation-menu-link");
var gang = document.getElementById("gang-tab");
var gangLink = document.getElementById("gang-menu-link");
var city = document.getElementById("city-tab");
var cityLink = document.getElementById("city-menu-link");
var travel = document.getElementById("travel-tab");
var travelLink = document.getElementById("travel-menu-link");
var job = document.getElementById("job-tab");
var jobLink = document.getElementById("job-menu-link");
var stockmarket = document.getElementById("stock-market-tab");
var stockmarketLink = document.getElementById("stock-market-menu-link");
var bladeburner = document.getElementById("bladeburner-tab");
var bladeburnerLink = document.getElementById("bladeburner-menu-link");
var corporation = document.getElementById("corporation-tab");
var corporationLink = document.getElementById("corporation-menu-link");
var gang = document.getElementById("gang-tab");
var gangLink = document.getElementById("gang-menu-link");
// Determine whether certain links should show up
job.style.display = Player.companyPosition !== "" ? "list-item" : "none";
stockmarket.style.display = Player.hasWseAccount ? "list-item" : "none";
bladeburner.style.display = Player.bladeburner instanceof Bladeburner ? "list-item" : "none";
corporation.style.display = Player.corporation instanceof Corporation ? "list-item" : "none";
gang.style.display = Player.inGang() ? "list-item" : "none";
this.classList.toggle("opened");
if (city.style.maxHeight) {
Engine.toggleMainMenuHeader(false,
[city, travel, job, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, bladeburnerLink, corporationLink, gangLink]
[city, travel, job, stockmarket, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, stockmarketLink, bladeburnerLink, corporationLink, gangLink]
);
} else {
Engine.toggleMainMenuHeader(true,
[city, travel, job, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, bladeburnerLink, corporationLink, gangLink]
[city, travel, job, stockmarket, bladeburner, corporation, gang],
[cityLink, travelLink, jobLink, stockmarketLink, bladeburnerLink, corporationLink, gangLink]
);
}
}
@@ -1633,6 +1654,12 @@ const Engine = {
return false;
});
Engine.Clickables.stockmarketMainMenuButton = clearEventListeners("stock-market-menu-link");
Engine.Clickables.stockmarketMainMenuButton.addEventListener("click", function() {
Engine.loadStockMarketContent();
return false;
});
Engine.Clickables.createProgramMainMenuButton = clearEventListeners("create-program-menu-link");
Engine.Clickables.createProgramMainMenuButton.addEventListener("click", function() {
+5 -2
View File
@@ -81,6 +81,9 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<li id="job-tab" class="mainmenu-accordion-panel">
<button id="job-menu-link"> Job </button>
</li>
<li id="stock-market-tab" class="mainmenu-accordion-panel">
<button id="stock-market-menu-link"> Stock Market </button>
</li>
<li id="bladeburner-tab" class="mainmenu-accordion-panel">
<button id="bladeburner-menu-link"> Bladeburner </button>
</li>
@@ -789,8 +792,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
</table>
</div>
<div class="character-quick-options">
<button id="character-overview-save-button">Save Game</button>
<button id="character-overview-options-button">Options</button>
<button id="character-overview-save-button" class="character-overview-btn">Save Game</button>
<button id="character-overview-options-button" class="character-overview-btn">Options</button>
</div>
</div>
</div>