FIxed merge conflicts with dev and also did some work on Gang UI

This commit is contained in:
danielyxie 2018-10-10 18:13:31 -05:00
commit fcab079b32
30 changed files with 1439 additions and 1065 deletions

File diff suppressed because one or more lines are too long

@ -11,6 +11,7 @@
/* Remove default <button> styling */
button {
border: none;
background-color: transparent;
}
.a-link-button,
@ -81,3 +82,26 @@ button {
pointer-events: none;
}
}
/**
* This is a button that is meant to be used on accordions (accordion-header and accordion-panel classes)
* It has a black background so it does not clash with the default accordion coloring
*/
.accordion-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
font-size: $defaultFontSize;
font-weight: bold;
margin: 4px;
padding: 4px;
background-color: #000;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
}

27
css/gang.scss Normal file

@ -0,0 +1,27 @@
@import "mixins";
@import "theme";
/**
* Styling for the Gang mechanic UI (BitNode-2)
*/
#gang-container {
position: fixed;
padding: 6px;
select {
background-color: black;
color: white;
}
}
#gang-management-subpage > p {
padding: 4px;
}
.gang-member-info-div {
background-color: #555;
display: inline;
float: left;
width: 30%;
}

@ -53,26 +53,19 @@
}
}
/*
#interactive-tutorial-exit {
float: left;
}
#interactive-tutorial-back {
margin-right: 20%;
float: right;
}
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0;
padding: 4px;
}
#interactive-tutorial-back {
float: left;
padding: 4px;
}
#interactive-tutorial-next {
float: right;
padding: 4px;
}

@ -248,25 +248,6 @@
}
}
.active-scripts-button {
@include borderRadius(12px);
@include boxShadow(1px 1px 3px #000);
color: #aaa;
font-size: $defaultFontSize;
font-weight: bold;
margin: 4px;
padding: 4px;
background-color: #000;
&:hover,
&:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
}
/* Hacknet Nodes */
#hacknet-nodes-container {
position: fixed;
@ -623,18 +604,3 @@
margin: 2px;
padding: 0;
}
/* Gang */
#gang-container {
position: fixed;
padding: 6px;
}
#gang-management-subpage > p {
padding: 4px;
}
.gang-member-info-div {
float: left;
background-color: #555;
}

@ -159,6 +159,7 @@ a:visited {
.help-tip {
content: '?';
padding: 1px;
margin-top: 5px;
margin-left: 3px;
color: #fff;
border: 1px solid #fff;

1064
dist/engine.bundle.js vendored

File diff suppressed because it is too large Load Diff

96
dist/engine.css vendored

@ -146,6 +146,7 @@ a:visited {
.help-tip {
content: '?';
padding: 1px;
margin-top: 5px;
margin-left: 3px;
color: #fff;
border: 1px solid #fff;
@ -482,7 +483,8 @@ a:visited {
*/
/* Remove default <button> styling */
button {
border: none; }
border: none;
background-color: transparent; }
.a-link-button,
.std-button {
@ -545,6 +547,28 @@ button {
.std-button-bought:active {
pointer-events: none; }
/**
* This is a button that is meant to be used on accordions (accordion-header and accordion-panel classes)
* It has a black background so it does not clash with the default accordion coloring
*/
.accordion-button {
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #aaa;
font-size: 16px;
font-weight: bold;
margin: 4px;
padding: 4px;
background-color: #000; }
.accordion-button:hover, .accordion-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
/* COLORS */
/* Attributes */
/**
@ -920,24 +944,6 @@ button {
color: #fff;
margin-left: 5%; }
.active-scripts-button {
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
border-radius: 12px;
-webkit-box-shadow: 1px 1px 3px #000;
-moz-box-shadow: 1px 1px 3px #000;
box-shadow: 1px 1px 3px #000;
color: #aaa;
font-size: 16px;
font-weight: bold;
margin: 4px;
padding: 4px;
background-color: #000; }
.active-scripts-button:hover, .active-scripts-button:focus {
color: #fff;
text-decoration: none;
cursor: pointer; }
/* Hacknet Nodes */
#hacknet-nodes-container {
position: fixed;
@ -1226,18 +1232,6 @@ button {
margin: 2px;
padding: 0; }
/* Gang */
#gang-container {
position: fixed;
padding: 6px; }
#gang-management-subpage > p {
padding: 4px; }
.gang-member-info-div {
float: left;
background-color: #555; }
/* COLORS */
/* Attributes */
/* Both Work in progress and BitNode stuff */
@ -1530,26 +1524,19 @@ button {
text-decoration: none;
cursor: pointer; }
/*
#interactive-tutorial-exit {
float: left;
}
#interactive-tutorial-back {
margin-right: 20%;
float: right;
}
*/
#interactive-tutorial-exit {
position: absolute;
bottom: 0;
left: 0; }
left: 0;
padding: 4px; }
#interactive-tutorial-back {
float: left; }
float: left;
padding: 4px; }
#interactive-tutorial-next {
float: right; }
float: right;
padding: 4px; }
/* COLORS */
/* Attributes */
@ -2046,5 +2033,26 @@ button {
-webkit-hyphens: auto;
-moz-hyphens: auto; }
/* COLORS */
/* Attributes */
/**
* Styling for the Gang mechanic UI (BitNode-2)
*/
#gang-container {
position: fixed;
padding: 6px; }
#gang-container select {
background-color: black;
color: white; }
#gang-management-subpage > p {
padding: 4px; }
.gang-member-info-div {
background-color: #555;
display: inline;
float: left;
width: 30%; }
/*# sourceMappingURL=engine.css.map*/

750
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -298,4 +298,4 @@ template {
display: none; }
/*# sourceMappingURL=0.css.map*/
/*# sourceMappingURL=vendor.css.map*/

