V 0.34.2 Changes

This commit is contained in:
danielyxie 2018-01-27 00:52:39 -06:00
parent 8d72dd0f4e
commit 5f817c8016
17 changed files with 3242 additions and 1124 deletions

@ -126,7 +126,6 @@
margin:4px;
padding:12px;
border-radius:25px;
width:45%;
color:var(--my-font-color);
}

@ -64,26 +64,6 @@
margin-right: 0px;
}
#script-editor-save-and-close-button,
#script-editor-netscript-doc-button,
#script-editor-status-text {
display: inline-block;
}
#script-editor-status-text {
margin: 10px;
}
#script-editor-save-and-close-button {
float:left;
}
#script-editor-netscript-doc-button {
float: right;
padding-right: 0px;
margin-right: 2px;
}
#script-editor-wrapper {
height:100%;
width: 70%;

2544
dist/bundle.js vendored

File diff suppressed because it is too large Load Diff

@ -101,11 +101,7 @@
<div id="javascript-editor"></div>
<div id="script-editor-buttons-wrapper">
<span id="script-editor-save-and-close-button" class="a-link-button">Save & Close (Ctrl + b)</span>
<p id="script-editor-status-text"> </p>
<a id="script-editor-netscript-doc-button" class="a-link-button" href="https://bitburner.wikia.com/wiki/Netscript" target="_blank"> Netscript Documentation </a>
</div>
<div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor -->
</div> <!-- End wrapper -->
<div id="script-editor-options-panel">

