mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-29 11:03:48 +01:00
v0.28.6 changes
This commit is contained in:
parent
c0369bc937
commit
e3991b8795
1001
dist/bundle.js
vendored
1001
dist/bundle.js
vendored
File diff suppressed because it is too large
Load Diff
@ -53,7 +53,20 @@ function initBitNodes() {
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
||||
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
||||
"that you can use.");
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "COMING SOON"); //Int
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman", "They said it couldn't be done. They said the human brain, " +
|
||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||
"by 1's and 0's. They were wrong.<br><br>" +
|
||||
"In this BitNode <br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
|
||||
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
|
||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
|
||||
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
|
||||
"in the game. This source file will also raise all of your hacking-related multipliers by:<br><br>" +
|
||||
"Level 1: 4%<br>" +
|
||||
"Level 2: 6%<br>" +
|
||||
"Level 3: 7%");
|
||||
BitNodes["BitNode6"] = new BitNode(6, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic
|
||||
BitNodes["BitNode7"] = new BitNode(7, "Do Androids Dream?", "COMING SOON"); //Build androids for automation
|
||||
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "COMING SOON"); //Trading only viable strategy
|
||||
|
@ -1,5 +1,5 @@
|
||||
let CONSTANTS = {
|
||||
Version: "0.28.5",
|
||||
Version: "0.28.6",
|
||||
|
||||
//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
|
||||
@ -86,9 +86,9 @@ let CONSTANTS = {
|
||||
|
||||
//Server constants
|
||||
ServerBaseGrowthRate: 1.03, //Unadjusted Growth rate
|
||||
ServerMaxGrowthRate: 1.0035, //Maximum possible growth rate (max rate accounting for server security)
|
||||
ServerMaxGrowthRate: 1.0035, //Maximum possible growth rate (max rate accounting for server security)
|
||||
ServerFortifyAmount: 0.002, //Amount by which server's security increases when its hacked/grown
|
||||
ServerWeakenAmount: 0.05, //Amount by which server's security decreases when weakened
|
||||
ServerWeakenAmount: 0.05, //Amount by which server's security decreases when weakened
|
||||
|
||||
PurchasedServerLimit: 25,
|
||||
|
||||
@ -112,6 +112,13 @@ let CONSTANTS = {
|
||||
//Hospital/Health
|
||||
HospitalCostPerHp: 100000,
|
||||
|
||||
//Intelligence-related constants
|
||||
IntelligenceCrimeWeight: 0.05, //Weight for how much int affects crime success rates
|
||||
IntelligenceInfiltrationWeight: 0.1, //Weight for how much int affects infiltration success rates
|
||||
IntelligenceCrimeBaseExpGain: 0.0001,
|
||||
IntelligenceProgramBaseExpGain: 1000, //Program required hack level divided by this to determine int exp gain
|
||||
IntelligenceTerminalHackBaseExpGain: 200, //Hacking exp divided by this to determine int exp gain
|
||||
|
||||
//Gang constants
|
||||
GangRespectToReputationRatio: 2, //Respect is divided by this to get rep gain
|
||||
MaximumGangMembers: 20,
|
||||
@ -231,7 +238,8 @@ let CONSTANTS = {
|
||||
"encounter diminishing returns in your hacking (since you are only hacking a certain percentage). You can " +
|
||||
"increase the amount of money on a server using a script and the grow() function in Netscript.<br><br>" +
|
||||
"<h1>Server Security</h1><br>" +
|
||||
"Each server has a security level, which is denoted by a number between 1 and 100. A higher number means " +
|
||||
"Each server has a security level, typically between 1 and 100. It is possible for a server to have a security " +
|
||||
"level 100 or higher, in which case hacking a server will become impossible. A higher number means " +
|
||||
"the server has stronger security. As mentioned above, a server's security level is an important factor " +
|
||||
"to consider when hacking. You can check a server's security level using the 'analyze' command, although this " +
|
||||
"only gives an estimate (with 5% uncertainty). You can also check a server's security in a script, using the " +
|
||||
@ -482,6 +490,8 @@ let CONSTANTS = {
|
||||
"<i>getServerRam(hostname/ip)</i><br>Returns an array with two elements that gives information about the target server's RAM. The first " +
|
||||
"element in the array is the amount of RAM that the server has (in GB). The second element in the array is the amount of RAM that " +
|
||||
"is currently being used on the server.<br><br>" +
|
||||
"<i>serverExists(hostname/ip)</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>fileExists(filename, [hostname/ip])</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 or a program. A script name is case-sensitive, but a " +
|
||||
"program is not. For example, fileExists('brutessh.exe') will work fine, even though the actual program is named BruteSSH.exe. <br><br> " +
|
||||
@ -547,6 +557,17 @@ let CONSTANTS = {
|
||||
"The second argument must be a string with the hostname/IP of the target server. If the first argument is specified " +
|
||||
"then the second argument must be specified as well. Any additional arguments passed to the function will specify " +
|
||||
"the arguments passed into the target script.<br><br>" +
|
||||
"<i>getScriptExpGain([scriptname], [hostname/ip], [args...])</i><br>" +
|
||||
"Returns the amount of hacking experience the specified script generates while online (when the game is open, does not apply for " +
|
||||
"offline experience gains). This function can also return the total experience gain rate of all of your active scripts by running the function " +
|
||||
"with no arguments.<br><br>" +
|
||||
"Remember that a script is uniquely identified by both its name and its arguments. So for example if you ran a script " +
|
||||
"with the arguments 'foodnstuff' and '5' then in order to use this function to get that script's income you must " +
|
||||
"specify those arguments in this function call.<br><br>" +
|
||||
"The first argument, if specified, must be a string with the name of the script (including the .script extension). " +
|
||||
"The second argument must be a string with the hostname/IP of the target server. If the first argument is specified " +
|
||||
"then the second argument must be specified as well. Any additional arguments passed to the function will specify " +
|
||||
"the arguments passed into the target script.<br><br>" +
|
||||
"<u><h1>Hacknet Nodes API</h1></u><br>" +
|
||||
"Netscript provides the following API for accessing and upgrading your Hacknet Nodes through scripts. This API does NOT work offline.<br><br>" +
|
||||
"<i>hacknetnodes</i><br> A special variable. This is an array that maps to the Player's Hacknet Nodes. The Hacknet Nodes are accessed through " +
|
||||
@ -875,41 +896,17 @@ let CONSTANTS = {
|
||||
"World Stock Exchange account and TIX API Access<br>",
|
||||
|
||||
LatestUpdate:
|
||||
"v0.28.5<br>" +
|
||||
"-The fl1ght.exe program that is received from jump3r is now sent very early on in the game, rather " +
|
||||
"than at hacking level 1000<br>" +
|
||||
"-Hostname is now displayed in Terminal<br>" +
|
||||
"-Syntax highlighting now works for all Netscript functions<br>" +
|
||||
"-Export should now work on Edge/IE<br><br>" +
|
||||
"v0.28.4<br>" +
|
||||
"-Added getScriptIncome() Netscript function<br>" +
|
||||
"-Added Javascript's Math module to Netscript. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math<br>" +
|
||||
"-Added several member variables for the Hacknet Node API that allow you to access info about their income<br>" +
|
||||
"-All valid Netscript functions are now syntax highlighted as keywords in the editor. This means they will a different " +
|
||||
"color than invalid netscript functions. The color will depend on your theme. " +
|
||||
"Note that right now, this only applies for normal Netscript functions, not " +
|
||||
"functions in the TIX API, Hacknet Node API, or Singularity Functions.<br><br>" +
|
||||
"-Comments and operators no longer count towards RAM usage in scripts.<br>" +
|
||||
"-Variety of bug fixes and updates to informational text in the game<br><br>" +
|
||||
"v0.28.3<br>" +
|
||||
"-Added ls() Netscript function<br>" +
|
||||
"-Increased company wages by about ~10% across the board<br>" +
|
||||
"-The scp() Netsction function and Terminal command now works for .lit files<br>" +
|
||||
"-Increased the amount of RAM on many lower level servers (up to level 200 hacking level required).<br><br>" +
|
||||
"v0.28.2<br>" +
|
||||
"-Added a few script editor configuration options. Includes key bindings, themes, etc.<br>" +
|
||||
"-Certain menu options will now be hidden until their relevant gameplay is unlocked. This " +
|
||||
"really only affects new players<br>" +
|
||||
"-Most unrecognized or un-implemented syntax errors in a script will now include line number in error message<br>" +
|
||||
"-Various bug fixes<br><br>" +
|
||||
"v0.28.1<br>" +
|
||||
"-The script editor now uses the open-source Ace editor, which provides a much better experience when coding!<br>" +
|
||||
"-Added tprint() Netscript function<br><br>" +
|
||||
"v0.28.0<br>" +
|
||||
"-Added BitNode-4: The Singularity<br>" +
|
||||
"-Added BitNode-11: The Big Crash<br>" +
|
||||
"-Migrated the codebase to use webpack (doesn't affect any in game content, except maybe some slight " +
|
||||
"performance improvements and there may be bugs that result from dependency errors)"
|
||||
"v0.28.6<br>" +
|
||||
"-Time required to create programs now scales better with hacking level, and should generally be much faster<br>" +
|
||||
"-Added serverExists(hostname/ip) and getScriptExpGain(scriptname, ip, args...) Netscript functions<br>" +
|
||||
"-Short circuiting && and || logical operators should now work<br>" +
|
||||
"-Assigning to multidimensional arrays should now work<br>" +
|
||||
"-Scripts will no longer wait for hack/grow/weaken functions to finish if they are killed. They will die immediately<br>" +
|
||||
"-The script loop that checks whether any scripts need to be started/stopped now runs every 6 seconds rather than 10 " +
|
||||
"(resulting in less delays when stopping/starting scripts)<br>" +
|
||||
"-Fixed several bugs/exploits<br>" +
|
||||
"-Added some description for BitNode-5 (not implemented yet, should be soon though)<br>",
|
||||
|
||||
}
|
||||
|
||||
export {CONSTANTS};
|
||||
|
@ -110,99 +110,113 @@ function determineCrimeSuccess(crime, moneyGained) {
|
||||
}
|
||||
}
|
||||
|
||||
let intWgt = CONSTANTS.IntelligenceCrimeWeight;
|
||||
let maxLvl = CONSTANTS.MaxSkillLevel;
|
||||
|
||||
function determineCrimeChanceShoplift() {
|
||||
var chance = ((Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel)) * 20;
|
||||
var chance = (Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) * 20;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceRobStore() {
|
||||
var chance = ((0.5 * Player.hacking_skill / CONSTANTS.MaxSkillLevel +
|
||||
2 * Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
1 * Player.agility / CONSTANTS.MaxSkillLevel)) * 5;
|
||||
var chance = (0.5 * Player.hacking_skill / maxLvl +
|
||||
2 * Player.dexterity / maxLvl +
|
||||
1 * Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) * 5;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceMug() {
|
||||
var chance = ((1.5 * Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
0.5 * Player.defense / CONSTANTS.MaxSkillLevel +
|
||||
1.5 * Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
0.5 * Player.agility / CONSTANTS.MaxSkillLevel)) * 5;
|
||||
var chance = (1.5 * Player.strength / maxLvl +
|
||||
0.5 * Player.defense / maxLvl +
|
||||
1.5 * Player.dexterity / maxLvl +
|
||||
0.5 * Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) * 5;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceLarceny() {
|
||||
var chance = ((0.5 * Player.hacking_skill / CONSTANTS.MaxSkillLevel +
|
||||
Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel)) * 3;
|
||||
var chance = (0.5 * Player.hacking_skill / maxLvl +
|
||||
Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) * 3;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceDealDrugs() {
|
||||
var chance = ((3*Player.charisma / CONSTANTS.MaxSkillLevel +
|
||||
2*Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel));
|
||||
var chance = (3*Player.charisma / maxLvl +
|
||||
2*Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl);
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceTraffickArms() {
|
||||
var chance = ((Player.charisma / CONSTANTS.MaxSkillLevel +
|
||||
Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
Player.defense / CONSTANTS.MaxSkillLevel +
|
||||
Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel)) / 2;
|
||||
var chance = (Player.charisma / maxLvl +
|
||||
Player.strength / maxLvl +
|
||||
Player.defense / maxLvl +
|
||||
Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) / 2;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceHomicide() {
|
||||
var chance = ((2 * Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
2 * Player.defense / CONSTANTS.MaxSkillLevel +
|
||||
0.5 * Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
0.5 * Player.agility / CONSTANTS.MaxSkillLevel));
|
||||
var chance = (2 * Player.strength / maxLvl +
|
||||
2 * Player.defense / maxLvl +
|
||||
0.5 * Player.dexterity / maxLvl +
|
||||
0.5 * Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl);
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceGrandTheftAuto() {
|
||||
var chance = ((Player.hacking_skill / CONSTANTS.MaxSkillLevel +
|
||||
Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
4 * Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
2 * Player.agility / CONSTANTS.MaxSkillLevel +
|
||||
2 * Player.charisma / CONSTANTS.MaxSkillLevel)) / 8;
|
||||
var chance = (Player.hacking_skill / maxLvl +
|
||||
Player.strength / maxLvl +
|
||||
4 * Player.dexterity / maxLvl +
|
||||
2 * Player.agility / maxLvl +
|
||||
2 * Player.charisma / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) / 8;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceKidnap() {
|
||||
var chance = ((Player.charisma / CONSTANTS.MaxSkillLevel +
|
||||
Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel)) / 5;
|
||||
var chance = (Player.charisma / maxLvl +
|
||||
Player.strength / maxLvl +
|
||||
Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) / 5;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceAssassination() {
|
||||
var chance = ((Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
2 * Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel)) / 8;
|
||||
var chance = (Player.strength / maxLvl +
|
||||
2 * Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) / 8;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
function determineCrimeChanceHeist() {
|
||||
var chance = ((Player.hacking_skill / CONSTANTS.MaxSkillLevel +
|
||||
Player.strength / CONSTANTS.MaxSkillLevel +
|
||||
Player.defense / CONSTANTS.MaxSkillLevel +
|
||||
Player.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
Player.agility / CONSTANTS.MaxSkillLevel +
|
||||
Player.charisma / CONSTANTS.MaxSkillLevel)) / 18;
|
||||
var chance = (Player.hacking_skill / maxLvl +
|
||||
Player.strength / maxLvl +
|
||||
Player.defense / maxLvl +
|
||||
Player.dexterity / maxLvl +
|
||||
Player.agility / maxLvl +
|
||||
Player.charisma / maxLvl +
|
||||
intWgt * Player.intelligence / maxLvl) / 18;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ function processAllGangPowerGains(numCycles=1) {
|
||||
if (name == playerGangName) {
|
||||
AllGangs[name].power += Player.gang.calculatePower();
|
||||
} else {
|
||||
var gain = Math.random() * 0.01; //TODO Adjust as necessary
|
||||
var gain = Math.random() * 0.02; //TODO Adjust as necessary
|
||||
AllGangs[name].power += (gain);
|
||||
}
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ function updateInfiltrationLevelText(inst) {
|
||||
var totalValue = 0;
|
||||
var totalMoneyValue = 0;
|
||||
for (var i = 0; i < inst.secretsStolen.length; ++i) {
|
||||
totalValue += inst.secretsStolen[i];
|
||||
totalValue += (inst.secretsStolen[i] * Player.faction_rep_mult * 1.25);
|
||||
totalMoneyValue += inst.secretsStolen[i] * CONSTANTS.InfiltrationMoneyValue;
|
||||
}
|
||||
document.getElementById("infiltration-level-text").innerHTML =
|
||||
@ -577,6 +577,7 @@ function updateInfiltrationButtons(inst, scenario) {
|
||||
}
|
||||
}
|
||||
|
||||
let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
|
||||
|
||||
//Kill
|
||||
//Success: 5%, Failure 10%, -Karma
|
||||
@ -649,7 +650,8 @@ function getInfiltrationStealthKnockoutChance(inst) {
|
||||
return Math.min(0.95,
|
||||
(0.5 * Player.strength +
|
||||
2 * Player.dexterity +
|
||||
2 * Player.agility) / (3 * lvl));
|
||||
2 * Player.agility +
|
||||
intWgt * Player.intelligence) / (3 * lvl));
|
||||
}
|
||||
|
||||
//Assassination
|
||||
@ -671,7 +673,8 @@ function getInfiltrationAssassinateChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.dexterity +
|
||||
0.5 * Player.agility) / (2 * lvl));
|
||||
0.5 * Player.agility +
|
||||
intWgt * Player.intelligence) / (2 * lvl));
|
||||
}
|
||||
|
||||
|
||||
@ -720,7 +723,8 @@ function attemptInfiltrationHack(inst) {
|
||||
function getInfiltrationHackChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.hacking_skill) / lvl);
|
||||
(Player.hacking_skill +
|
||||
(intWgt * Player.intelligence)) / lvl);
|
||||
}
|
||||
|
||||
//Sneak past security
|
||||
@ -740,7 +744,8 @@ function getInfiltrationSneakChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.agility +
|
||||
0.5 * Player.dexterity) / (2 * lvl));
|
||||
0.5 * Player.dexterity +
|
||||
intWgt * Player.intelligence) / (2 * lvl));
|
||||
}
|
||||
|
||||
//Pick locked door
|
||||
@ -760,7 +765,8 @@ function attemptInfiltrationPickLockedDoor(inst) {
|
||||
function getInfiltrationPickLockedDoorChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.dexterity) / lvl);
|
||||
(Player.dexterity +
|
||||
intWgt * Player.intelligence) / lvl);
|
||||
}
|
||||
|
||||
//Bribe
|
||||
@ -800,7 +806,8 @@ function getInfiltrationEscapeChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(2 * Player.agility +
|
||||
Player.dexterity) / lvl);
|
||||
Player.dexterity +
|
||||
intWgt * Player.intelligence) / lvl);
|
||||
}
|
||||
|
||||
export {beginInfiltration};
|
||||
|
@ -50,6 +50,30 @@ Environment.prototype = {
|
||||
return (scope || this).vars[name] = value;
|
||||
},
|
||||
|
||||
setArrayElement: function(name, idx, value) {
|
||||
if (!(idx instanceof Array)) {
|
||||
throw new Error("idx parameter is not an Array");
|
||||
}
|
||||
var scope = this.lookup(name);
|
||||
if (!scope && this.parent) {
|
||||
console.log("Here");
|
||||
throw new Error("Undefined variable " + name);
|
||||
}
|
||||
var arr = (scope || this).vars[name];
|
||||
if (!(arr.constructor === Array || arr instanceof Array)) {
|
||||
throw new Error("Variable is not an array: " + name);
|
||||
}
|
||||
var res = arr;
|
||||
for (var iterator = 0; iterator < idx.length-1; ++iterator) {
|
||||
var i = idx[iterator];
|
||||
if (!(res instanceof Array) || i >= res.length) {
|
||||
throw new Error("Out-of-bounds array access");
|
||||
}
|
||||
res = res[i];
|
||||
}
|
||||
return res[idx[idx.length-1]] = value;
|
||||
},
|
||||
/*
|
||||
setArrayElement: function(name, idx, value) {
|
||||
var scope = this.lookup(name);
|
||||
if (!scope && this.parent) {
|
||||
@ -61,7 +85,7 @@ Environment.prototype = {
|
||||
throw new Error("Variable is not an array: " + name);
|
||||
}
|
||||
return (scope || this).vars[name][idx] = value;
|
||||
},
|
||||
},*/
|
||||
|
||||
//Creates (or overwrites) a variable in the current scope
|
||||
def: function(name, value) {
|
||||
|
@ -252,6 +252,12 @@ function evalBinary(exp, workerScript){
|
||||
return new Promise(function(resolve, reject) {
|
||||
var expLeftPromise = evaluate(exp.left, workerScript);
|
||||
expLeftPromise.then(function(expLeft) {
|
||||
if (expLeft == true && exp.operator === "||") {
|
||||
return resolve(true);
|
||||
}
|
||||
if (expLeft == false && exp.operator === "&&") {
|
||||
return resolve(false);
|
||||
}
|
||||
var expRightPromise = evaluate(exp.right, workerScript);
|
||||
expRightPromise.then(function(expRight) {
|
||||
switch (exp.operator){
|
||||
@ -307,7 +313,7 @@ function evalBinary(exp, workerScript){
|
||||
resolve(expLeft && expRight);
|
||||
break;
|
||||
default:
|
||||
reject(makeRuntimeRejectMsg(workerScript, "Bitwise operators are not implemented"));
|
||||
reject(makeRuntimeRejectMsg(workerScript, "Unsupported operator: " + exp.operator));
|
||||
}
|
||||
}, function(e) {
|
||||
reject(e);
|
||||
@ -341,6 +347,40 @@ function evalUnary(exp, workerScript){
|
||||
});
|
||||
}
|
||||
|
||||
//Takes in a MemberExpression that should represent a Netscript array (possible multidimensional)
|
||||
//The return value is an array of the form:
|
||||
// [0th index (leftmost), array name, 1st index, 2nd index, ...]
|
||||
function getArrayElement(exp, workerScript) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var indices = [];
|
||||
var iPromise = evaluate(exp.property, workerScript);
|
||||
iPromise.then(function(idx) {
|
||||
if (isNaN(idx)) {
|
||||
return reject(makeRuntimeRejectMsg(workerScript, "Invalid access to array. Index is not a number: " + idx));
|
||||
} else {
|
||||
if (exp.object.name === undefined && exp.object.object) {
|
||||
var recursePromise = getArrayElement(exp.object, workerScript);
|
||||
recursePromise.then(function(res) {
|
||||
res.push(idx);
|
||||
indices = res;
|
||||
return resolve(indices);
|
||||
}).catch(function(e) {
|
||||
return reject(e);
|
||||
});
|
||||
} else {
|
||||
indices.push(idx);
|
||||
indices.push(exp.object.name);
|
||||
return resolve(indices);
|
||||
}
|
||||
}
|
||||
}).catch(function(e) {
|
||||
console.log(e);
|
||||
console.log("Error getting index in getArrayElement: " + e.toString());
|
||||
return reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function evalAssignment(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
return new Promise(function(resolve, reject) {
|
||||
@ -360,6 +400,22 @@ function evalAssignment(exp, workerScript) {
|
||||
//Assign to array element
|
||||
//Array object designed by exp.left.object.name
|
||||
//Index designated by exp.left.property
|
||||
var getArrayElementPromise = getArrayElement(exp.left, workerScript);
|
||||
getArrayElementPromise.then(function(res) {
|
||||
if (!(res instanceof Array) || res.length < 2) {
|
||||
return reject(makeRuntimeRejectMsg(workerScript, "Error evaluating array assignment. This is (probably) a bug please report to game dev"));
|
||||
}
|
||||
|
||||
//The array name is the second value
|
||||
var arrName = res.splice(1, 1);
|
||||
arrName = arrName[0];
|
||||
|
||||
env.setArrayElement(arrName, res, expRight);
|
||||
return resolve(false);
|
||||
}).catch(function(e) {
|
||||
return reject(e);
|
||||
});
|
||||
/*
|
||||
var name = exp.left.object.name;
|
||||
if (!(name in env.vars)){
|
||||
reject(makeRuntimeRejectMsg(workerScript, "variable " + name + " not defined"));
|
||||
@ -380,7 +436,7 @@ function evalAssignment(exp, workerScript) {
|
||||
});
|
||||
} else {
|
||||
return reject(makeRuntimeRejectMsg(workerScript, "Trying to access a non-array variable using the [] operator"));
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
//Other assignments
|
||||
try {
|
||||
@ -406,11 +462,12 @@ function evalAssignment(exp, workerScript) {
|
||||
default:
|
||||
reject(makeRuntimeRejectMsg(workerScript, "Bitwise assignment is not implemented"));
|
||||
}
|
||||
resolve(false);
|
||||
} catch (e) {
|
||||
return reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString()));
|
||||
}
|
||||
}
|
||||
resolve(false); //Return false so this doesnt cause conditionals to evaluate
|
||||
//resolve(false); //Return false so this doesnt cause conditionals to evaluate
|
||||
}, function(e) {
|
||||
reject(e);
|
||||
});
|
||||
@ -567,9 +624,13 @@ function evaluateProg(exp, workerScript, index) {
|
||||
});
|
||||
}
|
||||
|
||||
function netscriptDelay(time) {
|
||||
function netscriptDelay(time, workerScript) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, time);
|
||||
var delay = setTimeout(resolve, time);
|
||||
workerScript.killTrigger = function() {
|
||||
clearTimeout(delay);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -577,6 +638,7 @@ function makeRuntimeRejectMsg(workerScript, msg) {
|
||||
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg;
|
||||
}
|
||||
|
||||
/*
|
||||
function apply_op(op, a, b) {
|
||||
function num(x) {
|
||||
if (typeof x != "number")
|
||||
@ -605,6 +667,7 @@ function apply_op(op, a, b) {
|
||||
}
|
||||
throw new Error("Can't apply operator " + op);
|
||||
}
|
||||
*/
|
||||
|
||||
//Run a script from inside a script using run() command
|
||||
function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
|
||||
@ -671,7 +734,7 @@ function isScriptErrorMessage(msg) {
|
||||
//The same as Player's calculateHackingChance() function but takes in the server as an argument
|
||||
function scriptCalculateHackingChance(server) {
|
||||
var difficultyMult = (100 - server.hackDifficulty) / 100;
|
||||
var skillMult = (1.75 * Player.hacking_skill);
|
||||
var skillMult = (1.75 * Player.hacking_skill) + (0.2 * Player.intelligence);
|
||||
var skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
|
||||
var chance = skillChance * difficultyMult * Player.hacking_chance_mult;
|
||||
if (chance > 1) {return 1;}
|
||||
@ -682,7 +745,7 @@ function scriptCalculateHackingChance(server) {
|
||||
//The same as Player's calculateHackingTime() function but takes in the server as an argument
|
||||
function scriptCalculateHackingTime(server) {
|
||||
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50);
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
|
||||
var hackingTime = 5 * skillFactor / Player.hacking_speed_mult; //This is in seconds
|
||||
return hackingTime;
|
||||
}
|
||||
@ -708,7 +771,7 @@ function scriptCalculatePercentMoneyHacked(server) {
|
||||
//Amount of time to execute grow() in milliseconds
|
||||
function scriptCalculateGrowTime(server) {
|
||||
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50);
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
|
||||
var growTime = 16 * skillFactor / Player.hacking_speed_mult; //This is in seconds
|
||||
return growTime * 1000;
|
||||
}
|
||||
@ -716,7 +779,7 @@ function scriptCalculateGrowTime(server) {
|
||||
//Amount of time to execute weaken() in milliseconds
|
||||
function scriptCalculateWeakenTime(server) {
|
||||
var difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50);
|
||||
var skillFactor = (2.5 * difficultyMult + 500) / (Player.hacking_skill + 50 + (0.1 * Player.intelligence));
|
||||
var weakenTime = 20 * skillFactor / Player.hacking_speed_mult; //This is in seconds
|
||||
return weakenTime * 1000;
|
||||
}
|
||||
|
@ -37,9 +37,10 @@ import {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript,
|
||||
import {Environment} from "./NetscriptEnvironment.js";
|
||||
|
||||
import Decimal from '../utils/decimal.js';
|
||||
import {dialogBoxCreate} from "../utils/DialogBox.js";
|
||||
import {printArray, powerOfTwo} from "../utils/HelperFunctions.js";
|
||||
import {createRandomIp} from "../utils/IPAddress.js";
|
||||
import {formatNumber, isString} from "../utils/StringHelperFunctions.js";
|
||||
import {formatNumber, isString, isHTML} from "../utils/StringHelperFunctions.js";
|
||||
|
||||
var hasSingularitySF = false;
|
||||
var singularitySFLvl = 1;
|
||||
@ -101,7 +102,7 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
workerScript.scriptRef.log("Attempting to hack " + ip + " in " + hackingTime.toFixed(3) + " seconds (t=" + threads + ")");
|
||||
//console.log("Hacking " + server.hostname + " after " + hackingTime.toString() + " seconds (t=" + threads + ")");
|
||||
return netscriptDelay(hackingTime* 1000).then(function() {
|
||||
return netscriptDelay(hackingTime* 1000, workerScript).then(function() {
|
||||
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
|
||||
var hackChance = scriptCalculateHackingChance(server);
|
||||
var rand = Math.random();
|
||||
@ -146,7 +147,7 @@ function NetscriptFunctions(workerScript) {
|
||||
if (log) {
|
||||
workerScript.scriptRef.log("Sleeping for " + time + " milliseconds");
|
||||
}
|
||||
return netscriptDelay(time).then(function() {
|
||||
return netscriptDelay(time, workerScript).then(function() {
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
},
|
||||
@ -171,7 +172,7 @@ function NetscriptFunctions(workerScript) {
|
||||
var growTime = scriptCalculateGrowTime(server);
|
||||
//console.log("Executing grow() on server " + server.hostname + " in " + formatNumber(growTime/1000, 3) + " seconds")
|
||||
workerScript.scriptRef.log("Executing grow() on server " + server.hostname + " in " + formatNumber(growTime/1000, 3) + " seconds (t=" + threads + ")");
|
||||
return netscriptDelay(growTime).then(function() {
|
||||
return netscriptDelay(growTime, workerScript).then(function() {
|
||||
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
|
||||
server.moneyAvailable += (1 * threads); //It can be grown even if it has no money
|
||||
var growthPercentage = processSingleServerGrowth(server, 450 * threads);
|
||||
@ -207,10 +208,9 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
var weakenTime = scriptCalculateWeakenTime(server);
|
||||
//console.log("Executing weaken() on server " + server.hostname + " in " + formatNumber(weakenTime/1000, 3) + " seconds")
|
||||
workerScript.scriptRef.log("Executing weaken() on server " + server.hostname + " in " +
|
||||
formatNumber(weakenTime/1000, 3) + " seconds (t=" + threads + ")");
|
||||
return netscriptDelay(weakenTime).then(function() {
|
||||
return netscriptDelay(weakenTime, workerScript).then(function() {
|
||||
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
|
||||
server.weaken(CONSTANTS.ServerWeakenAmount * threads);
|
||||
workerScript.scriptRef.recordWeaken(server.ip, threads);
|
||||
@ -232,6 +232,14 @@ function NetscriptFunctions(workerScript) {
|
||||
if (args === undefined || args === null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "tprint() call has incorrect number of arguments. Takes 1 argument");
|
||||
}
|
||||
var x = args.toString();
|
||||
if (isHTML(x)) {
|
||||
Player.takeDamage(1);
|
||||
dialogBoxCreate("You suddenly feel a sharp shooting pain through your body as an angry voice in your head exclaims: <br><br>" +
|
||||
"DON'T USE TPRINT() TO OUTPUT HTML ELEMENTS TO YOUR TERMINAL!!!!<br><br>" +
|
||||
"(You lost 1 HP)");
|
||||
return;
|
||||
}
|
||||
post(workerScript.scriptRef.filename + ": " + args.toString());
|
||||
},
|
||||
clearLog : function() {
|
||||
@ -678,6 +686,9 @@ function NetscriptFunctions(workerScript) {
|
||||
workerScript.scriptRef.log("getServerRam() returned [" + formatNumber(server.maxRam, 2) + "GB, " + formatNumber(server.ramUsed, 2) + "GB]");
|
||||
return [server.maxRam, server.ramUsed];
|
||||
},
|
||||
serverExists : function(ip) {
|
||||
return (getServer(ip) !== null);
|
||||
},
|
||||
fileExists : function(filename,ip=workerScript.serverIp){
|
||||
if (filename === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "fileExists() call has incorrect number of arguments. Usage: fileExists(scriptname, [server])");
|
||||
@ -1038,6 +1049,32 @@ function NetscriptFunctions(workerScript) {
|
||||
return runningScriptObj.onlineMoneyMade / runningScriptObj.onlineRunningTime;
|
||||
}
|
||||
},
|
||||
getScriptExpGain : function(scriptname, ip) {
|
||||
if (arguments.length === 0) {
|
||||
var total = 0;
|
||||
for (var i = 0; i < workerScripts.length; ++i) {
|
||||
total += (workerScripts[i].scriptRef.onlineExpGained / workerScripts[i].scriptRef.onlineRunningTime);
|
||||
}
|
||||
return total;
|
||||
} else {
|
||||
//Get income for a particular script
|
||||
var server = getServer(ip);
|
||||
if (server === null) {
|
||||
workerScript.scriptRef.log("getScriptExpGain() failed. Invalid IP or hostnamed passed in: " + ip);
|
||||
throw makeRuntimeRejectMsg(workerScript, "getScriptExpGain() failed. Invalid IP or hostnamed passed in: " + ip);
|
||||
}
|
||||
var argsForScript = [];
|
||||
for (var i = 2; i < arguments.length; ++i) {
|
||||
argsForScript.push(arguments[i]);
|
||||
}
|
||||
var runningScriptObj = findRunningScript(scriptname, argsForScript, server);
|
||||
if (runningScriptObj == null) {
|
||||
workerScript.scriptRef.log("getScriptExpGain() failed. No such script "+ scriptname + " on " + server.hostname + " with args: " + printArray(argsForScript));
|
||||
return -1;
|
||||
}
|
||||
return runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime;
|
||||
}
|
||||
},
|
||||
|
||||
/* Singularity Functions */
|
||||
universityCourse(universityName, className) {
|
||||
|
@ -25,6 +25,7 @@ function WorkerScript(runningScriptObj) {
|
||||
this.scriptRef = runningScriptObj;
|
||||
this.errorMessage = "";
|
||||
this.args = runningScriptObj.args;
|
||||
this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay())
|
||||
}
|
||||
|
||||
//Returns the server on which the workerScript is running
|
||||
@ -148,7 +149,7 @@ function runScriptsLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(runScriptsLoop, 10000);
|
||||
setTimeout(runScriptsLoop, 6000);
|
||||
}
|
||||
|
||||
//Queues a script to be killed by settings its stop flag to true. Then, the code will reject
|
||||
@ -159,6 +160,7 @@ function killWorkerScript(runningScriptObj, serverIp) {
|
||||
if (workerScripts[i].name == runningScriptObj.filename && workerScripts[i].serverIp == serverIp &&
|
||||
compareArrays(workerScripts[i].args, runningScriptObj.args)) {
|
||||
workerScripts[i].env.stopFlag = true;
|
||||
workerScripts[i].killTrigger();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
140
src/Player.js
140
src/Player.js
@ -8,7 +8,8 @@ import {CONSTANTS} from "./Constants.js";
|
||||
import {Programs} from "./CreateProgram.js";
|
||||
import {determineCrimeSuccess} from "./Crimes.js";
|
||||
import {Engine} from "./engine.js";
|
||||
import {Factions, Faction} from "./Faction.js";
|
||||
import {Factions, Faction,
|
||||
displayFactionContent} from "./Faction.js";
|
||||
import {Gang, resetGangs} from "./Gang.js";
|
||||
import {Locations} from "./Location.js";
|
||||
import {AllServers, Server, AddToAllServers} from "./Server.js";
|
||||
@ -146,12 +147,14 @@ function PlayerObject() {
|
||||
this.workMoneyGained = 0;
|
||||
|
||||
this.createProgramName = "";
|
||||
this.createProgramReqLvl = 0;
|
||||
|
||||
this.className = "";
|
||||
|
||||
this.crimeType = "";
|
||||
|
||||
this.timeWorked = 0; //in ms
|
||||
this.timeWorkedCreateProgram = 0;
|
||||
this.timeNeededToCompleteWork = 0;
|
||||
|
||||
this.work_money_mult = 1;
|
||||
@ -196,15 +199,6 @@ PlayerObject.prototype.init = function() {
|
||||
this.getHomeComputer().programs.push(Programs.NukeProgram);
|
||||
}
|
||||
|
||||
PlayerObject.prototype.increaseMultiplier = function(name, val) {
|
||||
let mult = this[name];
|
||||
if (mult === null || mult === undefined) {
|
||||
console.log("ERROR: Could not find this multiplier " + name);
|
||||
return;
|
||||
}
|
||||
mult *= val;
|
||||
}
|
||||
|
||||
PlayerObject.prototype.prestigeAugmentation = function() {
|
||||
var homeComp = this.getHomeComputer();
|
||||
this.currentServer = homeComp.ip;
|
||||
@ -395,6 +389,12 @@ PlayerObject.prototype.updateSkillLevels = function() {
|
||||
this.agility = Math.floor(this.calculateSkill(this.agility_exp) * this.agility_mult);
|
||||
this.charisma = Math.floor(this.calculateSkill(this.charisma_exp) * this.charisma_mult);
|
||||
|
||||
if (this.intelligence > 0) {
|
||||
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
|
||||
} else {
|
||||
this.intelligence = 0;
|
||||
}
|
||||
|
||||
var ratio = this.hp / this.max_hp;
|
||||
this.max_hp = Math.floor(10 + this.defense / 10);
|
||||
Player.hp = Math.round(this.max_hp * ratio);
|
||||
@ -442,7 +442,7 @@ PlayerObject.prototype.resetMultipliers = function() {
|
||||
// (2 * hacking_chance_multiplier * hacking_skill) 100
|
||||
PlayerObject.prototype.calculateHackingChance = function() {
|
||||
var difficultyMult = (100 - this.getCurrentServer().hackDifficulty) / 100;
|
||||
var skillMult = (1.75 * this.hacking_skill);
|
||||
var skillMult = (1.75 * this.hacking_skill) + (0.2 * this.intelligence);
|
||||
var skillChance = (skillMult - this.getCurrentServer().requiredHackingSkill) / skillMult;
|
||||
var chance = skillChance * difficultyMult * this.hacking_chance_mult;
|
||||
if (chance > 1) {return 1;}
|
||||
@ -457,7 +457,7 @@ PlayerObject.prototype.calculateHackingChance = function() {
|
||||
// hacking_skill + 100
|
||||
PlayerObject.prototype.calculateHackingTime = function() {
|
||||
var difficultyMult = this.getCurrentServer().requiredHackingSkill * this.getCurrentServer().hackDifficulty;
|
||||
var skillFactor = (2.5 * difficultyMult + 200) / (this.hacking_skill + 100);
|
||||
var skillFactor = (2.5 * difficultyMult + 200) / (this.hacking_skill + 100 + (0.1 * this.intelligence));
|
||||
return 5 * skillFactor / this.hacking_speed_mult;
|
||||
}
|
||||
|
||||
@ -578,7 +578,18 @@ PlayerObject.prototype.gainIntelligenceExp = function(exp) {
|
||||
if (isNaN(exp)) {
|
||||
console.log("ERROR: NaN passed into Player.gainIntelligenceExp()"); return;
|
||||
}
|
||||
//TODO
|
||||
var hasBn = false;
|
||||
for (var i = 0; i < this.sourceFiles.length; ++i) {
|
||||
if (this.sourceFiles[i].n === 5) {
|
||||
hasBn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasBn || this.intelligence > 0) {
|
||||
this.intelligence_exp += exp;
|
||||
} else {
|
||||
console.log("Not gaining intelligence experience bc it hasn't been unlocked yet");
|
||||
}
|
||||
}
|
||||
|
||||
/******* Working functions *******/
|
||||
@ -602,6 +613,7 @@ PlayerObject.prototype.resetWorkStatus = function() {
|
||||
this.workMoneyGained = 0;
|
||||
|
||||
this.timeWorked = 0;
|
||||
this.timeWorkedCreateProgram = 0;
|
||||
|
||||
this.currentWorkFactionName = "";
|
||||
this.currentWorkFactionDescription = "";
|
||||
@ -658,7 +670,8 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
this.isWorking = false;
|
||||
Engine.loadTerminalContent();
|
||||
//Engine.loadTerminalContent();
|
||||
Engine.loadLocationContent();
|
||||
|
||||
if (sing) {
|
||||
return "You worked a short shift of " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
|
||||
@ -734,7 +747,7 @@ PlayerObject.prototype.work = function(numCycles) {
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
txt.innerHTML = "You are currently working as a " + this.companyPosition.positionName +
|
||||
" at " + Player.companyName + "<br><br>" +
|
||||
" at " + this.companyName + "<br><br>" +
|
||||
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
|
||||
"You have earned: <br><br>" +
|
||||
"$" + formatNumber(this.workMoneyGained, 2) + " ($" + formatNumber(this.workMoneyGainRate * cyclesPerSec, 2) + " / sec) <br><br>" +
|
||||
@ -851,7 +864,8 @@ PlayerObject.prototype.finishWorkPartTime = function(sing=false) {
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
this.isWorking = false;
|
||||
Engine.loadTerminalContent();
|
||||
//Engine.loadTerminalContent();
|
||||
Engine.loadLocationContent();
|
||||
if (sing) {
|
||||
return "You worked for " + convertTimeMsToTimeElapsedString(this.timeWorked) + " and " +
|
||||
"earned a total of " +
|
||||
@ -894,7 +908,9 @@ PlayerObject.prototype.finishFactionWork = function(cancelled, sing=false) {
|
||||
|
||||
this.isWorking = false;
|
||||
|
||||
Engine.loadTerminalContent();
|
||||
//Engine.loadTerminalContent();
|
||||
Engine.loadFactionContent();
|
||||
displayFactionContent(faction.name);
|
||||
if (sing) {
|
||||
return "You worked for your faction " + faction.name + " for a total of " + convertTimeMsToTimeElapsedString(this.timeWorked) + ". " +
|
||||
"You earned " +
|
||||
@ -936,7 +952,7 @@ PlayerObject.prototype.startFactionHackWork = function(faction) {
|
||||
this.resetWorkStatus();
|
||||
|
||||
this.workHackExpGainRate = .15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.workRepGainRate = this.hacking_skill / CONSTANTS.MaxSkillLevel * this.faction_rep_mult;
|
||||
this.workRepGainRate = this.workRepGainRate = (this.hacking_skill + this.intelligence) / CONSTANTS.MaxSkillLevel * this.faction_rep_mult;
|
||||
|
||||
this.factionWorkType = CONSTANTS.FactionWorkHacking;
|
||||
this.currentWorkFactionDescription = "carrying out hacking contracts";
|
||||
@ -984,7 +1000,7 @@ PlayerObject.prototype.workForFaction = function(numCycles) {
|
||||
//Constantly update the rep gain rate
|
||||
switch (this.factionWorkType) {
|
||||
case CONSTANTS.FactionWorkHacking:
|
||||
this.workRepGainRate = this.hacking_skill / CONSTANTS.MaxSkillLevel * this.faction_rep_mult;
|
||||
this.workRepGainRate = (this.hacking_skill + this.intelligence) / CONSTANTS.MaxSkillLevel * this.faction_rep_mult;
|
||||
break;
|
||||
case CONSTANTS.FactionWorkField:
|
||||
this.workRepGainRate = this.getFactionFieldWorkRepGain();
|
||||
@ -1103,6 +1119,10 @@ PlayerObject.prototype.getWorkRepGain = function() {
|
||||
var jobPerformance = this.companyPosition.calculateJobPerformance(this.hacking_skill, this.strength,
|
||||
this.defense, this.dexterity,
|
||||
this.agility, this.charisma);
|
||||
|
||||
//Intelligence provides a flat bonus to job performance
|
||||
jobPerformance += (this.intelligence / CONSTANTS.MaxSkillLevel);
|
||||
|
||||
//Update reputation gain rate to account for company favor
|
||||
var favorMult = 1 + (company.favor / 100);
|
||||
if (isNaN(favorMult)) {favorMult = 1;}
|
||||
@ -1124,7 +1144,8 @@ PlayerObject.prototype.getFactionFieldWorkRepGain = function() {
|
||||
this.defense / CONSTANTS.MaxSkillLevel +
|
||||
this.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
this.agility / CONSTANTS.MaxSkillLevel +
|
||||
this.charisma / CONSTANTS.MaxSkillLevel) / 6;
|
||||
this.charisma / CONSTANTS.MaxSkillLevel +
|
||||
this.intelligence / CONSTANTS.MaxSkillLevel) / 6;
|
||||
return t * this.faction_rep_mult;
|
||||
}
|
||||
|
||||
@ -1136,21 +1157,22 @@ PlayerObject.prototype.startCreateProgramWork = function(programName, time, reqL
|
||||
|
||||
//Time needed to complete work affected by hacking skill (linearly based on
|
||||
//ratio of (your skill - required level) to MAX skill)
|
||||
var timeMultiplier = (CONSTANTS.MaxSkillLevel - (this.hacking_skill - reqLevel)) / CONSTANTS.MaxSkillLevel;
|
||||
if (timeMultiplier > 1) {timeMultiplier = 1;}
|
||||
if (timeMultiplier < 0.01) {timeMultiplier = 0.01;}
|
||||
//var timeMultiplier = (CONSTANTS.MaxSkillLevel - (this.hacking_skill - reqLevel)) / CONSTANTS.MaxSkillLevel;
|
||||
//if (timeMultiplier > 1) {timeMultiplier = 1;}
|
||||
//if (timeMultiplier < 0.01) {timeMultiplier = 0.01;}
|
||||
this.createProgramReqLvl = reqLevel;
|
||||
|
||||
this.timeNeededToCompleteWork = timeMultiplier * time;
|
||||
this.timeNeededToCompleteWork = time;
|
||||
//Check for incomplete program
|
||||
for (var i = 0; i < Player.getHomeComputer().programs.length; ++i) {
|
||||
var programFile = Player.getHomeComputer().programs[i];
|
||||
for (var i = 0; i < this.getHomeComputer().programs.length; ++i) {
|
||||
var programFile = this.getHomeComputer().programs[i];
|
||||
if (programFile.startsWith(programName) && programFile.endsWith("%-INC")) {
|
||||
var res = programFile.split("-");
|
||||
if (res.length != 3) {break;}
|
||||
var percComplete = Number(res[1].slice(0, -1));
|
||||
if (isNaN(percComplete) || percComplete < 0 || percComplete >= 100) {break;}
|
||||
this.timeWorked = percComplete / 100 * this.timeNeededToCompleteWork;
|
||||
Player.getHomeComputer().programs.splice(i, 1);
|
||||
this.timeWorkedCreateProgram = percComplete / 100 * this.timeNeededToCompleteWork;
|
||||
this.getHomeComputer().programs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1168,17 +1190,24 @@ PlayerObject.prototype.startCreateProgramWork = function(programName, time, reqL
|
||||
}
|
||||
|
||||
PlayerObject.prototype.createProgramWork = function(numCycles) {
|
||||
this.timeWorked += Engine._idleSpeed * numCycles;
|
||||
//Higher hacking skill will allow you to create programs faster
|
||||
var reqLvl = this.createProgramReqLvl;
|
||||
var skillMult = (this.hacking_skill / reqLvl); //This should always be greater than 1;
|
||||
skillMult = 1 + ((skillMult - 1) / 5); //The divider constant can be adjusted as necessary
|
||||
|
||||
//Skill multiplier directly applied to "time worked"
|
||||
this.timeWorked += (Engine._idleSpeed * numCycles);
|
||||
this.timeWorkedCreateProgram += (Engine._idleSpeed * numCycles * skillMult);
|
||||
var programName = this.createProgramName;
|
||||
|
||||
if (this.timeWorked >= this.timeNeededToCompleteWork) {
|
||||
if (this.timeWorkedCreateProgram >= this.timeNeededToCompleteWork) {
|
||||
this.finishCreateProgramWork(false);
|
||||
}
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
txt.innerHTML = "You are currently working on coding " + programName + ".<br><br> " +
|
||||
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
|
||||
"The program is " + (this.timeWorked / this.timeNeededToCompleteWork * 100).toFixed(2) + "% complete. <br>" +
|
||||
"The program is " + (this.timeWorkedCreateProgram / this.timeNeededToCompleteWork * 100).toFixed(2) + "% complete. <br>" +
|
||||
"If you cancel, your work will be saved and you can come back to complete the program later.";
|
||||
}
|
||||
|
||||
@ -1188,17 +1217,19 @@ PlayerObject.prototype.finishCreateProgramWork = function(cancelled, sing=false)
|
||||
dialogBoxCreate("You've finished creating " + programName + "!<br>" +
|
||||
"The new program can be found on your home computer.");
|
||||
|
||||
Player.getHomeComputer().programs.push(programName);
|
||||
this.getHomeComputer().programs.push(programName);
|
||||
} else {
|
||||
var perc = Math.floor(this.timeWorked / this.timeNeededToCompleteWork * 100).toString();
|
||||
var perc = Math.floor(this.timeWorkedCreateProgram / this.timeNeededToCompleteWork * 100).toString();
|
||||
var incompleteName = programName + "-" + perc + "%-INC";
|
||||
Player.getHomeComputer().programs.push(incompleteName);
|
||||
this.getHomeComputer().programs.push(incompleteName);
|
||||
}
|
||||
|
||||
this.gainIntelligenceExp(this.createProgramReqLvl / CONSTANTS.IntelligenceProgramBaseExpGain);
|
||||
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
|
||||
Player.isWorking = false;
|
||||
this.isWorking = false;
|
||||
|
||||
Engine.loadTerminalContent();
|
||||
}
|
||||
@ -1397,16 +1428,16 @@ PlayerObject.prototype.startCrime = function(hackExp, strExp, defExp, dexExp, ag
|
||||
PlayerObject.prototype.commitCrime = function (numCycles) {
|
||||
this.timeWorked += Engine._idleSpeed * numCycles;
|
||||
|
||||
if (this.timeWorked >= this.timeNeededToCompleteWork) {Player.finishCrime(false); return;}
|
||||
if (this.timeWorked >= this.timeNeededToCompleteWork) {this.finishCrime(false); return;}
|
||||
|
||||
var percent = Math.round(Player.timeWorked / Player.timeNeededToCompleteWork * 100);
|
||||
var percent = Math.round(this.timeWorked / this.timeNeededToCompleteWork * 100);
|
||||
var numBars = Math.round(percent / 5);
|
||||
if (numBars < 0) {numBars = 0;}
|
||||
if (numBars > 20) {numBars = 20;}
|
||||
var progressBar = "[" + Array(numBars+1).join("|") + Array(20 - numBars + 1).join(" ") + "]";
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
txt.innerHTML = "You are attempting to " + Player.crimeType + ".<br>" +
|
||||
txt.innerHTML = "You are attempting to " + this.crimeType + ".<br>" +
|
||||
"Time remaining: " + convertTimeMsToTimeElapsedString(this.timeNeededToCompleteWork - this.timeWorked) + "<br>" +
|
||||
progressBar.replace( / /g, " " );
|
||||
}
|
||||
@ -1442,6 +1473,7 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
|
||||
break;
|
||||
case CONSTANTS.CrimeGrandTheftAuto:
|
||||
this.karma -= 5;
|
||||
this.gainIntelligenceExp(CONSTANTS.IntelligenceCrimeBaseExpGain);
|
||||
break;
|
||||
case CONSTANTS.CrimeKidnap:
|
||||
this.karma -= 6;
|
||||
@ -1449,9 +1481,11 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
|
||||
case CONSTANTS.CrimeAssassination:
|
||||
++this.numPeopleKilled;
|
||||
this.karma -= 10;
|
||||
this.gainIntelligenceExp(CONSTANTS.IntelligenceCrimeBaseExpGain);
|
||||
break;
|
||||
case CONSTANTS.CrimeHeist:
|
||||
this.karma -= 15;
|
||||
this.gainIntelligenceExp(5 * CONSTANTS.IntelligenceCrimeBaseExpGain);
|
||||
break;
|
||||
default:
|
||||
console.log(this.crimeType);
|
||||
@ -1542,7 +1576,7 @@ PlayerObject.prototype.hospitalize = function() {
|
||||
dialogBoxCreate("You were in critical condition! You were taken to the hospital where " +
|
||||
"luckily they were able to save your life. You were charged $" +
|
||||
formatNumber(this.max_hp * CONSTANTS.HospitalCostPerHp, 2));
|
||||
Player.loseMoney(this.max_hp * CONSTANTS.HospitalCostPerHp);
|
||||
this.loseMoney(this.max_hp * CONSTANTS.HospitalCostPerHp);
|
||||
this.hp = this.max_hp;
|
||||
}
|
||||
|
||||
@ -1626,8 +1660,8 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) {
|
||||
this.companyName = company.companyName;
|
||||
this.companyPosition = pos;
|
||||
|
||||
if (Player.firstJobRecvd === false) {
|
||||
Player.firstJobRecvd = true;
|
||||
if (this.firstJobRecvd === false) {
|
||||
this.firstJobRecvd = true;
|
||||
document.getElementById("job-tab").style.display = "list-item";
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -1735,8 +1769,8 @@ PlayerObject.prototype.applyForAgentJob = function(sing=false) {
|
||||
PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions.Employee)) {
|
||||
if (Player.firstJobRecvd === false) {
|
||||
Player.firstJobRecvd = true;
|
||||
if (this.firstJobRecvd === false) {
|
||||
this.firstJobRecvd = true;
|
||||
document.getElementById("job-tab").style.display = "list-item";
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -1755,8 +1789,8 @@ PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
|
||||
PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions.PartTimeEmployee)) {
|
||||
if (Player.firstJobRecvd === false) {
|
||||
Player.firstJobRecvd = true;
|
||||
if (this.firstJobRecvd === false) {
|
||||
this.firstJobRecvd = true;
|
||||
document.getElementById("job-tab").style.display = "list-item";
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -1775,8 +1809,8 @@ PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
|
||||
PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions.Waiter)) {
|
||||
if (Player.firstJobRecvd === false) {
|
||||
Player.firstJobRecvd = true;
|
||||
if (this.firstJobRecvd === false) {
|
||||
this.firstJobRecvd = true;
|
||||
document.getElementById("job-tab").style.display = "list-item";
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -1795,8 +1829,8 @@ PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
|
||||
PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions.PartTimeWaiter)) {
|
||||
if (Player.firstJobRecvd === false) {
|
||||
Player.firstJobRecvd = true;
|
||||
if (this.firstJobRecvd === false) {
|
||||
this.firstJobRecvd = true;
|
||||
document.getElementById("job-tab").style.display = "list-item";
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -2149,10 +2183,10 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
var totalHacknetRam = 0;
|
||||
var totalHacknetCores = 0;
|
||||
var totalHacknetLevels = 0;
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
totalHacknetLevels += Player.hacknetNodes[i].level;
|
||||
totalHacknetRam += Player.hacknetNodes[i].ram;
|
||||
totalHacknetCores += Player.hacknetNodes[i].cores;
|
||||
for (var i = 0; i < this.hacknetNodes.length; ++i) {
|
||||
totalHacknetLevels += this.hacknetNodes[i].level;
|
||||
totalHacknetRam += this.hacknetNodes[i].ram;
|
||||
totalHacknetCores += this.hacknetNodes[i].cores;
|
||||
}
|
||||
if (!netburnersFac.isBanned && !netburnersFac.isMember && !netburnersFac.alreadyInvited &&
|
||||
this.hacking_skill >= 80 && totalHacknetRam >= 8 &&
|
||||
|
@ -218,6 +218,9 @@ function prestigeSourceFile() {
|
||||
mainMenu.style.visibility = "visible";
|
||||
Terminal.resetTerminalInput();
|
||||
Engine.loadTerminalContent();
|
||||
|
||||
//Gain int exp
|
||||
Player.gainIntelligenceExp(5);
|
||||
}
|
||||
|
||||
export {prestigeAugmentation, prestigeSourceFile};
|
||||
|
@ -232,7 +232,8 @@ function calculateRamUsage(codeCopy) {
|
||||
numOccurrences(codeCopy, "getServerGrowth(") +
|
||||
numOccurrences(codeCopy, "getServerRequiredHackingLevel(") +
|
||||
numOccurrences(codeCopy, "getServerNumPortsRequired(") +
|
||||
numOccurrences(codeCopy, "getServerRam(");
|
||||
numOccurrences(codeCopy, "getServerRam(") +
|
||||
numOccurrences(codeCopy, "serverExists(");
|
||||
var fileExistsCount = numOccurrences(codeCopy, "fileExists(");
|
||||
var isRunningCount = numOccurrences(codeCopy, "isRunning(");
|
||||
var purchaseHacknetCount = numOccurrences(codeCopy, "purchaseHacknetNode(");
|
||||
@ -251,7 +252,9 @@ function calculateRamUsage(codeCopy) {
|
||||
var scriptReadCount = numOccurrences(codeCopy, "read(");
|
||||
var arbScriptCount = numOccurrences(codeCopy, "scriptRunning(") +
|
||||
numOccurrences(codeCopy, "scriptKill(");
|
||||
var getScriptCount = numOccurrences(codeCopy, "getScriptRam(");
|
||||
var getScriptCount = numOccurrences(codeCopy, "getScriptRam(") +
|
||||
numOccurrences(codeCopy, "getScriptIncome(") +
|
||||
numOccurrences(codeCopy, "getScriptExpGain(");
|
||||
var getHackTimeCount = numOccurrences(codeCopy, "getHackTime(") +
|
||||
numOccurrences(codeCopy, "getGrowTime(") +
|
||||
numOccurrences(codeCopy, "getWeakenTime(");
|
||||
|
@ -669,13 +669,14 @@ function processSingleServerGrowth(server, numCycles) {
|
||||
serverGrowth = 1;
|
||||
}
|
||||
|
||||
var oldMoneyAvailable = server.moneyAvailable;
|
||||
server.moneyAvailable *= serverGrowth;
|
||||
if (server.moneyMax && isNaN(server.moneyAvailable)) {
|
||||
server.moneyAvailable = server.moneyMax;
|
||||
}
|
||||
if (server.moneyMax && server.moneyAvailable > server.moneyMax) {
|
||||
server.moneyAvailable = server.moneyMax;
|
||||
return 1;
|
||||
return server.moneyAvailable / oldMoneyAvailable;
|
||||
}
|
||||
|
||||
//Growing increases server security twice as much as hacking
|
||||
|
@ -29,13 +29,13 @@ import {addOffset, printArray} from "../utils/HelperFunctions.js";
|
||||
import {logBoxCreate} from "../utils/LogBox.js";
|
||||
|
||||
/* Write text to terminal */
|
||||
//If replace is true then spaces are replaced with " "
|
||||
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);">' + input.replace( / /g, " " ) + '</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>');
|
||||
}
|
||||
|
||||
updateTerminalScroll();
|
||||
}
|
||||
|
||||
@ -441,13 +441,12 @@ let Terminal = {
|
||||
var moneyGained = Player.calculatePercentMoneyHacked();
|
||||
moneyGained = Math.floor(server.moneyAvailable * moneyGained);
|
||||
|
||||
//Safety check
|
||||
if (moneyGained <= 0) {moneyGained = 0;}
|
||||
if (moneyGained <= 0) {moneyGained = 0;} //Safety check
|
||||
|
||||
server.moneyAvailable -= moneyGained;
|
||||
Player.gainMoney(moneyGained);
|
||||
|
||||
Player.gainHackingExp(expGainedOnSuccess)
|
||||
Player.gainIntelligenceExp(expGainedOnSuccess / CONSTANTS.IntelligenceTerminalHackBaseExpGain);
|
||||
|
||||
server.fortify(CONSTANTS.ServerFortifyAmount);
|
||||
|
||||
@ -1418,6 +1417,7 @@ let Terminal = {
|
||||
(function() {
|
||||
var hostname = links[i].innerHTML.toString();
|
||||
links[i].onclick = function() {
|
||||
if (Terminal.analyzeFlag || Terminal.hackFlag) {return;}
|
||||
Terminal.connectToServer(hostname);
|
||||
}
|
||||
}());//Immediate invocation
|
||||
|
@ -30,7 +30,7 @@ function infiltrationBoxCreate(inst) {
|
||||
Player.gainDexterityExp(inst.dexExpGained);
|
||||
Player.gainAgilityExp(inst.agiExpGained);
|
||||
Player.gainCharismaExp(inst.chaExpGained);
|
||||
|
||||
|
||||
var totalValue = 0;
|
||||
for (var i = 0; i < inst.secretsStolen.length; ++i) {
|
||||
totalValue += inst.secretsStolen[i];
|
||||
@ -47,7 +47,7 @@ function infiltrationBoxCreate(inst) {
|
||||
formatNumber(inst.chaExpGained, 3) + " cha exp<br>");
|
||||
return;
|
||||
}
|
||||
var facValue = totalValue * Player.faction_rep_mult * 1.2
|
||||
var facValue = totalValue * Player.faction_rep_mult * 1.25
|
||||
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue;
|
||||
infiltrationSetText("You can sell the classified documents and secrets " +
|
||||
"you stole from " + inst.companyName + " for $" +
|
||||
|
@ -129,6 +129,16 @@ function numNetscriptOperators(string) {
|
||||
return total;
|
||||
}
|
||||
|
||||
//Checks if a string contains HTML elements
|
||||
function isHTML(str) {
|
||||
var a = document.createElement('div');
|
||||
a.innerHTML = str;
|
||||
for (var c = a.childNodes, i = c.length; i--; ) {
|
||||
if (c[i].nodeType == 1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export {getIndicesOf, convertTimeMsToTimeElapsedString, longestCommonStart,
|
||||
isString, isPositiveNumber, containsAllStrings, formatNumber,
|
||||
numOccurrences, numNetscriptOperators};
|
||||
numOccurrences, numNetscriptOperators, isHTML};
|
||||
|
Loading…
Reference in New Issue
Block a user