@ -3,6 +3,13 @@
Changelog
=========
v0.40.5 - 10/09/2018
--------------------
* Added codingcontract.getContractType() Netscript function
* Bug Fix: codingcontract.getData() Netscript function now returns arrays by value rather than reference
* Bug Fix: Decreased highest possible data value for 'Find Largest Prime Factor' Coding Contract (to avoid hangs when solving it)
* Bug Fix: Fixed a bug that caused game to freeze during Coding Contract generation
v0.40.4 - 9/29/2018
-------------------
* Added new Coding Contracts mechanic. Solve programming problems to earn rewards

@ -31,6 +31,20 @@ attempt
:returns: Boolean indicating whether the solution was correct
getContractType
---------------
.. js:function:: getContractType(fn[, hostname/ip=current ip])
:param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided
Returns a name describing the type of problem posed by the Coding Contract.
(e.g. Find Largest Prime Factor, Total Ways to Sum, etc.)
:returns: A string with the contract's problem type
getDescription
--------------

@ -25,7 +25,7 @@
ga('create', 'UA-100157497-1', 'auto');
ga('send', 'pageview');
</script>
<link rel="shortcut icon" href="favicon.ico"><link href="0.css" rel="stylesheet"><link href="dist/engine.css" rel="stylesheet"></head>
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="dist/engine.css" rel="stylesheet"></head>
<body>
<div id="entire-game-container" style="visibility:hidden;">
<div id="mainmenu-container">
@ -723,8 +723,8 @@
<!-- Log Box -->
<div id="log-box-container">
<div id="log-box-content">
<span id="log-box-close" class="popup-box-button"> Close </span>
<span id="log-box-kill-script" class="popup-box-button">Kill Script</span>
<button id="log-box-close" class="popup-box-button"> Close </button>
<button id="log-box-kill-script" class="popup-box-button">Kill Script</button>
<p id="log-box-text-header"> </p>
<p id="log-box-text"> </p>
</div>
@ -734,8 +734,8 @@
<div id="yes-no-box-container" class="popup-box-container">
<div id="yes-no-box-content" class="popup-box-content">
<p id="yes-no-box-text"> </p>
<span id="yes-no-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-box-no" class="popup-box-button"> No </span>
<button id="yes-no-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -744,8 +744,8 @@
<div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
<button id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-text-input-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -758,8 +758,8 @@
Would you like to join? <br /> <br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<span id="faction-invitation-box-yes" class="popup-box-button"> Yes </span>
<span id="faction-invitation-box-no" class="popup-box-button"> No </span>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
<button id="faction-invitation-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -768,9 +768,9 @@
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p>
<span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br /><br />
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br /><br />
<select id="infiltration-faction-select"> </select> <br />
<span id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </span>
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
</div>
</div>
@ -783,7 +783,7 @@
<div id="work-in-progress-container" class="generic-fullscreen-container">
<p id="work-in-progress-text"> </p>
<span id="work-in-progress-cancel-button"> Cancel Work </span>
<button id="work-in-progress-cancel-button"> Cancel Work </button>
</div>
<!-- Red Pill Container -->
@ -798,9 +798,9 @@
<div id="interactive-tutorial-wrapper">
<div id="interactive-tutorial-container">
<p id="interactive-tutorial-text"> </p>
<span id="interactive-tutorial-exit"> Exit Tutorial </span>
<span id="interactive-tutorial-next"> Next </span>
<span id="interactive-tutorial-back"> Back </span>
<button id="interactive-tutorial-exit"> Exit Tutorial </button>
<button id="interactive-tutorial-next"> Next </button>
<button id="interactive-tutorial-back"> Back </button>
</div>
</div>
@ -839,8 +839,8 @@
</table>
</div>
<div class="character-quick-options">
<span id="character-overview-save-button">Save Game</span>
<span id="character-overview-options-button">Options</span>
<button id="character-overview-save-button">Save Game</button>
<button id="character-overview-options-button">Options</button>
</div>
</div>
</div>
@ -853,7 +853,7 @@
<!-- Game Options -->
<div id="game-options-container" class="popup-box-container">
<div id="game-options-content" class="game-options-box">
<span id="game-options-close-button">&times;</span>
<button id="game-options-close-button">&times;</button>
<h1> Game Options </h1>
<br />
<div id="game-options-left-panel">