@ -356,7 +356,7 @@ let CompanyPositions = {
CompanyPositions.VicePresident.setPerformanceParameters(70, 0, 0, 0, 0, 30, 1.75);
CompanyPositions.VicePresident.setExperienceGains(1.2, 0, 0, 0, 0, .6);
CompanyPositions.CTO.setPerformanceParameters(65, 0, 0, 0, 0, 35, 2);
CompanyPositions.CTO.setExperienceGains(1.5, 0, 0, 0, 1);
CompanyPositions.CTO.setExperienceGains(1.5, 0, 0, 0, 0, 1);
//Business
CompanyPositions.BusinessIntern.setPerformanceParameters(10, 0, 0, 0, 0, 90, 0.9);

@ -59,12 +59,10 @@ var WarehouseInitialCost = 5e9; //Initial purchase cost of warehouse
var WarehouseInitialSize = 100;
var WarehouseUpgradeBaseCost = 1e9;
var OfficeInitialCost = 5e9;
var OfficeInitialCost = 4e9;
var OfficeInitialSize = 3;
var OfficeUpgradeBaseCost = 1e9;
function Material(params={}) {
this.name = params.name ? params.name : "";
this.qty = 0; //Quantity
@ -529,6 +527,20 @@ var ProductRatingWeights = {
}
}
//Industry upgrades
//The structure is:
// [index in array, base price, price mult, benefit mult (if applicable), name, desc]
var IndustryUpgrades = {
"0": [0, 500e3, 1, 1.05,
"Coffee", "Provide your employees with coffee, increasing their energy by 5%."],
"1": [1, 1e9, 1.02, 1.01,
"AdVert.Inc", "Hire AdVert.Inc to advertise your company. Each level of " +
"this upgrade grants your company a static increase of 5 and 1 to its awareness and " +
"popularity, respectively. It will then increase your company's awareness by 1%, and its popularity " +
"by a random percentage between 5% and 15%. These effects are increased by other upgrades " +
"that increase the power of your advertising."]
}
var empManualAssignmentModeActive = false;
function Industry(params={}) {
this.offices = { //Maps locations to offices. 0 if no office at that location
@ -594,6 +606,10 @@ function Industry(params={}) {
this.thisCycleRevenue = new Decimal(0);
this.thisCycleExpenses = new Decimal(0);
//Upgrades
var numUpgrades = Object.keys(IndustryUpgrades).length;
this.upgrades = Array(numUpgrades).fill(0);
this.state = "START";
this.init();
@ -750,6 +766,10 @@ Industry.prototype.init = function() {
case Industries.Software:
this.sciFac = 0.7;
this.advFac = 0.5;
this.hwFac = 0.25;
this.reFac = 0.1;
this.aiFac = 0.1;
this.robFac = 0.05;
this.reqMats = {
"Hardware": 0.5,
"Energy": 1,
@ -878,6 +898,18 @@ Industry.prototype.process = function(marketCycles=1, state, company) {
//Process change in demand/competition of materials/products
this.processMaterialMarket(marketCycles);
this.processProductMarket(marketCycles);
//Process loss of popularity
this.popularity -= (marketCycles * .0001);
this.popularity = Math.max(0, this.popularity);
//Process Dreamsense gains
var popularityGain = company.getDreamSenseGain(), awarenessGain = popularityGain * 4;
if (popularityGain > 0) {
this.popularity += (popularityGain * marketCycles);
this.awareness += (awarenessGain * marketCycles);
}
return;
}
@ -1048,9 +1080,6 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
Math.pow(this.sciResearch.qty, this.sciFac) +
Math.pow(warehouse.materials["AICores"].qty, this.aiFac) / 10e3);
}
} else {
console.log("Production of materials failed because producableFrac <= 0 or prod <= 0.");
console.log("prod: " + prod);
}
//Per second
var fooProd = prod * producableFrac / (SecsPerMarketCycle * marketCycles);
@ -1065,7 +1094,10 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
for (var matName in warehouse.materials) {
if (warehouse.materials.hasOwnProperty(matName)) {
var mat = warehouse.materials[matName];
if (mat.sCost < 0 || mat.sllman[0] === false) {continue;}
if (mat.sCost < 0 || mat.sllman[0] === false) {
mat.sll = 0;
continue;
}
var mat = warehouse.materials[matName];
var sCost;
@ -1093,7 +1125,8 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
}
var businessFactor = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]);
var maxSell = (mat.qlt + .001) * mat.dmd * (100 - mat.cmp)/100 * markup * businessFactor *
(this.awareness === 0 ? 0.01 : (this.popularity + .001) / this.awareness);
Math.pow(this.awareness + 1, 0.05) * Math.pow(this.popularity + 1, 0.07) *
(this.awareness === 0 ? 0.01 : Math.max((this.popularity + .001) / this.awareness, 0.01));
var sellAmt;
if (mat.sllman[1] !== -1) {
//Sell amount is manually limited
@ -1301,7 +1334,8 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
}
}
var maxSell = Math.pow(product.rat, 0.95) * product.dmd * (1-(product.cmp/100)) *
(this.awareness === 0 ? 0.01 : (this.popularity+0.001) / this.awareness) * markup;
markup * businessFactor * Math.pow(this.awareness + 1, 0.05) * Math.pow(this.popularity + 1, 0.07) *
(this.awareness === 0 ? 0.01 : Math.max((this.popularity + .001) / this.awareness, 0.01));
var sellAmt;
if (product.sllman[city][0] && product.sllman[city][1] > 0) {
//Sell amount is manually limited
@ -1345,6 +1379,33 @@ Industry.prototype.discontinueProduct = function(product, parentRefs) {
}
}
Industry.prototype.upgrade = function(upgrade, refs) {
var corporation = refs.corporation, division = refs.division,
office = refs.office;
var upgN = upgrade[0], basePrice = upgrade[1], priceMult = upgrade[2],
upgradeBenefit = upgrade[3];
while (this.upgrades.length <= upgN) {this.upgrades.push(0);}
++this.upgrades[upgN];
switch (upgN) {
case 0: //Coffee, 5% energy per employee
for (var i = 0; i < office.employees.length; ++i) {
office.employees[i].ene = Math.min(office.employees[i].ene * 1.05, 100);
}
break;
case 1: //AdVert.Inc,
var advMult = corporation.getAdvertisingMultiplier();
this.awareness += (5 * advMult);
this.popularity += (1 * advMult);
this.awareness *= (1.01 * advMult);
this.popularity *= ((1 + Math.random(5, 15) / 100) * advMult);
break;
default:
console.log("ERROR: Un-implemented function index: " + upgN);
break;
}
}
Industry.prototype.toJSON = function() {
return Generic_toJSON("Industry", this);
}
@ -1385,7 +1446,7 @@ function Employee(params={}) {
this.pro = 0; //Productivity, This is calculated
this.loc = params.loc ? params.loc : "";
this.pos = params.position ? params.position : EmployeePositions.Operations;
this.pos = EmployeePositions.Unassigned;
}
//Returns the amount the employee needs to be paid
@ -1444,6 +1505,9 @@ Employee.prototype.calculateProductivity = function(corporation) {
prodMult = (1.5 * effInt) + (0.8 * this.exp) + (effCre) +
(0.5 * effEff);
break;
case EmployeePositions.Unassigned:
prodMult = 0;
break;
default:
console.log("ERROR: Invalid employee position: " + this.pos);
break;
@ -1612,7 +1676,8 @@ OfficeSpace.prototype.calculateEmployeeProductivity = function(marketCycles=1, c
}
//Takes care of UI as well
OfficeSpace.prototype.findEmployees = function(company) {
OfficeSpace.prototype.findEmployees = function(parentRefs) {
var company = parentRefs.corporation, division = parentRefs.division;
if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) {return;}
//Generate three random employees (meh, decent, amazing)
@ -1667,7 +1732,7 @@ OfficeSpace.prototype.findEmployees = function(company) {
"Efficiency: " + formatNumber(employee.eff, 1) + "<br>" +
"Salary: " + numeral(employee.sal).format('$0.000a') + " \ s<br>",
clickListener:()=>{
office.hireEmployee(employee, company);
office.hireEmployee(employee, parentRefs);
removeElementById("cmpy-mgmt-hire-employee-popup");
return false;
}
@ -1694,7 +1759,8 @@ OfficeSpace.prototype.findEmployees = function(company) {
createPopup("cmpy-mgmt-hire-employee-popup", elems);
}
OfficeSpace.prototype.hireEmployee = function(employee, company) {
OfficeSpace.prototype.hireEmployee = function(employee, parentRefs) {
var company = parentRefs.corporation, division = parentRefs.division;
var yesBtn = yesNoTxtInpBoxGetYesButton(),
noBtn = yesNoTxtInpBoxGetNoButton();
yesBtn.innerHTML = "Hire";
@ -1709,7 +1775,7 @@ OfficeSpace.prototype.hireEmployee = function(employee, company) {
}
employee.name = name;
this.employees.push(employee);
company.updateUIContent();
company.displayDivisionContent(division, currentCityUi);
return yesNoTxtInpBoxClose();
});
noBtn.addEventListener("click", ()=>{
@ -1718,6 +1784,28 @@ OfficeSpace.prototype.hireEmployee = function(employee, company) {
yesNoTxtInpBoxCreate("Give your employee a nickname!");
}
//Finds the first unassigned employee and assigns its to the specified job
OfficeSpace.prototype.assignEmployeeToJob = function(job) {
for (var i = 0; i < this.employees.length; ++i) {
if (this.employees[i].pos === EmployeePositions.Unassigned) {
this.employees[i].pos = job;
return true;
}
}
return false;
}
//Finds the first employee with the given job and unassigns it
OfficeSpace.prototype.unassignEmployeeFromJob = function(job) {
for (var i = 0; i < this.employees.length; ++i) {
if (this.employees[i].pos === job) {
this.employees[i].pos = EmployeePositions.Unassigned;
return true;
}
}
return false;
}
OfficeSpace.prototype.toJSON = function() {
return Generic_toJSON("OfficeSpace", this);
}
@ -2099,16 +2187,22 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
buttonPanel.appendChild(createElement("br", {})); // Force line break
//Button to set sell amount
var innerTextString = (mat.sllman[1] === -1 ? "Sell (" + formatNumber(mat.sll, 3) + "/MAX)" :
"Sell (" + formatNumber(mat.sll, 3) + "/" + formatNumber(mat.sllman[1], 3) + ")")
if (mat.sCost) {
if (isString(mat.sCost)) {
var sCost = mat.sCost.replace(/MP/g, mat.bCost);
innerTextString += " @ $" + formatNumber(eval(sCost), 2);
} else {
innerTextString += " @ $" + formatNumber(mat.sCost, 2);
var innerTextString;
if (mat.sllman[0]) {
innerTextString = (mat.sllman[1] === -1 ? "Sell (" + formatNumber(mat.sll, 3) + "/MAX)" :
"Sell (" + formatNumber(mat.sll, 3) + "/" + formatNumber(mat.sllman[1], 3) + ")");
if (mat.sCost) {
if (isString(mat.sCost)) {
var sCost = mat.sCost.replace(/MP/g, mat.bCost);
innerTextString += " @ $" + formatNumber(eval(sCost), 2);
} else {
innerTextString += " @ $" + formatNumber(mat.sCost, 2);
}
}
} else {
innerTextString = "Sell (0.000/0.000)";
}
buttonPanel.appendChild(createElement("a", {
innerText: innerTextString, display:"inline-block", class:"a-link-button",
clickListener:()=>{
@ -2124,16 +2218,17 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"changing price that depends on the market price. For example, if you set the sell price " +
"to 'MP+10' then it will always be sold at $10 above the market price.",
});
var br = createElement("br", {});
var inputQty = createElement("input", {
type:"text", value: mat.sllman[1] ? mat.sllman[1] : null, placeholder: "Sell amount"
type:"text", marginTop:"4px",
value: mat.sllman[1] ? mat.sllman[1] : null, placeholder: "Sell amount"
});
var inputPx = createElement("input", {
type:"text", value: mat.sCost ? mat.sCost : null, placeholder: "Sell price"
type:"text", marginTop:"4px",
value: mat.sCost ? mat.sCost : null, placeholder: "Sell price"
});
var confirmBtn = createElement("a", {
innerText:"Confirm",
class:"a-link-button",
margin:"6px",
innerText:"Confirm", class:"a-link-button", margin:"6px",
clickListener:()=>{
//Parse price
//Sanitize cost
@ -2164,6 +2259,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
if (isNaN(qty)) {qty = 0;}
if (qty === 0) {
mat.sllman[0] = false;
mat.sllman[1] = 0;
} else {
mat.sllman[0] = true;
mat.sllman[1] = qty;
@ -2176,14 +2272,12 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
}
});
var cancelBtn = createElement("a", {
innerText:"Cancel",
class:"a-link-button",
margin: "6px",
innerText:"Cancel", class:"a-link-button", margin: "6px",
clickListener:()=>{
removeElementById(sellPopupid);
}
});
createPopup(sellPopupid, [txt, inputQty, inputPx, confirmBtn, cancelBtn]);
createPopup(sellPopupid, [txt, br, inputQty, inputPx, confirmBtn, cancelBtn]);
inputQty.focus();
}
}));
@ -2427,46 +2521,48 @@ var CorporationUnlockUpgrades = {
// name, desc]
var CorporationUpgrades = {
//Smart factories, increases production
"0": [0, 4e9, 1.07, 0.02,
"0": [0, 2e9, 1.07, 0.02,
"Smart Factories", "Advanced AI automatically optimizes the operation and productivity " +
"of factories. Each level of this upgrade increases your global production by 2% (additive)."],
//Smart warehouses, increases storage size
"1": [1, 4e9, 1.07, .1,
"1": [1, 2e9, 1.07, .1,
"Smart Storage", "Advanced AI automatically optimizes your warehouse storage methods. " +
"Each level of this upgrade increases your global warehouse storage size by 10% (additive)."],
//Advertise through dreams, passive popularity/ awareness gain
"2": [2, 999999e9, 1.08, .001,
"DreamSense", "NOT YET IMPLEMENTED! - Use DreamSense LCC Technologies to advertise your corporation " +
"2": [2, 8e9, 1.09, .001,
"DreamSense", "Use DreamSense LCC Technologies to advertise your corporation " +
"to consumers through their dreams. Each level of this upgrade provides a passive " +
"increase in awareness of your company by 0.001 / second."],
"increase in awareness of all of your companies (divisions) by 0.004 / market cycle," +
"and in popularity by 0.001 / market cycle. A market cycle is approximately " +
"20 seconds."],
//Makes advertising more effective
"3": [3, 999999e9, 1.11, 0.1,
"Wilson Analytics", "NOT YET IMPLEMENTED - Purchase data and analysis from Wilson, a marketing research " +
"3": [3, 4e9, 1.11, 0.1,
"Wilson Analytics", "Purchase data and analysis from Wilson, a marketing research " +
"firm. Each level of this upgrades increases the effectiveness of your " +
"advertising by 10% (additive)."],
//Augmentation for employees, increases cre
"4": [4, 2e9, 1.06, 0.1,
"4": [4, 1e9, 1.06, 0.1,
"Nuoptimal Nootropic Injector Implants", "Purchase the Nuoptimal Nootropic " +
"Injector augmentation for your employees. Each level of this upgrade " +
"globally increases the creativity of your employees by 10% (additive)."],
//Augmentation for employees, increases cha
"5": [5, 2e9, 1.06, 0.1,
"5": [5, 1e9, 1.06, 0.1,
"Speech Processor Implants", "Purchase the Speech Processor augmentation for your employees. " +
"Each level of this upgrade globally increases the charisma of your employees by 10% (additive)."],
//Augmentation for employees, increases int
"6": [6, 2e9, 1.06, 0.1,
"6": [6, 1e9, 1.06, 0.1,
"Neural Accelerators", "Purchase the Neural Accelerator augmentation for your employees. " +
"Each level of this upgrade globally increases the intelligence of your employees " +
"by 10% (additive)."],
//Augmentation for employees, increases eff
"7": [7, 2e9, 1.06, 0.1,
"7": [7, 1e9, 1.06, 0.1,
"FocusWires", "Purchase the FocusWire augmentation for your employees. Each level " +
"of this upgrade globally increases the efficiency of your employees by 10% (additive)."],
@ -2477,7 +2573,7 @@ var CorporationUpgrades = {
"by 1% (additive)."],
//Improves scientific research rate
"9": [9, 5e9, 1.08, 0.05,
"9": [9, 5e9, 1.07, 0.05,
"Project Insight", "Purchase 'Project Insight', a R&D service provided by the secretive " +
"Fulcrum Technologies. Each level of this upgrade globally increases the amount of " +
"Scientific Research you produce by 5% (additive)."],
@ -2544,7 +2640,7 @@ Corporation.prototype.process = function(numCycles=1) {
Corporation.prototype.determineValuation = function() {
var val, profit = (this.revenue.minus(this.expenses)).toNumber();
if (this.public) {
val = 25e9 + this.funds.toNumber() + (profit * 25e3);
val = this.funds.toNumber() + (profit * 100e3);
val *= (Math.pow(1.1, this.divisions.length));
val = Math.max(val, 0);
} else {
@ -2578,7 +2674,7 @@ Corporation.prototype.getInvestment = function() {
case 4:
return;
}
var funding = val * percShares,
var funding = val * percShares * 2,
investShares = Math.floor(TOTALSHARES * percShares),
yesBtn = yesNoBoxGetYesButton(),
noBtn = yesNoBoxGetNoButton();
@ -2588,6 +2684,7 @@ Corporation.prototype.getInvestment = function() {
++this.fundingRound;
this.funds = this.funds.plus(funding);
this.numShares -= investShares;
this.displayCorporationOverviewContent();
return yesNoBoxClose();
});
noBtn.addEventListener("click", ()=>{
@ -2609,12 +2706,13 @@ Corporation.prototype.goPublic = function() {
numeral(initialSharePrice).format('$0.000a') + " per share.<br><br>" +
"Furthermore, issuing more shares now will help drive up " +
"your company's stock price in the future.<br><br>" +
"You have a total of " + this.numShares + " of shares that you can issue.",
"You have a total of " + numeral(this.numShares).format("0.000a") + " of shares that you can issue.",
});
var input = createElement("input", {
type:"number",
placeholder: "Shares to issue",
});
var br = createElement("br", {});
var yesBtn = createElement("a", {
class:"a-link-button",
innerText:"Go Public",
@ -2646,7 +2744,7 @@ Corporation.prototype.goPublic = function() {
return false;
}
});
createPopup(goPublicPopupId, [txt, input, yesBtn, noBtn]);
createPopup(goPublicPopupId, [txt, br, input, yesBtn, noBtn]);
}
Corporation.prototype.updateSharePrice = function() {
@ -2715,6 +2813,11 @@ Corporation.prototype.getStorageMultiplier = function() {
if (isNaN(mult) || mult < 1) {return 1;} else {return mult;}
}
Corporation.prototype.getDreamSenseGain = function() {
var gain = this.upgradeMultipliers[2] - 1;
return gain <= 0 ? 0 : gain;
}
Corporation.prototype.getAdvertisingMultiplier = function() {
var mult = this.upgradeMultipliers[3];
if (isNaN(mult) || mult < 1) {return 1;} else {return mult;}
@ -2750,14 +2853,13 @@ Corporation.prototype.getScientificResearchMultiplier = function() {
if (isNaN(mult) || mult < 1) {return 1;} else {return mult;}
}
//Keep 'global' variables for DOM elements so we don't have to search
//through the DOM tree repeatedly when updating UI
var companyManagementDiv, companyManagementHeaderTabs, companyManagementPanel,
currentCityUi,
corporationUnlockUpgrades, corporationUpgrades,
industryOverviewPanel, industryOverviewText,
industryEmployeePanel, industryEmployeeText, industryEmployeeHireButton, industryEmployeeList,
industryEmployeePanel, industryEmployeeText, industryEmployeeHireButton, industryEmployeeManagementUI, industryEmployeeInfo,
industryOfficeUpgradeSizeButton,
industryWarehousePanel,
headerTabs, cityTabs;
@ -2980,13 +3082,37 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
this.selectHeaderTab(headerTabs[0]);
}
//Check if player has Corporation Handbook
var homeComp = Player.getHomeComputer(), hasHandbook = false,
handbookFn = "corporation-management-handbook.lit";
for (var i = 0; i < homeComp.messages.length; ++i) {
if (isString(homeComp.messages[i]) && homeComp.messages[i] === handbookFn) {
hasHandbook = true;
break;
}
}
if (!hasHandbook) {
companyManagementPanel.appendChild(createElement("a", {
class:"a-link-button", innerText:"Get Handbook", display:"inline-block",
tooltip:"Get a copy of 'The Complete Handbook for Creating a Successful Corporation.'" +
"This is a .lit file that provides some tips/pointers for helping you get started with " +
"starting and managing a Corporation.",
clickListener:()=>{
homeComp.messages.push(handbookFn);
this.displayCorporationOverviewContent();
return false;
}
}));
}
//Investors
if (this.public) {
//Sell share buttons
var sellShares = createElement("a", {
class:"a-link-button tooltip",
innerText:"Sell Shares",
display:"inline-block",
class:"a-link-button", innerText:"Sell Shares", display:"inline-block",
tooltip:"Sell your shares in the company. This is the only way to " +
"profit from your business venture.",
clickListener:()=>{
var popupId = "cmpy-mgmt-sell-shares-popup";
var currentStockPrice = this.sharePrice;
@ -2996,8 +3122,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
});
var profitIndicator = createElement("p", {});
var input = createElement("input", {
type:"number",
placeholder:"Shares to sell",
type:"number", placeholder:"Shares to sell", margin:"5px",
inputListener: ()=> {
var numShares = Math.round(input.value);
if (isNaN(numShares)) {
@ -3011,9 +3136,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
});
var confirmBtn = createElement("a", {
class:"a-link-button",
innerText:"Sell shares",
display:"inline-block",
class:"a-link-button", innerText:"Sell shares", display:"inline-block",
clickListener:()=>{
var shares = Math.round(input.value);
if (isNaN(shares)) {
@ -3031,9 +3154,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
});
var cancelBtn = createElement("a", {
class:"a-link-button",
innerText:"Cancel",
display:"inline-block",
class:"a-link-button", innerText:"Cancel", display:"inline-block",
clickListener:()=>{
removeElementById(popupId);
return false;
@ -3042,18 +3163,11 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
createPopup(popupId, [txt, profitIndicator, input, confirmBtn, cancelBtn]);
}
});
var sellSharesTooltip = createElement("span", {
class:"tooltiptext",
innerText:"Sell your shares in the company. This is the only way to " +
"profit from your business venture.",
});
sellShares.appendChild(sellSharesTooltip);
//Buyback shares button
var buybackShares = createElement("a", {
class:"a-link-button tooltip",
innerText:"Buyback shares",
display:"inline-block",
class:"a-link-button", innerText:"Buyback shares", display:"inline-block",
tooltip:"Buy back shares you that previously issued or sold at market price.",
clickListener:()=>{
var popupId = "cmpy-mgmt-buyback-shares-popup";
var currentStockPrice = this.sharePrice;
@ -3063,8 +3177,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
});
var costIndicator = createElement("p", {});
var input = createElement("input", {
type:"number",
placeholder:"Shares to sell",
type:"number", placeholder:"Shares to buyback", margin:"5px",
inputListener: ()=> {
var numShares = Math.round(input.value);
//TODO add conditional for if player doesn't have enough money
@ -3080,9 +3193,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
});
var confirmBtn = createElement("a", {
class:"a-link-button",
innerText:"Sell shares",
display:"inline-block",
class:"a-link-button", innerText:"Buy shares", display:"inline-block",
clickListener:()=>{
var shares = Math.round(input.value);
var tempStockPrice = this.sharePrice;
@ -3096,6 +3207,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
} else {
this.numShares += shares;
this.issuedShares -= shares;
Player.loseMoney(shares * tempStockPrice);
//TODO REMOVE from Player money
removeElementById(popupId);
}
@ -3115,12 +3227,6 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
createPopup(popupId, [txt, costIndicator, input, confirmBtn, cancelBtn]);
}
});
var buybackSharesTooltip = createElement("span", {
class:"tooltiptext",
innerText:"Buy back shares you that previously issued or sold at market " +
"price."
});
buybackShares.appendChild(buybackSharesTooltip);
companyManagementPanel.appendChild(sellShares);
companyManagementPanel.appendChild(buybackShares);
@ -3133,12 +3239,14 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
this.getInvestment();
}
});
var findInvestorsTooltip = createElement("span", {
class:"tooltiptext",
innerText:"Search for private investors who will give you startup funding in exchange " +
"for equity (stock shares) in your company"
});
findInvestors.appendChild(findInvestorsTooltip);
if (this.fundingRound < 4) {
var findInvestorsTooltip = createElement("span", {
class:"tooltiptext",
innerText:"Search for private investors who will give you startup funding in exchange " +
"for equity (stock shares) in your company"
});
findInvestors.appendChild(findInvestorsTooltip);
}
var goPublic = createElement("a", {
class:"a-link-button tooltip",
@ -3196,7 +3304,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
}
upgradeContainer.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div",
class:"cmpy-mgmt-upgrade-div", width:"45%",
innerHTML:upgrade[2] + " - " + numeral(upgrade[1]).format("$0.000a"),
tooltip: upgrade[3],
clickListener:()=>{
@ -3228,7 +3336,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
var baseCost = upgrade[1], priceMult = upgrade[2];
var cost = baseCost * Math.pow(priceMult, corp.upgrades[i]);
upgradeContainer.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div",
class:"cmpy-mgmt-upgrade-div", width:"45%",
innerHTML:upgrade[4] + " - " + numeral(cost).format("$0.000a"),
tooltip:upgrade[5],
clickListener:()=>{
@ -3312,7 +3420,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
innerText: "Would you like to expand into a new city by opening an office? " +
"This would cost " + numeral(OfficeInitialCost).format('$0.000a'),
});
var citySelector = createElement("select", {});
var citySelector = createElement("select", {margin:"5px"});
for (var cityName in division.offices) {
if (division.offices.hasOwnProperty(cityName)) {
if (!(division.offices[cityName] instanceof OfficeSpace)) {
@ -3356,39 +3464,87 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
}));
companyManagementPanel.appendChild(createElement("br", {})); // Force line break
//Get office object
var office = division.offices[currentCityUi];
if (!(office instanceof OfficeSpace)) {
console.log("ERROR: Current city for UI does not have an office space");
return;
}
//Left and right panels
var leftPanel = createElement("div", {
class:"cmpy-mgmt-industry-left-panel"
});
var rightPanel = createElement("div", {
class:"cmpy-mgmt-industry-right-panel"
});
var leftPanel = createElement("div", {class:"cmpy-mgmt-industry-left-panel"});
var rightPanel = createElement("div", {class:"cmpy-mgmt-industry-right-panel"});
companyManagementPanel.appendChild(leftPanel);
companyManagementPanel.appendChild(rightPanel);
//Different sections (Overview, Employee/Office, and Warehouse)
industryOverviewPanel = createElement("div", {
id:"cmpy-mgmt-industry-overview-panel",
class:"cmpy-mgmt-industry-overview-panel"
id:"cmpy-mgmt-industry-overview-panel", class:"cmpy-mgmt-industry-overview-panel"
});
leftPanel.appendChild(industryOverviewPanel);
industryEmployeePanel = createElement("div", {
id:"cmpy-mgmt-employee-panel",
class:"cmpy-mgmt-employee-panel"
id:"cmpy-mgmt-employee-panel", class:"cmpy-mgmt-employee-panel"
});
leftPanel.appendChild(industryEmployeePanel);
industryWarehousePanel = createElement("div", {
id:"cmpy-mgmt-warehouse-panel",
class:"cmpy-mgmt-warehouse-panel"
id:"cmpy-mgmt-warehouse-panel", class:"cmpy-mgmt-warehouse-panel"
});
rightPanel.appendChild(industryWarehousePanel);
//Industry overview text element
//Industry overview text
industryOverviewText = createElement("p", {});
industryOverviewPanel.appendChild(industryOverviewText);
//Industry overview Purchases & Upgrades
var numUpgrades = Object.keys(IndustryUpgrades).length;
while (division.upgrades.length < numUpgrades) {division.upgrades.push(0);} //Backwards compatibility
var industryOverviewUpgrades = createElement("div", {});
industryOverviewUpgrades.appendChild(createElement("h1", {innerText:"Purchases & Upgrades", margin:"4px", padding:"4px"}));
for (var i = 0; i < numUpgrades; ++i) {
(function(i, corp, division, office) {
var upgrade = IndustryUpgrades[i.toString()];
if (upgrade == null) {
console.log("ERROR: Could not find levelable upgrade index: " + i);
return;
}
var baseCost = upgrade[1], priceMult = upgrade[2], cost = 0;
switch(i) {
case 0: //Coffee, cost is static per employee
cost = office.employees.length * baseCost;
break;
default:
cost = baseCost * Math.pow(priceMult, division.upgrades[i]);
break;
}
industryOverviewUpgrades.appendChild(createElement("div", {
class:"cmpy-mgmt-upgrade-div", display:"inline-block",
innerHTML:upgrade[4] + ' - ' + numeral(cost).format("$0.000a"),
tooltip:upgrade[5],
clickListener:()=>{
if (corp.funds.lt(cost)) {
dialogBoxCreate("Insufficient funds");
} else {
corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, {
corporation:corp,
office:office,
});
corp.displayDivisionContent(division, city);
}
}
}));
industryOverviewUpgrades.appendChild(createElement("br", {}));
})(i, this, division, office);
}
industryOverviewPanel.appendChild(industryOverviewUpgrades);
//Industry Overview 'Create Product' button if applicable
if (division.makesProducts) {
//Get the text on the button based on Industry type
@ -3522,11 +3678,6 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
}
//Employee and Office Panel
var office = division.offices[currentCityUi];
if (!(office instanceof OfficeSpace)) {
console.log("ERROR: Current city for UI does not have an office space");
return;
}
industryEmployeeText = createElement("p", {
id: "cmpy-mgmt-employee-p",
display:"block",
@ -3543,7 +3694,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
innerText:"Hire Employee",
display:"inline-block",
clickListener:()=>{
office.findEmployees(this);
office.findEmployees({corporation:this, division:division});
return false;
}
});
@ -3554,7 +3705,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
class:"a-link-button", innerText:"Upgrade Office size", display:"inline-block", margin:"6px",
clickListener:()=>{
var popupId = "cmpy-mgmt-upgrade-office-size-popup";
var upgradeCost = OfficeInitialCost * Math.pow(1.13, Math.round(office.size / OfficeInitialSize));
var upgradeCost = OfficeInitialCost * Math.pow(1.07, Math.round(office.size / OfficeInitialSize));
var text = createElement("p", {
innerHTML:"Increase the size of your office space to fit " + OfficeInitialSize +
" more employees. This will cost " + numeral(upgradeCost).format('$0.000a'),
@ -3596,9 +3747,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
//Throw Office Party
industryEmployeePanel.appendChild(createElement("br",{}));
industryEmployeePanel.appendChild(createElement("a", {
class:"a-link-button",
display:"inline-block",
innerText:"Throw Office Party",
class:"a-link-button", display:"inline-block", innerText:"Throw Office Party",
tooltip:"Throw an office party to increase your employee's morale and happiness",
clickListener:()=>{
var popupId = "cmpy-mgmt-throw-office-party-popup";
@ -3610,7 +3759,7 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
innerText:"Throwing this party will cost a total of $0"
});
var input = createElement("input", {
type:"number",
type:"number", margin:"5px", placeholder:"$ / employee",
inputListener:()=>{
if (isNaN(input.value) || input.value < 0) {
totalCostTxt.innerText = "Invalid value entered!"
@ -3658,36 +3807,131 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
}
}));
/*
//Employee list
var industryEmployeeList = createElement("ul", {
id:"cmpy-mgmt-employee-ul"
});
industryEmployeePanel.appendChild(industryEmployeeList);
for (var i = 0; i < office.employees.length; ++i) {
(function(corp) {
var emp = office.employees[i];
var li = createAccordionElement({
id:"cmpy-mgmt-employee-" + emp.name,
hdrText:emp.name,
});
var panel = li.children[1];
if (panel == null) {
console.log("ERROR: Could not find employee accordion panel");
return;
}
emp.createUI(panel, corp);
industryEmployeeList.appendChild(li);
})(this);
}
*/
industryEmployeeManagementUI = createElement("div", {});
if (empManualAssignmentModeActive) {
//Employees manually assigned
industryEmployeeManagementUI.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", margin:"4px",
innerText:"Switch to Auto Mode",
tooltip:"Switch to Automatic Assignment Mode, which will automatically " +
"assign employees to your selected jobs. You simply have to select " +
"the number of assignments for each job",
clickListener:()=>{
empManualAssignmentModeActive = false;
this.displayDivisionContent(division, city);
}
}));
industryEmployeeManagementUI.appendChild(createElement("br", {}));
industryEmployeeInfo = createElement("div", {margin:"4px", padding:"4px"});
var selector = createElement("select", {
color: "white", backgroundColor:"black", margin:"4px", padding:"4px",
changeListener:()=>{
var name = selector.options[selector.selectedIndex].text;
for (var i = 0; i < office.employees.length; ++i) {
if (office.employees[i].name === name) {
removeChildrenFromElement(industryEmployeeInfo);
office.employees[i].createUI(industryEmployeeInfo, this);
return;
}
}
console.log("ERROR: Employee in selector could not be found");
}
});
for (var i = 0; i < office.employees.length; ++i) {
selector.add(createElement("option", {text:office.employees[i].name}));
}
selector.selectedIndex = -1;
industryEmployeeManagementUI.appendChild(selector);
industryEmployeeManagementUI.appendChild(industryEmployeeInfo);
} else {
//Player only manages the number of each occupation, not who gets what job
industryEmployeeManagementUI.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", margin:"4px",
innerText:"Switch to Manual Mode",
tooltip:"Switch to Manual Assignment Mode, which allows you to " +
"specify which employees should get which jobs",
clickListener:()=>{
empManualAssignmentModeActive = true;
this.displayDivisionContent(division, city);
}
}));
industryEmployeeManagementUI.appendChild(createElement("br", {}));
var opCount = 0, engCount = 0, busCount = 0,
mgmtCount = 0, rndCount = 0, unassignedCount = 0;
for (var i = 0; i < office.employees.length; ++i) {
switch (office.employees[i].pos) {
case EmployeePositions.Operations:
++opCount; break;
case EmployeePositions.Engineer:
++engCount; break;
case EmployeePositions.Business:
++busCount; break;
case EmployeePositions.Management:
++mgmtCount; break;
case EmployeePositions.RandD:
++rndCount; break;
case EmployeePositions.Unassigned:
++unassignedCount; break;
default:
console.log("ERROR: Unrecognized employee position: " + office.employees[i].pos);
break;
}
}
//Unassigned employee count display
industryEmployeeManagementUI.appendChild(createElement("p", {
display:"inline-block",
innerText:"Unassigned Employees: " + unassignedCount,
}));
industryEmployeeManagementUI.appendChild(createElement("br", {}));
//General display of employee information (avg morale, avg energy, etc.)
industryEmployeeInfo = createElement("p", {margin:"4px", padding:"4px"});
industryEmployeeManagementUI.appendChild(industryEmployeeInfo);
industryEmployeeManagementUI.appendChild(createElement("br", {}));
var positions = [EmployeePositions.Operations, EmployeePositions.Engineer,
EmployeePositions.Business, EmployeePositions.Management,
EmployeePositions.RandD];
var counts = [opCount, engCount, busCount, mgmtCount, rndCount];
for (var i = 0; i < positions.length; ++i) {
(function(corp, i) {
var info = createElement("h2", {
display:"inline-block", width:"40%",
innerText: positions[i] + "(" + counts[i] + ")"
});
var plusBtn = createElement("a", {
class: unassignedCount > 0 ? "a-link-button" : "a-link-button-inactive",
display:"inline-block", innerText:"+",
clickListener:()=>{
office.assignEmployeeToJob(positions[i]);
corp.displayDivisionContent(division, city);
}
});
var minusBtn = createElement("a", {
class: counts[i] > 0 ? "a-link-button" : "a-link-button-inactive",
display:"inline-block", innerText:"-",
clickListener:()=>{
office.unassignEmployeeFromJob(positions[i]);
corp.displayDivisionContent(division, city);
}
});
var newline = createElement("br", {});
industryEmployeeManagementUI.appendChild(info);
industryEmployeeManagementUI.appendChild(plusBtn);
industryEmployeeManagementUI.appendChild(minusBtn);
industryEmployeeManagementUI.appendChild(newline);
})(this, i);
}
}
industryEmployeePanel.appendChild(industryEmployeeManagementUI);
//Warehouse Panel
var warehouse = division.warehouses[currentCityUi];
@ -3725,8 +3969,8 @@ Corporation.prototype.updateDivisionContent = function(division) {
profitStr = profit >= 0 ? numeral(profit).format("$0.000a") : "-" + numeral(-1 * profit).format("$0.000a");
industryOverviewText.innerHTML =
"Industry: " + division.type + "<br><br>" +
"Awareness: " + division.awareness + "<br>" +
"Popularity: " + division.popularity + "<br><br>" +
"Awareness: " + formatNumber(division.awareness, 3) + "<br>" +
"Popularity: " + formatNumber(division.popularity, 3) + "<br><br>" +
"Revenue: " + numeral(division.lastCycleRevenue.toNumber()).format("$0.000a") + " / s<br>" +
"Expenses: " + numeral(division.lastCycleExpenses.toNumber()).format("$0.000a") + " /s<br>" +
"Profit: " + profitStr + " / s<br><br>" +
@ -3748,27 +3992,28 @@ Corporation.prototype.updateDivisionContent = function(division) {
} else {
industryEmployeeHireButton.className = "a-link-button";
}
var employeeList = document.getElementById("cmpy-mgmt-employee-ul");
if (employeeList && office instanceof OfficeSpace) {
if (!empManualAssignmentModeActive) {
//Calculate average morale, happiness, and energy
var totalMorale = 0, totalHappiness = 0, totalEnergy = 0,
avgMorale = 0, avgHappiness = 0, avgEnergy = 0;
for (var i = 0; i < office.employees.length; ++i) {
(function(company) {
var emp = office.employees[i];
var panel = document.getElementById("cmpy-mgmt-employee-" + emp.name + "-panel");
if (panel == null) {
var li = createAccordionElement({
id:"cmpy-mgmt-employee-" + emp.name,
hdrText:emp.name,
});
panel = li.children[1];
emp.createUI(panel, company);
employeeList.appendChild(li);
return;
}
emp.updateUI(panel, company);
})(this);
totalMorale += office.employees[i].mor;
totalHappiness += office.employees[i].hap;
totalEnergy += office.employees[i].ene;
}
if (office.employees.length > 0) {
avgMorale = totalMorale / office.employees.length;
avgHappiness = totalHappiness / office.employees.length;
avgEnergy = totalEnergy / office.employees.length;
}
industryEmployeeInfo.innerHTML =
"Avg Employee Morale: " + formatNumber(avgMorale, 3) + "<br>" +
"Avg Employee Happiness: " + formatNumber(avgHappiness, 3) + "<br>" +
"Avg Employee Energy: " + formatNumber(avgEnergy, 3);
}
//Warehouse
var warehouse = division.warehouses[currentCityUi];
if (warehouse instanceof Warehouse) {
@ -3816,10 +4061,11 @@ Corporation.prototype.clearUI = function() {
industryOverviewPanel = null;
industryOverviewText = null;
industryEmployeePanel = null;
industryEmployeeText = null;
industryEmployeeHireButton = null;
industryEmployeeList = null;
industryEmployeePanel = null;
industryEmployeeText = null;
industryEmployeeHireButton = null;
industryEmployeeManagementUI = null;
industryEmployeeInfo = null;
industryOfficeUpgradeSizeButton = null;

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.34.1",
Version: "0.34.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
@ -10,8 +10,8 @@ let CONSTANTS = {
CorpFactionRepRequirement: 250000,
/* Base costs */
BaseCostFor1GBOfRamHome: 30000,
BaseCostFor1GBOfRamServer: 50000, //1 GB of RAM
BaseCostFor1GBOfRamHome: 32000,
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
BaseCostFor1GBOfRamHacknetNode: 30000,
BaseCostForHacknetNode: 1000,
@ -41,29 +41,23 @@ let CONSTANTS = {
//RAM Costs for different commands
ScriptWhileRamCost: 0.2,
ScriptForRamCost: 0.2,
ScriptIfRamCost: 0.1,
ScriptIfRamCost: 0.15,
ScriptHackRamCost: 0.1,
ScriptGrowRamCost: 0.15,
ScriptWeakenRamCost: 0.15,
ScriptScanRamCost: 0.2,
ScriptNukeRamCost: 0.05,
ScriptBrutesshRamCost: 0.05,
ScriptFtpcrackRamCost: 0.05,
ScriptRelaysmtpRamCost: 0.05,
ScriptHttpwormRamCost: 0.05,
ScriptSqlinjectRamCost: 0.05,
ScriptRunRamCost: 0.8,
ScriptExecRamCost: 1.1,
ScriptScpRamCost: 0.5,
ScriptPortProgramRamCost: 0.05,
ScriptRunRamCost: 1.0,
ScriptExecRamCost: 1.3,
ScriptScpRamCost: 0.6,
ScriptKillRamCost: 0.5, //Kill and killall
ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp()
ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel()
ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers()
ScriptGetServerCost: 0.1,
ScriptGetServerRamCost: 0.1,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptOperatorRamCost: 0.01,
ScriptPurchaseHacknetRamCost: 1.5,
ScriptHacknetNodesRamCost: 1.0, //Base cost for accessing hacknet nodes array
ScriptHNUpgLevelRamCost: 0.4,
@ -71,7 +65,7 @@ let CONSTANTS = {
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
ScriptPurchaseServerRamCost: 2.0,
ScriptPurchaseServerRamCost: 2.25,
ScriptRoundRamCost: 0.05,
ScriptReadWriteRamCost: 1.0,
ScriptArbScriptRamCost: 1.0, //Functions that apply to all scripts regardless of args
@ -613,8 +607,8 @@ let CONSTANTS = {
"<i><u>serverExists(hostname/ip)</u></i><br>Returns a boolean denoting whether or not the specified server exists. The argument " +
"must be a string with the hostname or IP of the target server.<br><br>" +
"<i><u>fileExists(filename, [hostname/ip])</u></i><br> Returns a boolean (true or false) indicating whether the specified file exists on a server. " +
"The first argument must be a string with the name of the file. A file can either be a script, program, or literature file. A script name is case-sensitive, but a " +
"program/literature file is not. For example, fileExists('brutessh.exe') will work fine, even though the actual program is named BruteSSH.exe. <br><br> " +
"The first argument must be a string with the filename. A file can either be a script, program, literature file, or a text file. The filename for a script is case-sensitive, but " +
"for the other files it is not. For example, fileExists('brutessh.exe') will work fine, even though the actual program is named BruteSSH.exe. <br><br> " +
"The second argument is a string with the hostname or IP of the server on which to search for the program. This second argument is optional. " +
"If it is omitted, then the function will search through the current server (the server running the script that calls this function) for the file. <br> " +
"Example: fileExists('foo.script', 'foodnstuff');<br>" +
@ -667,6 +661,8 @@ let CONSTANTS = {
"<i><u>clear(port/fn)</u></i><br>This function is used to clear a Netscript Port or a text file.<br><br>" +
"It takes a single argument. If this argument is a number between 1 and 10, then it specifies a port and will clear it (deleting all data from it). " +
"If the argument is a string, then it specifies the name of a text file (.txt) and will clear the text file so that it is empty.<br><br>" +
"<i><u>rm(fn)</u></i><br>This function is used to remove a file. It takes a string with the filename as the argument. Returns " +
"true if it successfully deletes the given file, and false otherwise. This function works for every file type except message files (.msg).<br><br>" +
"<i><u>scriptRunning(scriptname, hostname/ip)</u></i><br>Returns a boolean indicating whether any instance of the specified script is running " +
"on a server, regardless of its arguments. This is different than the isRunning() function because it does not " +
"try to identify a specific instance of a running script by its arguments.<br><br>" +
@ -1118,6 +1114,30 @@ let CONSTANTS = {
LatestUpdate:
"v0.34.2<br>" +
"-Corporation Management Changes:<br>" +
"---Added advertising mechanics<br>" +
"---Added Industry-specific purchases<br>" +
"---Re-designed employee management UI<br>" +
"---Rebalancing: Made many upgrades/purchases cheaper. Receive more money from investors in early stage. Company valuation is higher after going public<br>" +
"---Multiple bug fixes<br>" +
"-Added rm() Netscript function<br>" +
"-Updated the way script RAM usage is calculated. Now, a function only increases RAM usage the " +
"first time it is called. i.e. even if you call hack() multiple times in a script, it only counts " +
"against RAM usage once. The same change applies for while/for loops and if conditionals.<br>" +
"-The RAM cost of the following were increased:<br>" +
"---If statements: increased by 0.05GB<br>" +
"---run() and exec(): increased by 0.2GB<br>" +
"---scp(): increased by 0.1GB<br>" +
"---purchaseServer(): increased by 0.25GB<br>" +
"-Note: You may need to re-save all of your scripts in order to re-calculate their RAM usages. Otherwise, " +
"it should automatically be re-calculated when you reset/prestige<br>" +
"-The cost to upgrade your home computer's RAM has been increased (both the base cost and the exponential upgrade multiplier)<br>" +
"-The cost of purchasing a server was increased by 10% (it is now $55k per RAM)<br>" +
"-Bug fix: (Hopefully) removed an exploit where you could avoid RAM usage for Netscript function calls " +
"by assigning functions to a variable (foo = hack(); foo('helios');)<br>" +
"-Bug fix: (Hopefully) removed an exploit where you could run arbitrary Javascript code using the constructor() " +
"method<br>" +
"-Thanks to Github user mateon1 and Reddit users havoc_mayhem and spaceglace for notifying me of the above exploits<br>" +
"-The fileExists() Netscript function now works on text files (.txt). Thanks to Github user devoidfury for this<br>"
}

@ -39,6 +39,21 @@ function initLiterature() {
"Netscript command to copy your scripts onto these servers and then run them.";
Literatures[fn] = new Literature(title, fn, txt);
title = "The Complete Handbook for Creating a Successful Corporation";
fn = "corporation-management-handbook.lit";
txt = "This is a brief collection of tips/pointers on how to successfully start and manage a Corporation.<br><br>" +
"-Purchasing Hardware, Robots, AI Cores, and Real Estate can potentially increase your production. " +
"The effects of these depend on what industry you are in.<br><br>" +
"-In order to optimize your production, you will need a good balance of Operators, Managers, and Engineers<br><br>" +
"-Different employees excel in different jobs. For example, the highly intelligent employees will probably do best " +
"if they are assigned to do Engineering work or Research & Development.<br><br>" +
"-If your employees have low morale, energy, or happiness, their production will greatly suffer.<br><br>" +
"-Tech is important, but don't neglect sales! Having several Businessmen can boost your sales and your bottom line.<br><br>" +
"-Don't forget to advertise your company. You won't have any business if nobody knows you.<br><br>" +
"-Having company awareness is great, but what's really important is your company's popularity. Try to keep " +
"your popularity as high as possible to see the biggest benefit for your sales<br><br>";
Literatures[fn] = new Literature(title, fn, txt);
title = "A Green Tomorrow";
fn = "A-Green-Tomorrow.lit";
txt = "Starting a few decades ago, there was a massive global movement towards the generation of renewable energy in an effort to " +

@ -1768,7 +1768,7 @@ function initLocationButtons() {
//Calculate cost
//Have cost increase by some percentage each time RAM has been upgraded
var cost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHome;
var mult = Math.pow(1.55, numUpgrades);
var mult = Math.pow(1.58, numUpgrades);
cost = cost * mult;
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();

@ -58,6 +58,11 @@ function evaluate(exp, workerScript) {
return Promise.resolve(exp.value);
break;
case "Identifier":
//Javascript constructor() method can be used as an exploit to run arbitrary code
if (exp.name == "constructor") {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it."));
}
if (!(exp.name in env.vars)){
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.name + " not defined"));
}
@ -177,9 +182,11 @@ function evaluate(exp, workerScript) {
} else {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression"));
}
});
} else {
if (exp.property.name === "constructor") {
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it."));
}
try {
return Promise.resolve(object[exp.property.name])
} catch (e) {

File diff suppressed because it is too large Load Diff

@ -25,9 +25,10 @@ function WorkerScript(runningScriptObj) {
this.scriptRef = runningScriptObj;
this.errorMessage = "";
this.args = runningScriptObj.args;
//this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay())
this.delay = null;
this.fnWorker = null; //Workerscript for a function call
this.checkingRam = false;
this.loadedFns = {}; //Stores names of fns that are "loaded" by this script, thus using RAM
}
//Returns the server on which the workerScript is running
@ -65,7 +66,6 @@ function runScriptsLoop() {
//items fucks up the indexing
for (var i = workerScripts.length - 1; i >= 0; i--) {
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == true) {
console.log("Deleting script: " + workerScripts[i].name);
//Delete script from the runningScripts array on its host serverIp
var ip = workerScripts[i].serverIp;
var name = workerScripts[i].name;
@ -95,7 +95,7 @@ function runScriptsLoop() {
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
try {
var ast = parse(workerScripts[i].code);
//console.log(ast);
console.log(ast);
} catch (e) {
console.log("Error parsing script: " + workerScripts[i].name);
dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":<br>" + e);
@ -113,7 +113,6 @@ function runScriptsLoop() {
w.env.stopFlag = true;
w.scriptRef.log("Script finished running");
}).catch(function(w) {
console.log(w);
if (w instanceof Error) {
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
console.log("ERROR: Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());

@ -18,16 +18,18 @@ import {Engine} from "./engine.js";
import {iTutorialSteps, iTutorialNextStep,
iTutorialIsRunning, currITutorialStep} from "./InteractiveTutorial.js";
import {NetscriptFunctions} from "./NetscriptFunctions.js";
import {addWorkerScript, killWorkerScript} from "./NetscriptWorker.js";
import {addWorkerScript, killWorkerScript,
WorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js";
import {AllServers, processSingleServerGrowth} from "./Server.js";
import {Settings} from "./Settings.js";
import {post} from "./Terminal.js";
import {parse, Node} from "../utils/acorn.js";
import {dialogBoxCreate} from "../utils/DialogBox.js";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js";
import {compareArrays} from "../utils/HelperFunctions.js";
import {compareArrays, createElement} from "../utils/HelperFunctions.js";
import {formatNumber, numOccurrences,
numNetscriptOperators} from "../utils/StringHelperFunctions.js";
@ -37,15 +39,53 @@ var keybindings = {
emacs: "ace/keyboard/emacs",
};
var scriptEditorRamCheck = null, scriptEditorRamText = null;
function scriptEditorInit() {
//Initialize save and close button
var closeButton = document.getElementById("script-editor-save-and-close-button");
closeButton.addEventListener("click", function() {
saveAndCloseScriptEditor();
return false;
//Create buttons at the bottom of script editor
var wrapper = document.getElementById("script-editor-buttons-wrapper");
if (wrapper == null) {
console.log("Error finding 'script-editor-buttons-wrapper'");
return;
}
var closeButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"Save & Close (Ctrl + b)",
clickListener:()=>{
saveAndCloseScriptEditor();
return false;
}
});
scriptEditorRamText = createElement("p", {
display:"inline-block", margin:"10px", id:"script-editor-status-text"
});
var checkboxLabel = createElement("label", {
for:"script-editor-ram-check", margin:"4px", marginTop: "8px",
innerText:"Dynamic RAM Usage Checker", color:"white",
tooltip:"Enable/Disable the dynamic RAM Usage display. You may " +
"want to disable it for very long scripts because there may be " +
"performance issues"
});
scriptEditorRamCheck = createElement("input", {
type:"checkbox", name:"script-editor-ram-check", id:"script-editor-ram-check",
margin:"4px", marginTop: "8px",
});
scriptEditorRamCheck.checked = true;
var documentationButton = createElement("a", {
display:"inline-block", class:"a-link-button", innerText:"Netscript Documentation",
href:"https://bitburner.wikia.com/wiki/Netscript",
target:"_blank"
});
wrapper.appendChild(closeButton);
wrapper.appendChild(scriptEditorRamText);
wrapper.appendChild(scriptEditorRamCheck);
wrapper.appendChild(checkboxLabel);
wrapper.appendChild(documentationButton);
//Initialize ACE Script editor
var editor = ace.edit('javascript-editor');
editor.getSession().setMode('ace/mode/netscript');
@ -127,14 +167,19 @@ function scriptEditorInit() {
}
document.addEventListener("DOMContentLoaded", scriptEditorInit, false);
//Updates line number and RAM usage in script
//Updates RAM usage in script
function updateScriptEditorContent() {
if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked) {
scriptEditorRamText.innerText = "N/A";
return;
}
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
var codeCopy = code.repeat(1);
var ramUsage = calculateRamUsage(codeCopy);
document.getElementById("script-editor-status-text").innerText =
"RAM: " + formatNumber(ramUsage, 2).toString() + "GB";
if (ramUsage !== -1) {
scriptEditorRamText.innerText = "RAM: " + formatNumber(ramUsage, 2).toString() + "GB";
}
}
//Define key commands in script editor (ctrl o to save + close, etc.)
@ -233,154 +278,87 @@ Script.prototype.saveScript = function() {
//Updates how much RAM the script uses when it is running.
Script.prototype.updateRamUsage = function() {
var codeCopy = this.code.repeat(1);
this.ramUsage = calculateRamUsage(codeCopy);
console.log("ram usage: " + this.ramUsage);
if (isNaN(this.ramUsage)) {
dialogBoxCreate("ERROR in calculating ram usage. This is a bug, please report to game develoepr");
var res = calculateRamUsage(codeCopy);
if (res !== -1) {
this.ramUsage = res;
}
}
function calculateRamUsage(codeCopy) {
codeCopy = codeCopy.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1'); //Delete comments
codeCopy = codeCopy.replace(/\s/g,''); //Remove all whitespace
var baseRam = 1.4;
var whileCount = numOccurrences(codeCopy, "while(");
var forCount = numOccurrences(codeCopy, "for(");
var ifCount = numOccurrences(codeCopy, "if(");
var hackCount = numOccurrences(codeCopy, "hack(");
var growCount = numOccurrences(codeCopy, "grow(");
var weakenCount = numOccurrences(codeCopy, "weaken(");
var scanCount = numOccurrences(codeCopy, "scan(");
var nukeCount = numOccurrences(codeCopy, "nuke(");
var brutesshCount = numOccurrences(codeCopy, "brutessh(");
var ftpcrackCount = numOccurrences(codeCopy, "ftpcrack(");
var relaysmtpCount = numOccurrences(codeCopy, "relaysmtp(");
var httpwormCount = numOccurrences(codeCopy, "httpworm(");
var sqlinjectCount = numOccurrences(codeCopy, "sqlinject(");
var runCount = numOccurrences(codeCopy, "run(");
var execCount = numOccurrences(codeCopy, "exec(");
var killCount = numOccurrences(codeCopy, "kill(") + numOccurrences(codeCopy, "killall(") +
numOccurrences(codeCopy, "exit(");
var scpCount = numOccurrences(codeCopy, "scp(");
var hasRootAccessCount = numOccurrences(codeCopy, "hasRootAccess(");
var getHostnameCount = numOccurrences(codeCopy, "getHostname(") +
numOccurrences(codeCopy, "getIp(");
var getHackingLevelCount = numOccurrences(codeCopy, "getHackingLevel(");
var getMultipliersCount = numOccurrences(codeCopy, "getHackingMultipliers(") +
numOccurrences(codeCopy, "getBitNodeMultipliers(");
var getServerCount = numOccurrences(codeCopy, "getServerMoneyAvailable(") +
numOccurrences(codeCopy, "getServerMaxMoney(") +
numOccurrences(codeCopy, "getServerSecurityLevel(") +
numOccurrences(codeCopy, "getServerBaseSecurityLevel(") +
numOccurrences(codeCopy, "getServerMinSecurityLevel(") +
numOccurrences(codeCopy, "getServerGrowth(") +
numOccurrences(codeCopy, "getServerRequiredHackingLevel(") +
numOccurrences(codeCopy, "getServerNumPortsRequired(") +
numOccurrences(codeCopy, "getServerRam(") +
numOccurrences(codeCopy, "serverExists(");
var fileExistsCount = numOccurrences(codeCopy, "fileExists(");
var isRunningCount = numOccurrences(codeCopy, "isRunning(");
var purchaseHacknetCount = numOccurrences(codeCopy, "purchaseHacknetNode(");
var hacknetnodesArrayCount = numOccurrences(codeCopy, "hacknetnodes[");
var hnUpgLevelCount = numOccurrences(codeCopy, ".upgradeLevel(");
var hnUpgRamCount = numOccurrences(codeCopy, ".upgradeRam()");
var hnUpgCoreCount = numOccurrences(codeCopy, ".upgradeCore()");
var scriptGetStockCount = numOccurrences(codeCopy, "getStockPrice(") +
numOccurrences(codeCopy, "getStockPosition(");
var scriptBuySellStockCount = numOccurrences(codeCopy, "buyStock(") +
numOccurrences(codeCopy, "sellStock(") +
numOccurrences(codeCopy, "shortStock(") +
numOccurrences(codeCopy, "sellShort(") +
numOccurrences(codeCopy, "placeOrder(") +
numOccurrences(codeCopy, "cancelOrder(");
var scriptPurchaseServerCount = numOccurrences(codeCopy, "purchaseServer(") +
numOccurrences(codeCopy, "deleteServer(") +
numOccurrences(codeCopy, "getPurchasedServers(");
var scriptRoundCount = numOccurrences(codeCopy, "round(");
var scriptWriteCount = numOccurrences(codeCopy, "write(") + numOccurrences(codeCopy, "clear(");
var scriptReadCount = numOccurrences(codeCopy, "read(");
var arbScriptCount = numOccurrences(codeCopy, "scriptRunning(") +
numOccurrences(codeCopy, "scriptKill(");
var getScriptCount = numOccurrences(codeCopy, "getScriptRam(") +
numOccurrences(codeCopy, "getScriptIncome(") +
numOccurrences(codeCopy, "getScriptExpGain(");
var getHackTimeCount = numOccurrences(codeCopy, "getHackTime(") +
numOccurrences(codeCopy, "getGrowTime(") +
numOccurrences(codeCopy, "getWeakenTime(") +
numOccurrences(codeCopy, "getTimeSinceLastAug(");
var singFn1Count = numOccurrences(codeCopy, "universityCourse(") +
numOccurrences(codeCopy, "gymWorkout(") +
numOccurrences(codeCopy, "travelToCity(") +
numOccurrences(codeCopy, "purchaseTor(") +
numOccurrences(codeCopy, "purchaseProgram(") +
numOccurrences(codeCopy, "getStats(") +
numOccurrences(codeCopy, "isBusy(");
var singFn2Count = numOccurrences(codeCopy, "upgradeHomeRam(") +
numOccurrences(codeCopy, "getUpgradeHomeRamCost(") +
numOccurrences(codeCopy, "workForCompany(") +
numOccurrences(codeCopy, "applyToCompany(") +
numOccurrences(codeCopy, "getCompanyRep(") +
numOccurrences(codeCopy, "checkFactionInvitations(") +
numOccurrences(codeCopy, "joinFaction(") +
numOccurrences(codeCopy, "workForFaction(") +
numOccurrences(codeCopy, "getFactionRep(");
var singFn3Count = numOccurrences(codeCopy, "createProgram(") +
numOccurrences(codeCopy, "commitCrime(") +
numOccurrences(codeCopy, "getCrimeChance(") +
numOccurrences(codeCopy, "getOwnedAugmentations(") +
numOccurrences(codeCopy, "getAugmentationsFromFaction(") +
numOccurrences(codeCopy, "getAugmentationCost(") +
numOccurrences(codeCopy, "purchaseAugmentation(") +
numOccurrences(codeCopy, "installAugmentations(");
//Create a temporary/mock WorkerScript and an AST from the code
var workerScript = new WorkerScript({
filename:"foo",
scriptRef: {code:""},
args:[]
});
workerScript.checkingRam = true; //Netscript functions will return RAM usage
if (Player.bitNodeN != 4) {
singFn1Count *= 10;
singFn2Count *= 10;
singFn3Count *= 10;
try {
var ast = parse(codeCopy);
} catch(e) {
console.log("returning -1 bc parsing error: " + e.toString());
return -1;
}
return baseRam +
((whileCount * CONSTANTS.ScriptWhileRamCost) +
(forCount * CONSTANTS.ScriptForRamCost) +
(ifCount * CONSTANTS.ScriptIfRamCost) +
(hackCount * CONSTANTS.ScriptHackRamCost) +
(growCount * CONSTANTS.ScriptGrowRamCost) +
(weakenCount * CONSTANTS.ScriptWeakenRamCost) +
(scanCount * CONSTANTS.ScriptScanRamCost) +
(nukeCount * CONSTANTS.ScriptNukeRamCost) +
(brutesshCount * CONSTANTS.ScriptBrutesshRamCost) +
(ftpcrackCount * CONSTANTS.ScriptFtpcrackRamCost) +
(relaysmtpCount * CONSTANTS.ScriptRelaysmtpRamCost) +
(httpwormCount * CONSTANTS.ScriptHttpwormRamCost) +
(sqlinjectCount * CONSTANTS.ScriptSqlinjectRamCost) +
(runCount * CONSTANTS.ScriptRunRamCost) +
(execCount * CONSTANTS.ScriptExecRamCost) +
(killCount * CONSTANTS.ScriptKillRamCost) +
(scpCount * CONSTANTS.ScriptScpRamCost) +
(hasRootAccessCount * CONSTANTS.ScriptHasRootAccessRamCost) +
(getHostnameCount * CONSTANTS.ScriptGetHostnameRamCost) +
(getHackingLevelCount * CONSTANTS.ScriptGetHackingLevelRamCost) +
(getMultipliersCount * CONSTANTS.ScriptGetMultipliersRamCost) +
(getServerCount * CONSTANTS.ScriptGetServerCost) +
(fileExistsCount * CONSTANTS.ScriptFileExistsRamCost) +
(isRunningCount * CONSTANTS.ScriptIsRunningRamCost) +
(purchaseHacknetCount * CONSTANTS.ScriptPurchaseHacknetRamCost) +
(hacknetnodesArrayCount * CONSTANTS.ScriptHacknetNodesRamCost) +
(hnUpgLevelCount * CONSTANTS.ScriptHNUpgLevelRamCost) +
(hnUpgRamCount * CONSTANTS.ScriptHNUpgRamRamCost) +
(hnUpgCoreCount * CONSTANTS.ScriptHNUpgCoreRamCost) +
(scriptGetStockCount * CONSTANTS.ScriptGetStockRamCost) +
(scriptBuySellStockCount * CONSTANTS.ScriptBuySellStockRamCost) +
(scriptPurchaseServerCount * CONSTANTS.ScriptPurchaseServerRamCost) +
(scriptRoundCount * CONSTANTS.ScriptRoundRamCost) +
(scriptWriteCount * CONSTANTS.ScriptReadWriteRamCost) +
(scriptReadCount * CONSTANTS.ScriptReadWriteRamCost) +
(arbScriptCount * CONSTANTS.ScriptArbScriptRamCost) +
(getScriptCount * CONSTANTS.ScriptGetScriptRamCost) +
(getHackTimeCount * CONSTANTS.ScriptGetHackTimeRamCost) +
(singFn1Count * CONSTANTS.ScriptSingularityFn1RamCost) +
(singFn2Count * CONSTANTS.ScriptSingularityFn2RamCost) +
(singFn3Count * CONSTANTS.ScriptSingularityFn3RamCost));
//Search through AST, scanning for any 'Identifier' nodes for functions, or While/For/If nodes
var queue = [], ramUsage = 1.4;
var whileUsed = false, forUsed = false, ifUsed = false;
queue.push(ast);
while (queue.length != 0) {
var exp = queue.shift();
switch (exp.type) {
case "BlockStatement":
case "Program":
for (var i = 0; i < exp.body.length; ++i) {
if (exp.body[i] instanceof Node) {
queue.push(exp.body[i]);
}
}
break;
case "WhileStatement":
if (!whileUsed) {
ramUsage += CONSTANTS.ScriptWhileRamCost;
whileUsed = true;
}
break;
case "ForStatement":
if (!forUsed) {
ramUsage += CONSTANTS.ScriptForRamCost;
forUsed = true;
}
break;
case "IfStatement":
if (!ifUsed) {
ramUsage += CONSTANTS.ScriptIfRamCost;
ifUsed = true;
}
break;
case "Identifier":
if (exp.name in workerScript.env.vars) {
var func = workerScript.env.get(exp.name);
if (typeof func === "function") {
try {
var res = func.apply(null, []);
if (!isNaN(res)) {ramUsage += res;}
} catch(e) {
console.log("ERROR applying function: " + e);
}
}
}
break;
default:
break;
}
for (var prop in exp) {
if (exp.hasOwnProperty(prop)) {
if (exp[prop] instanceof Node) {
queue.push(exp[prop]);
}
}
}
}
return ramUsage;
}
Script.prototype.toJSON = function() {

@ -32,15 +32,14 @@ import {containsAllStrings, longestCommonStart,
formatNumber, isString} from "../utils/StringHelperFunctions.js";
import {addOffset, printArray} from "../utils/HelperFunctions.js";
import {logBoxCreate} from "../utils/LogBox.js";
import {yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox.js";
/* Write text to terminal */
//If replace is true then spaces are replaced with "&nbsp;"
function post(input, replace=true) {
if (replace) {
$("#terminal-input").before('<tr class="posted"><td class="terminal-line" style="color: var(--my-font-color); background-color: var(--my-background-color); white-space:pre;">' + input.replace( / /g, "&nbsp;" ) + '</td></tr>');
} else {
$("#terminal-input").before('<tr class="posted"><td class="terminal-line" style="color: var(--my-font-color); background-color: var(--my-background-color);">' + input + '</td></tr>');
}
function post(input) {
$("#terminal-input").before('<tr class="posted"><td class="terminal-line" style="color: var(--my-font-color); background-color: var(--my-background-color); white-space:pre-wrap;">' + input + '</td></tr>');
updateTerminalScroll();
}
@ -744,7 +743,7 @@ let Terminal = {
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
executeDarkwebTerminalCommand(commandArray);
} else {
post("You need to be connected to the Dark Web to use the buy command");
post("You need to be able to connect to the Dark Web to use the buy command. (Maybe there's a TOR router you can buy somewhere)");
}
break;
case "cat":
@ -1636,7 +1635,21 @@ let Terminal = {
post("Agility: " + Player.agility + " / 1500");
break;
case Programs.BitFlume:
hackWorldDaemon(Player.bitNodeN, true);
var yesBtn = yesNoBoxGetYesButton(),
noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Travel to BitNode Nexus";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function() {
hackWorldDaemon(Player.bitNodeN, true);
return yesNoBoxClose();
});
noBtn.addEventListener("click", function() {
return yesNoBoxClose();
});
yesNoBoxCreate("WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.<br><br>" +
"Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode " +
"and select a new one.");
break;
default:
post("Invalid executable. Cannot be run");

@ -75,4 +75,15 @@ function createTextFile(fn, txt, server) {
return file;
}
function deleteTextFile(fn, server) {
if (!fn.endsWith(".txt")) {fn += ".txt";}
for (var i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === fn) {
server.textFiles.splice(i, 1);
return true;
}
}
return false;
}
export {TextFile, getTextFile, createTextFile};

@ -937,7 +937,6 @@ let Engine = {
messages: 150,
stockTick: 30, //Update stock prices
sCr: 1500,
updateScriptEditorDisplay: 5,
},
decrementAllCounters: function(numCycles = 1) {
@ -997,6 +996,8 @@ let Engine = {
if (Engine.Counters.updateDisplaysLong <= 0) {
if (Engine.currentPage === Engine.Page.Gang) {
updateGangContent();
} else if (Engine.currentPage === Engine.Page.ScriptEditor) {
updateScriptEditorContent();
}
Engine.Counters.updateDisplaysLong = 15;
}
@ -1058,13 +1059,6 @@ let Engine = {
}
Engine.Counters.sCr = 1500;
}
if (Engine.Counters.updateScriptEditorDisplay <= 0) {
if (Engine.currentPage == Engine.Page.ScriptEditor) {
updateScriptEditorContent();
}
Engine.Counters.updateScriptEditorDisplay = 5;
}
},
/* Calculates the hack progress for a manual (non-scripted) hack and updates the progress bar/time accordingly */

@ -71,6 +71,7 @@ function createElement(type, params) {
if (params.visibility) {el.style.visibility = params.visibility;}
if (params.margin) {el.style.margin = params.margin;}
if (params.marginLeft) {el.style.marginLeft = params.marginLeft;}
if (params.marginTop) {el.style.marginTop = params.marginTop;}
if (params.padding) {el.style.padding = params.padding;}
if (params.color) {el.style.color = params.color;}
if (params.border) {el.style.border = params.border;}
@ -94,6 +95,8 @@ function createElement(type, params) {
innerHTML:params.tooltip
}));
}
if (params.href) {el.href = params.href;}
if (params.target) {el.target = params.target;}
if (params.clickListener) {
el.addEventListener("click", params.clickListener);
}