@ -112,7 +112,8 @@ let NetscriptFunctions =
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime|" +
// Coding Contract API
"codingcontract|attempt|getData|getDescription|getNumTriesRemaining";
"codingcontract|attempt|getContractType|getData|getDescription|" +
"getNumTriesRemaining";
var NetscriptHighlightRules = function(options) {
var keywordMapper = this.createKeywordMapper({

@ -152,7 +152,7 @@ function addActiveScriptsItem(workerscript) {
panel.appendChild(createElement("br"));
panel.appendChild(createElement("span", {
innerText: "Log",
class: "active-scripts-button",
class: "accordion-button",
margin: "4px",
padding: "4px",
clickListener: () => {
@ -162,7 +162,7 @@ function addActiveScriptsItem(workerscript) {
}));
panel.appendChild(createElement("span", {
innerText: "Kill Script",
class: "active-scripts-button",
class: "accordion-button",
margin: "4px",
padding: "4px",
clickListener: () => {

@ -154,6 +154,10 @@ export class CodingContract {
return CodingContractTypes[this.type].numTries;
}
getType(): string {
return CodingContractTypes[this.type].name;
}
isSolution(solution: string): boolean {
return CodingContractTypes[this.type].solver(this.data, solution);
}

@ -1,5 +1,5 @@
let CONSTANTS = {
Version: "0.40.4",
Version: "0.40.5",
//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
@ -499,18 +499,10 @@ let CONSTANTS = {
LatestUpdate:
`
v0.40.4
* Added new Coding Contracts mechanic. Solve programming problems to earn rewards
* The write() and read() Netscript functions now work on scripts
* Added getStockSymbols() Netscript function to the TIX API (by InfraK)
* Added wget() Netscript function
* Added bladeburner.getActionRepGain() function to the Netscript Bladeburner API
* The getLevelUpgradeCost(), getRamUpgradeCost(), and getCoreUpgradeCost() functions in the Hacknet API now return Infinity if the node is at max level. See documentation
* It is now possible to use freely use angled bracket (<, >) and create DOM elements using tprint()
* The game's theme colors can now be set through the Terminal configuration (.fconf).
* You can now switch to the old left-hand main menu bar through the Terminal configuration (.fconf)
* Bug Fix: grow() percentage is no longer reported as Infinity when a server's money is grown from 0 to X
* Bug Fix: Infiltration popup now displays the correct amount of exp gained
v0.41.0
* b1t_flum3.exe now takes significantly less time to create
* Bug Fix: Fixed a bug that sometimes caused a blank black screen when destroying/resetting/switching BitNodes
* Bug Fix: Netscript calls that throw errors will now no longer cause the 'concurrent calls' error if they are caught in the script. i.e. try/catch should now work properly in scripts
`
}

@ -86,7 +86,7 @@ const Programs = {
level: 1,
tooltip:"This program creates a portal to the BitNode Nexus (allows you to restart and switch BitNodes)",
req: function() {return Player.sourceFiles.length > 0 && Player.hacking_skill >= 1},
time: CONSTANTS.MillisecondsPerFiveMinutes / 5,
time: CONSTANTS.MillisecondsPerFiveMinutes / 20,
}),
// special because you can't create it.
Flight: new Program("fl1ght.exe"),

@ -300,10 +300,11 @@ Gang.prototype.canRecruitMember = function() {
}
Gang.prototype.getRespectNeededToRecruitMember = function() {
// First 3 members are free
if (this.members.length < 3) { return 0; }
// First N gang members are free (can be recruited at 0 respect)
const numFreeMembers = 3;
if (this.members.length < numFreeMembers) { return 0; }
const i = this.members.length;
const i = this.members.length - (numFreeMembers - 1);
return Math.round(0.7 * Math.pow(i, 3) + 0.8 * Math.pow(i, 2));
}
@ -558,7 +559,7 @@ GangMember.fromJSON = function(value) {
Reviver.constructors.GangMember = GangMember;
//Defines tasks that Gang Members can work on
function GangMemberTask(name="", desc="",
function GangMemberTask(name="", desc="", isHacking=false, isCombat=false,
params={baseRespect: 0, baseWanted: 0, baseMoney: 0,
hackWeight: 0, strWeight: 0, defWeight: 0,
dexWeight: 0, agiWeight: 0, chaWeight: 0,
@ -566,11 +567,17 @@ function GangMemberTask(name="", desc="",
this.name = name;
this.desc = desc;
// Flags that describe whether this Task is applicable for Hacking/Combat gangs
this.isHacking = isHacking;
this.isCombat = isCombat;
// Base gain rates for respect/wanted/money
this.baseRespect = params.baseRespect ? params.baseRespect : 0;
this.baseWanted = params.baseWanted ? params.baseWanted : 0;
this.baseMoney = params.baseMoney ? params.baseMoney : 0;
//Weights must add up to 100
// Weighting for the effect that each stat has on the tasks effectiveness.
// Weights must add up to 100
this.hackWeight = params.hackWeight ? params.hackWeight : 0;
this.strWeight = params.strWeight ? params.strWeight : 0;
this.defWeight = params.defWeight ? params.defWeight : 0;
@ -578,7 +585,7 @@ function GangMemberTask(name="", desc="",
this.agiWeight = params.agiWeight ? params.agiWeight : 0;
this.chaWeight = params.chaWeight ? params.chaWeight : 0;
//1 - 100
// 1 - 100
this.difficulty = params.difficulty ? params.difficulty : 1;
}
@ -595,12 +602,12 @@ Reviver.constructors.GangMemberTask = GangMemberTask;
//TODO Human trafficking and an equivalent hacking crime
const GangMemberTasks = {};
function addGangMemberTask(name, desc, params) {
GangMemberTasks[name] = new GangMemberTask(name, desc, params);
function addGangMemberTask(name, desc, isHacking, isCombat, params) {
GangMemberTasks[name] = new GangMemberTask(name, desc, isHacking, isCombat, params);
}
gangMemberTasksMetadata.forEach((e) => {
addGangMemberTask(e.name, e.desc, e.params);
addGangMemberTask(e.name, e.desc, e.isHacking, e.isCombat, e.params);
});
function GangMemberUpgrade(name="", cost=0, type="w", mults={}) {
@ -1245,51 +1252,76 @@ function updateGangContent() {
//Takes in a GangMember object
function createGangMemberDisplayElement(memberObj) {
if (!gangContentCreated || !Player.inGang()) {return;}
var name = memberObj.name;
const name = memberObj.name;
var accordion = createAccordionElement({
id:name + "gang-member",
hdrText:name,
id: name + "gang-member",
hdrText: name,
});
var li = accordion[0];
var hdr = accordion[1];
var gangMemberDiv = accordion[2];
const li = accordion[0];
const hdr = accordion[1];
const gangMemberDiv = accordion[2];
//Gang member content divided into 3 panels:
//Stats Panel
var statsDiv = createElement("div", {
id: name + "gang-member-stats", class: "gang-member-info-div",
width:"30%", display:"inline"
// Gang member content divided into 3 panels:
// Panel 1 - Shows member's stats & Ascension stuff
const statsDiv = createElement("div", {
class: "gang-member-info-div",
id: name + "gang-member-stats",
});
var statsP = createElement("p", {
id:name + "gang-member-stats-text", display:"inline"
const statsP = createElement("p", {
id: name + "gang-member-stats-text", display: "inline"
});
const ascendButton = createElement("button", {
class: "accordion-button",
innerText: "Ascend",
clickListener: () => {
Player.gang.ascendMember(memberObj);
return false;
}
});
const ascendHelpTip = createElement("div", {
backgroundColor: "black",
class: "help-tip",
clickListener: () => {
dialogBoxCreate("TODO");
},
innerText: "?"
});
statsDiv.appendChild(statsP);
//statsDiv.appendChild(upgradeButton);
statsDiv.appendChild(ascendButton);
statsDiv.appendChild(ascendHelpTip);
//Panel for Selecting task and show respect/wanted gain
var taskDiv = createElement("div", {
id: name + "gang-member-task", class:"gang-member-info-div",
width:"30%", display:"inline"
// Panel 2 - Task Selection & Info
const taskDiv = createElement("div", {
class:"gang-member-info-div",
id: name + "gang-member-task",
});
var taskSelector = createElement("select", {
color:"white", backgroundColor:"black",
id:name + "gang-member-task-selector"
const taskSelector = createElement("select", {
id: name + "gang-member-task-selector",
});
var tasks = null;
// Get an array of the name of all tasks that are applicable for this Gang
let tasks = null;
const allTasks = Object.keys(GangMemberTasks);
if (Player.gang.isHackingGang) {
tasks = ["---", "Ransomware", "Phishing", "Identity Theft", "DDoS Attacks",
"Plant Virus", "Fraud & Counterfeiting","Money Laundering",
"Cyberterrorism", "Ethical Hacking", "Train Combat",
"Train Hacking", "Territory Warfare"];
tasks = allTasks.filter((e) => {
let task = GangMemberTasks[e];
if (task == null) { return false; }
if (e === "Unassigned") { return false; }
return task.isHacking;
});
} else {
tasks = ["---", "Mug People", "Deal Drugs", "Run a Con", "Armed Robbery",
"Traffick Illegal Arms", "Threaten & Blackmail",
"Terrorism", "Vigilante Justice", "Train Combat",
"Train Hacking", "Territory Warfare"];
tasks = allTasks.filter((e) => {
let task = GangMemberTasks[e];
if (task == null) { return false; }
if (e === "Unassigned") { return false; }
return task.isCombat;
});
}
tasks.unshift("---");
// Create selector for Gang member task
for (var i = 0; i < tasks.length; ++i) {
var option = document.createElement("option");
option.text = tasks[i];
@ -1301,17 +1333,19 @@ function createGangMemberDisplayElement(memberObj) {
setGangMemberTaskDescription(memberObj, task);
updateGangContent();
});
//Set initial task in selector element
// Set initial task in selector
if (memberObj.task instanceof GangMemberTask) {
var taskName = memberObj.task.name;
var taskIndex = 0;
for (let i = 0; i < tasks.length; ++i) {
if (taskName == tasks[i]) {
if (taskName === tasks[i]) {
taskIndex = i;
break;
}
}
taskSelector.selectedIndex = taskIndex;
setGangMemberTaskDescription(memberObj, taskName);
}
var gainInfo = createElement("p", {id:name + "gang-member-gain-info"});
@ -1320,19 +1354,13 @@ function createGangMemberDisplayElement(memberObj) {
//Panel for Description of task
var taskDescDiv = createElement("div", {
id:name + "gang-member-task-desc", class:"gang-member-info-div",
width:"30%", display:"inline"
class:"gang-member-info-div",
id: name + "gang-member-task-desc",
});
var taskDescP = createElement("p", {id: name + "gang-member-task-description", display:"inline"});
taskDescDiv.appendChild(taskDescP);
statsDiv.style.width = "30%";
taskDiv.style.width = "30%";
taskDescDiv.style.width = "30%";
statsDiv.style.display = "inline";
taskDiv.style.display = "inline";
taskDescDiv.style.display = "inline";
gangMemberDiv.appendChild(statsDiv);
gangMemberDiv.appendChild(taskDiv);
gangMemberDiv.appendChild(taskDescDiv);
@ -1372,7 +1400,7 @@ function setGangMemberTaskDescription(memberObj, taskName) {
var taskDesc = document.getElementById(name + "gang-member-task-description");
if (taskDesc) {
var task = GangMemberTasks[taskName];
if (task == null) {return;}
if (task == null) { task = GangMemberTasks["Unassigned"]; }
var desc = task.desc;
taskDesc.innerHTML = desc;
}

@ -4085,17 +4085,35 @@ function NetscriptFunctions(workerScript) {
return false;
}
},
getContractType : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("getContractType", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
}
updateDynamicRam("getContractType", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
let contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`);
return null;
}
return contract.getType();
},
getData : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
}
updateDynamicRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
var contract = getCodingContract(fn, ip);
let contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`);
return null;
}
return contract.getData();
let data = contract.getData();
if (data.constructor === Array) {
// Pass a copy
return data.slice();
} else {
return data;
}
},
getDescription : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {

@ -54,14 +54,13 @@ export async function executeJSScript(scripts = [], workerScript) {
//
// - script -- the script for whom we are getting a URL.
// - scripts -- all the scripts available on this server
// - envHeader -- the preamble that goes at the start of every NSJS script.
// - seen -- The modules above this one -- to prevent mutual dependency.
//
// TODO We don't make any effort to cache a given module when it is imported at
// different parts of the tree. That hasn't presented any problem with during
// testing, but it might be an idea for the future. Would require a topo-sort
// then url-izing from leaf-most to root-most.
function _getScriptUrls(script, scripts, seen) {
export function _getScriptUrls(script, scripts, seen) {
// Inspired by: https://stackoverflow.com/a/43834063/91401
const urlStack = [];
seen.push(script);
@ -90,7 +89,8 @@ function _getScriptUrls(script, scripts, seen) {
// The top url in the stack is the replacement import file for this script.
urlStack.push(...urls);
return [prefix, urls[urls.length - 1], suffix].join('');
});
}
);
// If we successfully transformed the code, create a blob url for it and

@ -138,7 +138,18 @@ function startNetscript2Script(workerScript) {
throw workerScript;
}
runningFn = propName;
let result = f(...args);
// If the function throws an error, clear the runningFn flag first, and then re-throw it
// This allows people to properly catch errors thrown by NS functions without getting
// the concurrent call error above
let result;
try {
result = f(...args);
} catch(e) {
runningFn = null;
throw(e);
}
if (result && result.finally !== undefined) {
return result.finally(function () {
runningFn = null;

@ -18,7 +18,6 @@ import {yesNoBoxCreate, yesNoBoxGetYesButton,
//Returns promise
function writeRedPillLine(line) {
return new Promise(function(resolve, reject) {
var container = document.getElementById("red-pill-content");
var pElem = document.createElement("p");
container.appendChild(pElem);
@ -54,6 +53,10 @@ function writeRedPillLetter(pElem, line, i=0) {
let redPillFlag = false;
function hackWorldDaemon(currentNodeNumber, flume=false) {
// Clear Red Pill screen first
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
redPillFlag = true;
Engine.loadRedPillContent();
return writeRedPillLine("[ERROR] SEMPOOL INVALID").then(function() {
@ -301,7 +304,7 @@ function createBitNodeYesNoEventListeners(newBitNode, destroyedBitNode, flume=fa
Player.gainIntelligenceExp(-5);
}
redPillFlag = false;
var container = document.getElementById("red-pill-container");
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
//Set new Bit Node

@ -24,8 +24,7 @@ import {iTutorialSteps, iTutorialNextStep,
ITutorial} from "./InteractiveTutorial";
import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript,
WorkerScript} from "./NetscriptWorker";
import {addWorkerScript, WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings";
@ -240,7 +239,7 @@ function scriptEditorInit() {
}
//Updates RAM usage in script
function updateScriptEditorContent() {
async function updateScriptEditorContent() {
var filename = document.getElementById("script-editor-filename").value;
if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked || !isScriptFilename(filename)) {
scriptEditorRamText.innerText = "N/A";
@ -249,7 +248,7 @@ function updateScriptEditorContent() {
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
var codeCopy = code.repeat(1);
var ramUsage = calculateRamUsage(codeCopy);
var ramUsage = await calculateRamUsage(codeCopy);
if (ramUsage !== -1) {
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB";
} else {
@ -402,9 +401,9 @@ Script.prototype.saveScript = function() {
}
//Updates how much RAM the script uses when it is running.
Script.prototype.updateRamUsage = function() {
Script.prototype.updateRamUsage = async function() {
var codeCopy = this.code.repeat(1);
var res = calculateRamUsage(codeCopy);
var res = await calculateRamUsage(codeCopy);
if (res !== -1) {
this.ramUsage = roundToTwo(res);
}
@ -422,7 +421,7 @@ const memCheckGlobalKey = ".__GLOBAL__";
// Calcluates the amount of RAM a script uses. Uses parsing and AST walking only,
// rather than NetscriptEvaluator. This is useful because NetscriptJS code does
// not work under NetscriptEvaluator.
function parseOnlyRamCalculate(server, code, workerScript) {
async function parseOnlyRamCalculate(server, code, workerScript) {
try {
// Maps dependent identifiers to their dependencies.
//
@ -465,11 +464,27 @@ function parseOnlyRamCalculate(server, code, workerScript) {
// Get the code from the server.
const nextModule = parseQueue.shift();
const script = server.getScript(nextModule);
if (!script) return -1; // No such script on the server.
let code;
if (nextModule.startsWith("https://")) {
try {
const module = await eval('import(nextModule)');
code = "";
for (const prop in module) {
if (typeof module[prop] === 'function') {
code += module[prop].toString() + ";\n";
}
}
} catch(e) {
console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`);
return -1;
}
} else {
const script = server.getScript(nextModule);
if (!script) return -1; // No such script on the server.
code = script.code;
}
// Not sure why we always take copies, but let's do that here too.
parseCode(script.code.repeat(1), nextModule);
parseCode(code, nextModule);
}
// Finally, walk the reference map and generate a ram cost. The initial set of keys to scan
@ -551,7 +566,7 @@ function parseOnlyRamCalculate(server, code, workerScript) {
return ram;
} catch (error) {
//console.info("parse or eval error: ", error);
// console.info("parse or eval error: ", error);
// This is not unexpected. The user may be editing a script, and it may be in
// a transitory invalid state.
return -1;
@ -688,7 +703,7 @@ function parseOnlyCalculateDeps(code, currentModule) {
return {dependencyMap: dependencyMap, additionalModules: additionalModules};
}
function calculateRamUsage(codeCopy) {
async function calculateRamUsage(codeCopy) {
//Create a temporary/mock WorkerScript and an AST from the code
var currServ = Player.getCurrentServer();
var workerScript = new WorkerScript({
@ -700,7 +715,7 @@ function calculateRamUsage(codeCopy) {
workerScript.serverIp = currServ.ip;
try {
return parseOnlyRamCalculate(currServ, codeCopy, workerScript);
return await parseOnlyRamCalculate(currServ, codeCopy, workerScript);
} catch (e) {
console.log("Failed to parse ram using new method. Falling back.", e);
}
@ -862,7 +877,6 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
var thisUpdate = new Date().getTime();
var lastUpdate = Player.lastUpdate;
var timePassed = (thisUpdate - lastUpdate) / 1000; //Seconds
console.log("Offline for " + timePassed + " seconds");
//Calculate the "confidence" rating of the script's true production. This is based
//entirely off of time. We will arbitrarily say that if a script has been running for

@ -50,7 +50,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
difficulty: 1,
gen: () => {
return getRandomInt(500, 9e9);
return getRandomInt(500, 1e9);
},
name: "Find Largest Prime Factor",
numTries: 10,

@ -1,111 +1,185 @@
/* tslint:disable:max-line-length */
/**
* Defines the parameters that can be used to initialize and describe a GangMemberTask
* (defined in Gang.js)
*/
export interface IGangMemberTaskMetadata {
/**
* Description of the task
*/
desc: string;
/**
* Whether or not this task is meant for Combat-type gangs
*/
isCombat: boolean;
/**
* Whether or not this task is for Hacking-type gangs
*/
isHacking: boolean;
/**
* Name of the task
*/
name: string;
/**
* An object containing weighting parameters for the task. These parameters are used for
* various calculations (respect gain, wanted gain, etc.)
*/
params?: any;
}
/**
* Array of metadata for all Gang Member tasks. Used to construct the global GangMemberTask
* objects in Gang.js
*/
export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
{
desc: "This gang member is currently idle",
isCombat: true,
isHacking: true,
name: "Unassigned",
},
{
desc: "Assign this gang member to create and distribute ransomware<br><br>Earns money - Slightly increases respect - Slightly increases wanted level",
isCombat: false,
isHacking: true,
name: "Ransomware",
params: {baseRespect: 0.00005, baseWanted: 0.00001, baseMoney: 1, hackWeight: 100, difficulty: 1},
},
{
desc: "Assign this gang member to attempt phishing scams and attacks<br><br>Earns money - Slightly increases respect - Slightly increases wanted level",
isCombat: false,
isHacking: true,
name: "Phishing",
params: {baseRespect: 0.00008, baseWanted: 0.001, baseMoney: 2.5, hackWeight: 85, chaWeight: 15, difficulty: 3},
},
{
desc: "Assign this gang member to attempt identity theft<br><br>Earns money - Increases respect - Increases wanted level",
isCombat: false,
isHacking: true,
name: "Identity Theft",
params: {baseRespect: 0.0001, baseWanted: 0.01, baseMoney: 6, hackWeight: 80, chaWeight: 20, difficulty: 4},
},
{
desc: "Assign this gang member to carry out DDoS attacks<br><br>Increases respect - Increases wanted level",
isCombat: false,
isHacking: true,
name: "DDoS Attacks",
params: {baseRespect: 0.0004, baseWanted: 0.05, hackWeight: 100, difficulty: 7},
},
{
desc: "Assign this gang member to create and distribute malicious viruses<br><br>Increases respect - Increases wanted level",
isCombat: false,
isHacking: true,
name: "Plant Virus",
params: {baseRespect: 0.0006, baseWanted: 0.05, hackWeight: 100, difficulty: 10},
},
{
desc: "Assign this gang member to commit financial fraud and digital counterfeiting<br><br>Earns money - Slightly increases respect - Slightly increases wanted level",
isCombat: false,
isHacking: true,
name: "Fraud & Counterfeiting",
params: {baseRespect: 0.0005, baseWanted: 0.1, baseMoney: 15, hackWeight: 80, chaWeight: 20, difficulty: 17},
},
{
desc: "Assign this gang member to launder money<br><br>Earns money - Increases respect - Increases wanted level",
isCombat: false,
isHacking: true,
name: "Money Laundering",
params: {baseRespect: 0.0006, baseWanted:0.2, baseMoney: 40, hackWeight: 75, chaWeight: 25, difficulty: 20},
params: {baseRespect: 0.0006, baseWanted: 0.2, baseMoney: 40, hackWeight: 75, chaWeight: 25, difficulty: 20},
},
{
desc: "Assign this gang member to commit acts of cyberterrorism<br><br>Greatly increases respect - Greatly increases wanted level",
isCombat: false,
isHacking: true,
name: "Cyberterrorism",
params: {baseRespect: 0.001, baseWanted: 0.5, hackWeight: 80, chaWeight: 20, difficulty: 33},
},
{
desc: "Assign this gang member to be an ethical hacker for corporations<br><br>Earns money - Lowers wanted level",
isCombat: false,
isHacking: true,
name: "Ethical Hacking",
params: {baseWanted: -0.001, baseMoney: 1, hackWeight: 90, chaWeight: 10, difficulty: 1},
},
{
desc: "Assign this gang member to mug random people on the streets<br><br>Earns money - Slightly increases respect - Very slightly increases wanted level",
isCombat: true,
isHacking: false,
name: "Mug People",
params: {baseRespect: 0.00005, baseWanted: 0.00001, baseMoney: 1, strWeight: 25, defWeight: 25, dexWeight: 25, agiWeight: 10, chaWeight: 15, difficulty: 1},
},
{
desc: "Assign this gang member to sell drugs.<br><br>Earns money - Slightly increases respect - Slightly increases wanted level",
isCombat: true,
isHacking: false,
name: "Deal Drugs",
params: {baseRespect: 0.00008, baseWanted: 0.001, baseMoney: 4, agiWeight: 20, dexWeight: 20, chaWeight: 60, difficulty: 3},
},
{
desc: "Assign this gang member to run cons<br><br>Earns money - Increases respect - Increases wanted level",
isCombat: true,
isHacking: false,
name: "Run a Con",
params: {baseRespect: 0.00015, baseWanted: 0.01, baseMoney: 10, strWeight: 5, defWeight: 5, agiWeight: 25, dexWeight: 25, chaWeight: 40, difficulty: 10},
},
{
desc: "Assign this gang member to commit armed robbery on stores, banks and armored cars<br><br>Earns money - Increases respect - Increases wanted level",
isCombat: true,
isHacking: false,
name: "Armed Robbery",
params: {baseRespect: 0.00015, baseWanted: 0.05, baseMoney: 25, hackWeight: 20, strWeight: 15, defWeight: 15, agiWeight: 10, dexWeight: 20, chaWeight: 20, difficulty: 17},
},
{
desc: "Assign this gang member to traffick illegal arms<br><br>Earns money - Increases respect - Increases wanted level",
isCombat: true,
isHacking: false,
name: "Traffick Illegal Arms",
params: {baseRespect: 0.0003, baseWanted: 0.1, baseMoney: 40, hackWeight: 15, strWeight: 20, defWeight: 20, dexWeight: 20, chaWeight: 75, difficulty: 25},
},
{
desc: "Assign this gang member to threaten and black mail high-profile targets<br><br>Earns money - Slightly increases respect - Slightly increases wanted level",
isCombat: true,
isHacking: false,
name: "Threaten & Blackmail",
params: {baseRespect: 0.0002, baseWanted: 0.05, baseMoney: 15, hackWeight: 25, strWeight: 25, dexWeight: 25, chaWeight: 25, difficulty: 28},
},
{
desc: "Assign this gang member to commit acts of terrorism<br><br>Greatly increases respect - Greatly increases wanted level",
isCombat: true,
isHacking: false,
name: "Terrorism",
params: {baseRespect: 0.001, baseWanted: 1, hackWeight: 20, strWeight: 20, defWeight: 20,dexWeight: 20, chaWeight: 20, difficulty: 33},
params: {baseRespect: 0.001, baseWanted: 1, hackWeight: 20, strWeight: 20, defWeight: 20, dexWeight: 20, chaWeight: 20, difficulty: 33},
},
{
desc: "Assign this gang member to be a vigilante and protect the city from criminals<br><br>Decreases wanted level",
isCombat: true,
isHacking: true,
name: "Vigilante Justice",
params: {baseWanted: -0.001, hackWeight: 20, strWeight: 20, defWeight: 20, dexWeight: 20, agiWeight:20, difficulty: 1},
params: {baseWanted: -0.001, hackWeight: 20, strWeight: 20, defWeight: 20, dexWeight: 20, agiWeight: 20, difficulty: 1},
},
{
desc: "Assign this gang member to increase their combat stats (str, def, dex, agi)",
isCombat: true,
isHacking: true,
name: "Train Combat",
params: {strWeight: 25, defWeight: 25, dexWeight: 25, agiWeight: 25, difficulty: 5},
},
{
desc: "Assign this gang member to train their hacking skills",
isCombat: true,
isHacking: true,
name: "Train Hacking",
params: {hackWeight: 100, difficulty: 8},
},
{
desc: "Assign this gang member to engage in territorial warfare with other gangs. Members assigned to this task will help increase your gang's territory and will defend your territory from being taken.",
isCombat: true,
isHacking: true,
name: "Territory Warfare",
params: {hackWeight: 15, strWeight: 20, defWeight: 20, dexWeight: 20, agiWeight: 20, chaWeight: 5, difficulty: 3},
},

@ -1,3 +1,7 @@
/**
* Defines the parameters that can be used to initialize and describe a GangMemberUpgrade
* (defined in Gang.js)
*/
export interface IGangMemberUpgradeMetadata {
cost: number;
mults: any;
@ -5,6 +9,10 @@ export interface IGangMemberUpgradeMetadata {
upgType: string;
}
/**
* Array of metadata for all Gang Member upgrades. Used to construct the global GangMemberUpgrade
* objects in Gang.js
*/
export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
{
cost: 1e6,
@ -20,19 +28,19 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 25e6,
mults: {str: 1.10, def: 1.10, dex: 1.10, agi: 1.10},
mults: {str: 1.1, def: 1.1, dex: 1.1, agi: 1.1},
name: "Glock 18C",
upgType: "w",
},
{
cost: 50e6,
mults: {str: 1.12, def: 1.12, agi: 1.10},
mults: {str: 1.12, def: 1.12, agi: 1.1},
name: "P90C",
upgType: "w",
},
{
cost: 60e6,
mults: {str: 1.20, def: 1.20},
mults: {str: 1.2, def: 1.2},
name: "Steyr AUG",
upgType: "w",
},
@ -44,13 +52,13 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 150e6,
mults: {str: 1.30, def: 1.30},
mults: {str: 1.3, def: 1.3},
name: "M15A10 Assault Rifle",
upgType: "w",
},
{
cost: 225e6,
mults: {str: 1.30, dex: 1.30, agi: 1.30},
mults: {str: 1.3, dex: 1.3, agi: 1.3},
name: "AWM Sniper Rifle",
upgType: "w",
},
@ -74,7 +82,7 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 40e6,
mults: {def: 1.20},
mults: {def: 1.2},
name: "Graphene Plating Armor",
upgType: "a",
},
@ -110,7 +118,7 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 15e6,
mults: {hack: 1.10},
mults: {hack: 1.1},
name: "Soulstealer Rootkit",
upgType: "r",
},
@ -122,13 +130,13 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 10e9,
mults: {str: 1.30, dex: 1.30},
mults: {str: 1.3, dex: 1.3},
name: "Bionic Arms",
upgType: "g",
},
{
cost: 10e9,
mults: {agi: 1.60},
mults: {agi: 1.6},
name: "Bionic Legs",
upgType: "g",
},
@ -140,25 +148,25 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 20e9,
mults: {str: 1.40, def: 1.40},
mults: {str: 1.4, def: 1.4},
name: "BrachiBlades",
upgType: "g",
},
{
cost: 12e9,
mults: {str: 1.20, def: 1.20},
mults: {str: 1.2, def: 1.2},
name: "Nanofiber Weave",
upgType: "g",
},
{
cost: 25e9,
mults: {str: 1.50, agi: 1.50},
mults: {str: 1.5, agi: 1.5},
name: "Synthetic Heart",
upgType: "g",
},
{
cost: 15e9,
mults: {str: 1.30, def:1.30},
mults: {str: 1.3, def: 1.3},
name: "Synfibril Muscle",
upgType: "g",
},
@ -176,7 +184,7 @@ export const gangMemberUpgradesMetadata: IGangMemberUpgradeMetadata[] = [
},
{
cost: 50e9,
mults: {str: 1.70, def: 1.70},
mults: {str: 1.7, def: 1.7},
name: "Graphene Bone Lacings",
upgType: "g",
},

@ -89,6 +89,7 @@ import "../css/loader.scss";
import "../css/missions.scss";
import "../css/companymanagement.scss";
import "../css/bladeburner.scss";
import "../css/gang.scss";
/* Shortcuts to navigate through the game
* Alt-t - Terminal
@ -1169,10 +1170,18 @@ const Engine = {
reward.type = getRandomInt(0, CodingContractRewardType.Money);
// Change type based on certain conditions
if (reward.type === CodingContractRewardType.FactionReputation && Player.factions.length === 0) {
var factionsThatAllowHacking = Player.factions.filter((fac) => {
try {
return Factions[fac].getInfo().offerHackingWork;
} catch (e) {
console.error(`Error when trying to filter Hacking Factions for Coding Contract Generation: ${e}`);
return false;
}
});
if (reward.type === CodingContractRewardType.FactionReputation && factionsThatAllowHacking.length === 0) {
reward.type = CodingContractRewardType.CompanyReputation;
}
if (reward.type === CodingContractRewardType.FactionReputationAll && Player.factions.length === 0) {
if (reward.type === CodingContractRewardType.FactionReputationAll && factionsThatAllowHacking.length === 0) {
reward.type = CodingContractRewardType.CompanyReputation;
}
if (reward.type === CodingContractRewardType.CompanyReputation && Player.companyName === "") {
@ -1183,18 +1192,10 @@ const Engine = {
switch (reward.type) {
case CodingContractRewardType.FactionReputation:
// Get a random faction that player is a part of. That
//faction must allow hacking contracts
var numFactions = Player.factions.length;
var randFaction = Player.factions[getRandomInt(0, numFactions - 1)];
try {
while(Factions[randFaction].getInfo().offerHackingWork !== true) {
randFaction = Player.factions[getRandomInt(0, numFactions - 1)];
}
reward.name = randFaction;
} catch (e) {
exceptionAlert("Failed to find a faction for Coding Contract Generation: " + e);
}
// faction must allow hacking contracts
var numFactions = factionsThatAllowHacking.length;
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction;
break;
case CodingContractRewardType.CompanyReputation:
if (Player.companyName !== "") {

@ -725,8 +725,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<!-- Log Box -->
<div id="log-box-container">
<div id="log-box-content">
<span id="log-box-close" class="popup-box-button"> Close </span>
<span id="log-box-kill-script" class="popup-box-button">Kill Script</span>
<button id="log-box-close" class="popup-box-button"> Close </button>
<button id="log-box-kill-script" class="popup-box-button">Kill Script</button>
<p id="log-box-text-header"> </p>
<p id="log-box-text"> </p>
</div>
@ -736,8 +736,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="yes-no-box-container" class="popup-box-container">
<div id="yes-no-box-content" class="popup-box-content">
<p id="yes-no-box-text"> </p>
<span id="yes-no-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-box-no" class="popup-box-button"> No </span>
<button id="yes-no-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -746,8 +746,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="yes-no-text-input-box-content" class="popup-box-content">
<p id="yes-no-text-input-box-text"> </p>
<input type="text" id="yes-no-text-input-box-input" pattern="[a-zA-Z0-9-_]" maxlength="30" />
<span id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </span>
<span id="yes-no-text-input-box-no" class="popup-box-button"> No </span>
<button id="yes-no-text-input-box-yes" class="popup-box-button"> Yes </button>
<button id="yes-no-text-input-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -760,8 +760,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
Would you like to join? <br /> <br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<span id="faction-invitation-box-yes" class="popup-box-button"> Yes </span>
<span id="faction-invitation-box-no" class="popup-box-button"> No </span>
<button id="faction-invitation-box-yes" class="popup-box-button"> Yes </button>
<button id="faction-invitation-box-no" class="popup-box-button"> No </button>
</div>
</div>
@ -770,9 +770,9 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="infiltration-box-content" class="popup-box-content">
<p id="infiltration-box-text"> </p>
<span id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </span> <br /><br />
<button id="infiltration-box-sell" class="a-link-button"> Sell on Black Market </button> <br /><br />
<select id="infiltration-faction-select"> </select> <br />
<span id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </span>
<button id="infiltration-box-faction" class="a-link-button"> Give to Faction for Reputation </button>
</div>
</div>
@ -785,7 +785,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="work-in-progress-container" class="generic-fullscreen-container">
<p id="work-in-progress-text"> </p>
<span id="work-in-progress-cancel-button"> Cancel Work </span>
<button id="work-in-progress-cancel-button"> Cancel Work </button>
</div>
<!-- Red Pill Container -->
@ -800,9 +800,9 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="interactive-tutorial-wrapper">
<div id="interactive-tutorial-container">
<p id="interactive-tutorial-text"> </p>
<span id="interactive-tutorial-exit"> Exit Tutorial </span>
<span id="interactive-tutorial-next"> Next </span>
<span id="interactive-tutorial-back"> Back </span>
<button id="interactive-tutorial-exit"> Exit Tutorial </button>
<button id="interactive-tutorial-next"> Next </button>
<button id="interactive-tutorial-back"> Back </button>
</div>
</div>
@ -841,8 +841,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
</table>
</div>
<div class="character-quick-options">
<span id="character-overview-save-button">Save Game</span>
<span id="character-overview-options-button">Options</span>
<button id="character-overview-save-button">Save Game</button>
<button id="character-overview-options-button">Options</button>
</div>
</div>
</div>
@ -855,7 +855,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<!-- Game Options -->
<div id="game-options-container" class="popup-box-container">
<div id="game-options-content" class="game-options-box">
<span id="game-options-close-button">&times;</span>
<button id="game-options-close-button">&times;</button>
<h1> Game Options </h1>
<br />
<div id="game-options-left-panel">

@ -67,8 +67,7 @@ module.exports = (env, argv) => {
]
}),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
filename: "[name].css"
})
],
target: "web",