mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-22 14:12:27 +01:00
Added hackAnalyze() functions. Fixed bug with gymWorkout() and Millenium Fitness Gym. Updated documentation for new functions
This commit is contained in:
parent
56a727ae48
commit
ee831a87f8
@ -139,3 +139,7 @@
|
|||||||
.cmpy-mgmt-advertising-info {
|
.cmpy-mgmt-advertising-info {
|
||||||
font-size: $defaultFontSize * 0.75;
|
font-size: $defaultFontSize * 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#corporation-research-popup-box-content {
|
||||||
|
overflow-x: visible !important;
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
.Treant > .researched {
|
.Treant > .researched {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
pointer-events: none;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1582
dist/engine.bundle.js
vendored
1582
dist/engine.bundle.js
vendored
File diff suppressed because it is too large
Load Diff
4
dist/engine.css
vendored
4
dist/engine.css
vendored
@ -2019,6 +2019,9 @@ button {
|
|||||||
.cmpy-mgmt-advertising-info {
|
.cmpy-mgmt-advertising-info {
|
||||||
font-size: 12px; }
|
font-size: 12px; }
|
||||||
|
|
||||||
|
#corporation-research-popup-box-content {
|
||||||
|
overflow-x: visible !important; }
|
||||||
|
|
||||||
/* COLORS */
|
/* COLORS */
|
||||||
/* Attributes */
|
/* Attributes */
|
||||||
#bladeburner-container a,
|
#bladeburner-container a,
|
||||||
@ -2185,7 +2188,6 @@ button {
|
|||||||
|
|
||||||
.Treant > .researched {
|
.Treant > .researched {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
pointer-events: none;
|
|
||||||
font-size: 16px; }
|
font-size: 16px; }
|
||||||
|
|
||||||
.Treant > .locked > div {
|
.Treant > .locked > div {
|
||||||
|
@ -151,6 +151,22 @@ Terminal commands::
|
|||||||
$ download *.script
|
$ download *.script
|
||||||
$ download *.txt
|
$ download *.txt
|
||||||
|
|
||||||
|
expr
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
$ expr [math expression]
|
||||||
|
|
||||||
|
Evaluate a mathematical expression. The expression is evaluated in JavaScript,
|
||||||
|
and therefore all JavaScript operators should be supported.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
$ expr 5.6 * 10 - 123
|
||||||
|
$ expr 3 ** 3
|
||||||
|
|
||||||
|
|
||||||
|
Evalutes a
|
||||||
|
|
||||||
free
|
free
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
@ -74,6 +74,64 @@ weaken
|
|||||||
|
|
||||||
weaken("foodnstuff");
|
weaken("foodnstuff");
|
||||||
|
|
||||||
|
hackAnalyzeThreads
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. js:function:: hackAnalyzeThreads(hostname/ip, hackAmount)
|
||||||
|
|
||||||
|
:param string hostname/ip: IP or hostname of server to analyze
|
||||||
|
:param number hackAmount: Amount of money you want to hack from the server
|
||||||
|
:returns: The number of threads needed to hack() the server for *hackAmount* money
|
||||||
|
:RAM cost: 1 GB
|
||||||
|
|
||||||
|
This function returns the number of script threads you need when running
|
||||||
|
the `hack()` command to steal the specified amount of money from the target server.
|
||||||
|
|
||||||
|
If `hackAmount` is less than zero or greater than the amount of money available
|
||||||
|
on the server, then this function returns -1.
|
||||||
|
|
||||||
|
For example, let's say the `foodnstuff` server has $10m and you run::
|
||||||
|
|
||||||
|
hackAnalyzeThreads("foodnstuff", 1e6);
|
||||||
|
|
||||||
|
If this function returns 50, this means that if your next `hack()` call
|
||||||
|
is run on a script with 50 threads, it will steal $1m from the `foodnstuff` server.
|
||||||
|
|
||||||
|
**Warning**: The value returned by this function isn't necessarily a whole number.
|
||||||
|
|
||||||
|
hackAnalyzePercent
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. js:function:: hackAnalyzePercent(hostname/ip)
|
||||||
|
|
||||||
|
:param string hostname/ip: IP or hostname of target server
|
||||||
|
:returns: The percentage of money you will steal from the target server with a single hack
|
||||||
|
:RAM cost: 1 GB
|
||||||
|
|
||||||
|
Returns the percentage of the specified server's money you will steal with a
|
||||||
|
single hack. This value is returned in **percentage form, not decimal (Netscript
|
||||||
|
functions typically return in decimal form, but not this one).**
|
||||||
|
|
||||||
|
For example, assume the following returns 1::
|
||||||
|
|
||||||
|
hackAnalyzePercent("foodnstuff");
|
||||||
|
|
||||||
|
This means that if hack the `foodnstuff` server, then you will steal 1% of its
|
||||||
|
total money. If you `hack()` using N threads, then you will steal N% of its total
|
||||||
|
money.
|
||||||
|
|
||||||
|
hackChance
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
.. js:function:: hackChance(hostname/ip)
|
||||||
|
|
||||||
|
:param string hostname/ip: IP or hostname of target server
|
||||||
|
:returns: The chance you have of successfully hacking the target server
|
||||||
|
:RAM cost: 1 GB
|
||||||
|
|
||||||
|
Returns the chance you have of successfully hacking the specified server. This
|
||||||
|
returned value is in decimal form, not percentage.
|
||||||
|
|
||||||
growthAnalyze
|
growthAnalyze
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -31,6 +31,12 @@ purchase will have the name "hacknet-node-0" and is referenced using index 0.
|
|||||||
The fifth Hacknet Node you purchase will have the name "hacknet-node-4" and is
|
The fifth Hacknet Node you purchase will have the name "hacknet-node-4" and is
|
||||||
referenced using index 4.
|
referenced using index 4.
|
||||||
|
|
||||||
|
RAM Cost
|
||||||
|
--------
|
||||||
|
Accessing the `hacknet` namespace incurs a one time cost of 4 GB of RAM.
|
||||||
|
In other words, using multiple Hacknet Node API functions in a script will not cost
|
||||||
|
more than 4 GB of RAM.
|
||||||
|
|
||||||
numNodes
|
numNodes
|
||||||
--------
|
--------
|
||||||
.. js:function:: numNodes()
|
.. js:function:: numNodes()
|
||||||
@ -150,7 +156,7 @@ getCoreUpgradeCost
|
|||||||
Returns the cost of upgrading the number of cores of the specified Hacknet Node by *n*.
|
Returns the cost of upgrading the number of cores of the specified Hacknet Node by *n*.
|
||||||
|
|
||||||
If an invalid value for *n* is provided, then this function returns 0. If the
|
If an invalid value for *n* is provided, then this function returns 0. If the
|
||||||
specified Hacknet Node is already at the max number of cores, then Infinity is returned.
|
specified Hacknet Node is already at the max number of cores, then Infinity is returned.
|
||||||
|
|
||||||
Utilities
|
Utilities
|
||||||
---------
|
---------
|
||||||
|
@ -15,6 +15,8 @@ getStockSymbols
|
|||||||
|
|
||||||
.. js:function:: getStockSymbols()
|
.. js:function:: getStockSymbols()
|
||||||
|
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
Returns an array of the symbols of the tradable stocks
|
Returns an array of the symbols of the tradable stocks
|
||||||
|
|
||||||
getStockPrice
|
getStockPrice
|
||||||
@ -23,6 +25,7 @@ getStockPrice
|
|||||||
.. js:function:: getStockPrice(sym)
|
.. js:function:: getStockPrice(sym)
|
||||||
|
|
||||||
:param string sym: Stock symbol
|
:param string sym: Stock symbol
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
Returns the price of a stock, given its symbol (NOT the company name). The symbol is a sequence
|
Returns the price of a stock, given its symbol (NOT the company name). The symbol is a sequence
|
||||||
of two to four capital letters.
|
of two to four capital letters.
|
||||||
@ -37,6 +40,7 @@ getStockPosition
|
|||||||
.. js:function:: getStockPosition(sym)
|
.. js:function:: getStockPosition(sym)
|
||||||
|
|
||||||
:param string sym: Stock symbol
|
:param string sym: Stock symbol
|
||||||
|
:RAM cost: 2 GB
|
||||||
|
|
||||||
Returns an array of four elements that represents the player's position in a stock.
|
Returns an array of four elements that represents the player's position in a stock.
|
||||||
|
|
||||||
@ -65,6 +69,7 @@ buyStock
|
|||||||
|
|
||||||
:param string sym: Symbol of stock to purchase
|
:param string sym: Symbol of stock to purchase
|
||||||
:param number shares: Number of shares to purchased. Must be positive. Will be rounded to nearest integer
|
:param number shares: Number of shares to purchased. Must be positive. Will be rounded to nearest integer
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Attempts to purchase shares of a stock using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
Attempts to purchase shares of a stock using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
||||||
|
|
||||||
@ -81,6 +86,7 @@ sellStock
|
|||||||
|
|
||||||
:param string sym: Symbol of stock to sell
|
:param string sym: Symbol of stock to sell
|
||||||
:param number shares: Number of shares to sell. Must be positive. Will be rounded to nearest integer
|
:param number shares: Number of shares to sell. Must be positive. Will be rounded to nearest integer
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Attempts to sell shares of a stock using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
Attempts to sell shares of a stock using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
||||||
|
|
||||||
@ -101,6 +107,7 @@ shortStock
|
|||||||
|
|
||||||
:param string sym: Symbol of stock to short
|
:param string sym: Symbol of stock to short
|
||||||
:param number shares: Number of shares to short. Must be positive. Will be rounded to nearest integer
|
:param number shares: Number of shares to short. Must be positive. Will be rounded to nearest integer
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Attempts to purchase a `short <http://bitburner.wikia.com/wiki/Stock_Market#Positions:_Long_vs_Short>`_ position of a stock
|
Attempts to purchase a `short <http://bitburner.wikia.com/wiki/Stock_Market#Positions:_Long_vs_Short>`_ position of a stock
|
||||||
using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
||||||
@ -119,6 +126,7 @@ sellShort
|
|||||||
|
|
||||||
:param string sym: Symbol of stock to sell
|
:param string sym: Symbol of stock to sell
|
||||||
:param number shares: Number of shares to sell. Must be positive. Will be rounded to nearest integer
|
:param number shares: Number of shares to sell. Must be positive. Will be rounded to nearest integer
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Attempts to sell a `short <http://bitburner.wikia.com/wiki/Stock_Market#Positions:_Long_vs_Short>`_ position of a stock
|
Attempts to sell a `short <http://bitburner.wikia.com/wiki/Stock_Market#Positions:_Long_vs_Short>`_ position of a stock
|
||||||
using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
using a `Market Order <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
||||||
@ -149,6 +157,7 @@ placeOrder
|
|||||||
:param string pos:
|
:param string pos:
|
||||||
Specifies whether the order is a "Long" or "Short" position. The Values "L" or "S" can also be used. This is
|
Specifies whether the order is a "Long" or "Short" position. The Values "L" or "S" can also be used. This is
|
||||||
NOT case-sensitive.
|
NOT case-sensitive.
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Places an order on the stock market. This function only works for `Limit and Stop Orders <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
Places an order on the stock market. This function only works for `Limit and Stop Orders <http://bitburner.wikia.com/wiki/Stock_Market#Order_Types>`_.
|
||||||
|
|
||||||
@ -175,17 +184,88 @@ cancelOrder
|
|||||||
:param string pos:
|
:param string pos:
|
||||||
Specifies whether the order is a "Long" or "Short" position. The Values "L" or "S" can also be used. This is
|
Specifies whether the order is a "Long" or "Short" position. The Values "L" or "S" can also be used. This is
|
||||||
NOT case-sensitive.
|
NOT case-sensitive.
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Cancels an oustanding Limit or Stop order on the stock market.
|
Cancels an oustanding Limit or Stop order on the stock market.
|
||||||
|
|
||||||
The ability to use limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
|
The ability to use limit and stop orders is **not** immediately available to the player and must be unlocked later on in the game.
|
||||||
|
|
||||||
|
getOrders
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. js:function:: getOrders()
|
||||||
|
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
|
Returns your order book for the stock market. This is an object containing information
|
||||||
|
for all the Limit and Stop Orders you have in the stock market.
|
||||||
|
|
||||||
|
The object has the following structure::
|
||||||
|
|
||||||
|
{
|
||||||
|
StockSymbol1: [ // Array of orders for this stock
|
||||||
|
{
|
||||||
|
shares: Order quantity
|
||||||
|
price: Order price
|
||||||
|
type: Order type
|
||||||
|
position: Either "L" or "S" for Long or Short position
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
StockSymbol2: [ // Array of orders for this stock
|
||||||
|
...
|
||||||
|
],
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
The "Order type" property can have one of the following four values:
|
||||||
|
|
||||||
|
* "Limit Buy Order"
|
||||||
|
* "Limit Sell Order"
|
||||||
|
* "Stop Buy Order"
|
||||||
|
* "Stop Sell Order"
|
||||||
|
|
||||||
|
**Note that the order book will only contain information for stocks that you actually
|
||||||
|
have orders in**. For example, if you do not have orders in Nova Medical (NVMD), then the returned
|
||||||
|
object will not have a "NVMD" property.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
{
|
||||||
|
ECP: [
|
||||||
|
{
|
||||||
|
shares: 5,
|
||||||
|
price: 100,000
|
||||||
|
type: "Stop Buy Order",
|
||||||
|
position: "S",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
shares: 25,
|
||||||
|
price: 125,000
|
||||||
|
type: "Limit Sell Order",
|
||||||
|
position: "L",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
SYSC: [
|
||||||
|
{
|
||||||
|
shares: 100,
|
||||||
|
price: 10,000
|
||||||
|
type: "Limit Buy Order",
|
||||||
|
position: "L",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
getStockVolatility
|
getStockVolatility
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
.. js:function:: getStockVolatility(sym)
|
.. js:function:: getStockVolatility(sym)
|
||||||
|
|
||||||
:param string sym: Symbol of stock
|
:param string sym: Symbol of stock
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Returns the volatility of the specified stock.
|
Returns the volatility of the specified stock.
|
||||||
|
|
||||||
@ -203,6 +283,7 @@ getStockForecast
|
|||||||
.. js:function:: getStockForecast(sym)
|
.. js:function:: getStockForecast(sym)
|
||||||
|
|
||||||
:param string sym: Symbol of stock
|
:param string sym: Symbol of stock
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Returns the probability that the specified stock's price will increase
|
Returns the probability that the specified stock's price will increase
|
||||||
(as opposed to decrease) during the next tick.
|
(as opposed to decrease) during the next tick.
|
||||||
@ -220,6 +301,8 @@ purchase4SMarketData
|
|||||||
|
|
||||||
.. js:function:: purchase4SMarketData()
|
.. js:function:: purchase4SMarketData()
|
||||||
|
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Purchase 4S Market Data Access.
|
Purchase 4S Market Data Access.
|
||||||
|
|
||||||
Returns true if you successfully purchased it or if you already have access.
|
Returns true if you successfully purchased it or if you already have access.
|
||||||
@ -230,6 +313,8 @@ purchase4SMarketDataTixApi
|
|||||||
|
|
||||||
.. js:function:: purchase4SMarketDataTixApi()
|
.. js:function:: purchase4SMarketDataTixApi()
|
||||||
|
|
||||||
|
:RAM cost: 2.5 GB
|
||||||
|
|
||||||
Purchase 4S Market Data TIX API Access.
|
Purchase 4S Market Data TIX API Access.
|
||||||
|
|
||||||
Returns true if you successfully purchased it or if you already have access.
|
Returns true if you successfully purchased it or if you already have access.
|
||||||
|
@ -59,7 +59,8 @@ var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules;
|
|||||||
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*";
|
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*";
|
||||||
|
|
||||||
let NetscriptFunctions =
|
let NetscriptFunctions =
|
||||||
"hack|sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" +
|
"hack|hackAnalyzeThreads|hackAnalyzePercent|hackChance|" +
|
||||||
|
"sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" +
|
||||||
"ftpcrack|" +
|
"ftpcrack|" +
|
||||||
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
|
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
|
||||||
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
|
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||||
import { CONSTANTS } from "./Constants";
|
import { CONSTANTS } from "./Constants";
|
||||||
import { Engine } from "./engine";
|
|
||||||
import { Factions,
|
import { Factions,
|
||||||
factionExists } from "./Faction/Factions";
|
factionExists } from "./Faction/Factions";
|
||||||
import { hasBladeburnerSF } from "./NetscriptFunctions";
|
import { hasBladeburnerSF } from "./NetscriptFunctions";
|
||||||
@ -18,6 +17,7 @@ import { dialogBoxCreate } from "../utils/DialogBox";
|
|||||||
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
||||||
import { Reviver, Generic_toJSON,
|
import { Reviver, Generic_toJSON,
|
||||||
Generic_fromJSON } from "../utils/JSONReviver";
|
Generic_fromJSON } from "../utils/JSONReviver";
|
||||||
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
import { clearObject } from "../utils/helpers/clearObject";
|
import { clearObject } from "../utils/helpers/clearObject";
|
||||||
import { createElement } from "../utils/uiHelpers/createElement";
|
import { createElement } from "../utils/uiHelpers/createElement";
|
||||||
import { isString } from "../utils/helpers/isString";
|
import { isString } from "../utils/helpers/isString";
|
||||||
@ -2475,9 +2475,9 @@ function giveAllAugmentations() {
|
|||||||
Player.reapplyAllAugmentations();
|
Player.reapplyAllAugmentations();
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayAugmentationsContent() {
|
function displayAugmentationsContent(contentEl) {
|
||||||
removeChildrenFromElement(Engine.Display.augmentationsContent);
|
removeChildrenFromElement(contentEl);
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
|
contentEl.appendChild(createElement("h1", {
|
||||||
innerText:"Purchased Augmentations",
|
innerText:"Purchased Augmentations",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -2487,7 +2487,7 @@ function displayAugmentationsContent() {
|
|||||||
bladeburnerText = "Bladeburner Progress\n\n";
|
bladeburnerText = "Bladeburner Progress\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("pre", {
|
contentEl.appendChild(createElement("pre", {
|
||||||
width:"70%", whiteSpace:"pre-wrap", display:"block",
|
width:"70%", whiteSpace:"pre-wrap", display:"block",
|
||||||
innerText:"Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.\n" +
|
innerText:"Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.\n" +
|
||||||
"WARNING: Installing your Augmentations resets most of your progress, including:\n\n" +
|
"WARNING: Installing your Augmentations resets most of your progress, including:\n\n" +
|
||||||
@ -2505,7 +2505,7 @@ function displayAugmentationsContent() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
//Install Augmentations button
|
//Install Augmentations button
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("a", {
|
contentEl.appendChild(createElement("a", {
|
||||||
class:"a-link-button", innerText:"Install Augmentations",
|
class:"a-link-button", innerText:"Install Augmentations",
|
||||||
tooltip:"'I never asked for this'",
|
tooltip:"'I never asked for this'",
|
||||||
clickListener:()=>{
|
clickListener:()=>{
|
||||||
@ -2515,7 +2515,7 @@ function displayAugmentationsContent() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
//Backup button
|
//Backup button
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("a", {
|
contentEl.appendChild(createElement("a", {
|
||||||
class:"a-link-button flashing-button", innerText:"Backup Save (Export)",
|
class:"a-link-button flashing-button", innerText:"Backup Save (Export)",
|
||||||
tooltip:"It's always a good idea to backup/export your save!",
|
tooltip:"It's always a good idea to backup/export your save!",
|
||||||
clickListener:()=>{
|
clickListener:()=>{
|
||||||
@ -2539,13 +2539,13 @@ function displayAugmentationsContent() {
|
|||||||
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
|
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
|
||||||
queuedAugmentationsList.appendChild(accordion[0]);
|
queuedAugmentationsList.appendChild(accordion[0]);
|
||||||
}
|
}
|
||||||
Engine.Display.augmentationsContent.appendChild(queuedAugmentationsList);
|
contentEl.appendChild(queuedAugmentationsList);
|
||||||
|
|
||||||
//Installed augmentations list
|
//Installed augmentations list
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
|
contentEl.appendChild(createElement("h1", {
|
||||||
innerText:"Installed Augmentations", marginTop:"8px",
|
innerText:"Installed Augmentations", marginTop:"8px",
|
||||||
}));
|
}));
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("p", {
|
contentEl.appendChild(createElement("p", {
|
||||||
width:"70%", whiteSpace:"pre-wrap",
|
width:"70%", whiteSpace:"pre-wrap",
|
||||||
innerText:"List of all Augmentations (including Source Files) that have been " +
|
innerText:"List of all Augmentations (including Source Files) that have been " +
|
||||||
"installed. You have gained the effects of these Augmentations."
|
"installed. You have gained the effects of these Augmentations."
|
||||||
@ -2554,7 +2554,7 @@ function displayAugmentationsContent() {
|
|||||||
var augmentationsList = createElement("ul", {class:"augmentations-list"});
|
var augmentationsList = createElement("ul", {class:"augmentations-list"});
|
||||||
|
|
||||||
//Expand/Collapse All buttons
|
//Expand/Collapse All buttons
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("a", {
|
contentEl.appendChild(createElement("a", {
|
||||||
class:"a-link-button", fontSize:"14px", innerText:"Expand All", display:"inline-block",
|
class:"a-link-button", fontSize:"14px", innerText:"Expand All", display:"inline-block",
|
||||||
clickListener:()=>{
|
clickListener:()=>{
|
||||||
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
||||||
@ -2563,7 +2563,7 @@ function displayAugmentationsContent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Engine.Display.augmentationsContent.appendChild(createElement("a", {
|
contentEl.appendChild(createElement("a", {
|
||||||
class:"a-link-button", fontSize:"14px", innerText:"Collapse All", display:"inline-block",
|
class:"a-link-button", fontSize:"14px", innerText:"Collapse All", display:"inline-block",
|
||||||
clickListener:()=>{
|
clickListener:()=>{
|
||||||
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
||||||
@ -2595,7 +2595,7 @@ function displayAugmentationsContent() {
|
|||||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
|
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Engine.Display.augmentationsContent.appendChild(sortInOrderButton);
|
contentEl.appendChild(sortInOrderButton);
|
||||||
|
|
||||||
const sortByAcquirementTimeButton = createElement("a", {
|
const sortByAcquirementTimeButton = createElement("a", {
|
||||||
class:"a-link-button", fontSize:"14px", innerText:"Sort by Acquirement Time",
|
class:"a-link-button", fontSize:"14px", innerText:"Sort by Acquirement Time",
|
||||||
@ -2608,15 +2608,47 @@ function displayAugmentationsContent() {
|
|||||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
|
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Engine.Display.augmentationsContent.appendChild(sortByAcquirementTimeButton);
|
contentEl.appendChild(sortByAcquirementTimeButton);
|
||||||
|
|
||||||
//Source Files - Temporary...Will probably put in a separate pane Later
|
|
||||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
sortInOrderButton.click();
|
sortInOrderButton.click();
|
||||||
} else {
|
} else {
|
||||||
sortByAcquirementTimeButton.click();
|
sortByAcquirementTimeButton.click();
|
||||||
}
|
}
|
||||||
Engine.Display.augmentationsContent.appendChild(augmentationsList);
|
contentEl.appendChild(augmentationsList);
|
||||||
|
|
||||||
|
// Display multiplier information at the bottom
|
||||||
|
contentEl.appendChild(createElement("p", {
|
||||||
|
display: "block",
|
||||||
|
innerHTML:
|
||||||
|
`<br><br><strong><u>Total Multipliers:</u></strong><br>` +
|
||||||
|
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacking Money multiplier: ' + formatNumber(Player.hacking_money_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacking Growth multiplier: ' + formatNumber(Player.hacking_grow_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Hacking Level multiplier: ' + formatNumber(Player.hacking_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacking Experience multiplier: ' + formatNumber(Player.hacking_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Strength Level multiplier: ' + formatNumber(Player.strength_mult * 100, 2) + '%<br>' +
|
||||||
|
'Strength Experience multiplier: ' + formatNumber(Player.strength_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Defense Level multiplier: ' + formatNumber(Player.defense_mult * 100, 2) + '%<br>' +
|
||||||
|
'Defense Experience multiplier: ' + formatNumber(Player.defense_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Dexterity Level multiplier: ' + formatNumber(Player.dexterity_mult * 100, 2) + '%<br>' +
|
||||||
|
'Dexterity Experience multiplier: ' + formatNumber(Player.dexterity_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Agility Level multiplier: ' + formatNumber(Player.agility_mult * 100, 2) + '%<br>' +
|
||||||
|
'Agility Experience multiplier: ' + formatNumber(Player.agility_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Charisma Level multiplier: ' + formatNumber(Player.charisma_mult * 100, 2) + '%<br>' +
|
||||||
|
'Charisma Experience multiplier: ' + formatNumber(Player.charisma_exp_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Hacknet Node production multiplier: ' + formatNumber(Player.hacknet_node_money_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacknet Node purchase cost multiplier: ' + formatNumber(Player.hacknet_node_purchase_cost_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacknet Node RAM upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_ram_cost_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacknet Node Core purchase cost multiplier: ' + formatNumber(Player.hacknet_node_core_cost_mult * 100, 2) + '%<br>' +
|
||||||
|
'Hacknet Node level upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_level_cost_mult * 100, 2) + '%<br><br>' +
|
||||||
|
'Company reputation gain multiplier: ' + formatNumber(Player.company_rep_mult * 100, 2) + '%<br>' +
|
||||||
|
'Faction reputation gain multiplier: ' + formatNumber(Player.faction_rep_mult * 100, 2) + '%<br>' +
|
||||||
|
'Salary multiplier: ' + formatNumber(Player.work_money_mult * 100, 2) + '%<br>' +
|
||||||
|
'Crime success multiplier: ' + formatNumber(Player.crime_success_mult * 100, 2) + '%<br>' +
|
||||||
|
'Crime money multiplier: ' + formatNumber(Player.crime_money_mult * 100, 2) + '%<br><br><br>',
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
//Creates the accordion elements to display Augmentations
|
//Creates the accordion elements to display Augmentations
|
||||||
|
@ -55,6 +55,7 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
ScriptForRamCost: 0,
|
ScriptForRamCost: 0,
|
||||||
ScriptIfRamCost: 0,
|
ScriptIfRamCost: 0,
|
||||||
ScriptHackRamCost: 0.1,
|
ScriptHackRamCost: 0.1,
|
||||||
|
ScriptHackAnalyzeRamCost: 1,
|
||||||
ScriptGrowRamCost: 0.15,
|
ScriptGrowRamCost: 0.15,
|
||||||
ScriptGrowthAnalyzeRamCost: 1,
|
ScriptGrowthAnalyzeRamCost: 1,
|
||||||
ScriptWeakenRamCost: 0.15,
|
ScriptWeakenRamCost: 0.15,
|
||||||
@ -520,11 +521,16 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
|
|
||||||
* Added getOrders() Netscript function to the TIX API
|
* Added getOrders() Netscript function to the TIX API
|
||||||
* Added getAugmentationPrereq() Singularity function (by havocmayhem)
|
* Added getAugmentationPrereq() Singularity function (by havocmayhem)
|
||||||
|
* Added hackAnalyzePercent() and hackAnalyzeThreads() Netscript functions
|
||||||
* Stock Market, Travel, and Corporation main menu links are now properly styled
|
* Stock Market, Travel, and Corporation main menu links are now properly styled
|
||||||
* Many pop-up/dialog boxes now support the 'Enter' and 'Esc' hotkeys. If you find a pop-up/dialog box that doesnt support this, let me know specifically which one ('Enter' for the default option, 'Esc' for cancelling and closing the pop-up box)
|
* Many pop-up/dialog boxes now support the 'Enter' and 'Esc' hotkeys. If you find a pop-up/dialog box that doesnt support this, let me know specifically which one ('Enter' for the default option, 'Esc' for cancelling and closing the pop-up box)
|
||||||
* Added "brace_style = preserve_inline" configuration to Script Editor Beautifier
|
* Added "brace_style = preserve_inline" configuration to Script Editor Beautifier
|
||||||
* ServerProfiler.exe can now be purchased from the Dark Web
|
* ServerProfiler.exe can now be purchased from the Dark Web
|
||||||
* Added an option to copy save data to clipboard
|
* Added an option to copy save data to clipboard
|
||||||
|
* Added total multiplier information on the "Augmentations" page
|
||||||
|
* Bug Fix: gymWorkout() Singularity function should now work properly with Millenium Fitness Gym
|
||||||
|
* Began migrating gameplay information to the ReadTheDocs documentation
|
||||||
|
*
|
||||||
`
|
`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1265,6 +1265,11 @@ Industry.prototype.createResearchBox = function() {
|
|||||||
// Add Event Listeners for all Nodes
|
// Add Event Listeners for all Nodes
|
||||||
const allResearch = researchTree.getAllNodes();
|
const allResearch = researchTree.getAllNodes();
|
||||||
for (let i = 0; i < allResearch.length; ++i) {
|
for (let i = 0; i < allResearch.length; ++i) {
|
||||||
|
// If this is already Researched, skip it
|
||||||
|
if (this.researched[allResearch[i]] === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the Research object
|
// Get the Research object
|
||||||
const research = ResearchMap[allResearch[i]];
|
const research = ResearchMap[allResearch[i]];
|
||||||
|
|
||||||
@ -3842,11 +3847,12 @@ Corporation.prototype.updateCorporationOverviewContent = function() {
|
|||||||
const dividendsPerShare = totalDividends / TOTALSHARES;
|
const dividendsPerShare = totalDividends / TOTALSHARES;
|
||||||
const playerEarnings = this.numShares * dividendsPerShare;
|
const playerEarnings = this.numShares * dividendsPerShare;
|
||||||
|
|
||||||
dividendStr = `Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s<br>` +
|
dividendStr = `Dividend Percentage: ${numeralWrapper.format(this.dividendPercentage / 100, "0%")}<br>` +
|
||||||
|
`Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s<br>` +
|
||||||
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
||||||
`Your earnings (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
||||||
`Dividend Tax Rate: ${this.dividendTaxPercentage}%<br>` +
|
`Dividend Tax Rate: ${this.dividendTaxPercentage}%<br>` +
|
||||||
`Your earnings (Post-Tax): ${numeralWrapper.format(playerEarnings * (this.dividendTaxPercentage / 100), "$0.000a")} / s<br>`;
|
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (this.dividendTaxPercentage / 100), "$0.000a")} / s<br>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
var txt = "Total Funds: " + numeralWrapper.format(this.funds.toNumber(), '$0.000a') + "<br>" +
|
var txt = "Total Funds: " + numeralWrapper.format(this.funds.toNumber(), '$0.000a') + "<br>" +
|
||||||
@ -4186,9 +4192,6 @@ Corporation.prototype.displayDivisionContent = function(division, city) {
|
|||||||
id: "cmpy-mgmt-employee-p",
|
id: "cmpy-mgmt-employee-p",
|
||||||
display:"block",
|
display:"block",
|
||||||
innerHTML: "<h1>Office Space</h1><br>" +
|
innerHTML: "<h1>Office Space</h1><br>" +
|
||||||
"Type: " + office.tier + "<br>" +
|
|
||||||
"Comfort: " + office.comf + "<br>" +
|
|
||||||
"Beauty: " + office.beau + "<br>" +
|
|
||||||
"Size: " + office.employees.length + " / " + office.size + " employees",
|
"Size: " + office.employees.length + " / " + office.size + " employees",
|
||||||
});
|
});
|
||||||
industryEmployeePanel.appendChild(industryEmployeeText);
|
industryEmployeePanel.appendChild(industryEmployeeText);
|
||||||
@ -4640,9 +4643,6 @@ Corporation.prototype.updateDivisionContent = function(division) {
|
|||||||
var office = division.offices[currentCityUi];
|
var office = division.offices[currentCityUi];
|
||||||
industryEmployeeText.innerHTML =
|
industryEmployeeText.innerHTML =
|
||||||
"<h1>Office Space</h1><br>" +
|
"<h1>Office Space</h1><br>" +
|
||||||
"Type: " + office.tier + "<br>" +
|
|
||||||
"Comfort: " + office.comf + "<br>" +
|
|
||||||
"Beauty: " + office.beau + "<br>" +
|
|
||||||
"Size: " + office.employees.length + " / " + office.size + " employees";
|
"Size: " + office.employees.length + " / " + office.size + " employees";
|
||||||
if (office.employees.length >= office.size) {
|
if (office.employees.length >= office.size) {
|
||||||
industryEmployeeHireButton.className = "a-link-button-inactive";
|
industryEmployeeHireButton.className = "a-link-button-inactive";
|
||||||
@ -4657,12 +4657,13 @@ Corporation.prototype.updateDivisionContent = function(division) {
|
|||||||
|
|
||||||
//Employee Overview stats
|
//Employee Overview stats
|
||||||
//Calculate average morale, happiness, and energy
|
//Calculate average morale, happiness, and energy
|
||||||
var totalMorale = 0, totalHappiness = 0, totalEnergy = 0,
|
var totalMorale = 0, totalHappiness = 0, totalEnergy = 0, totalSalary = 0,
|
||||||
avgMorale = 0, avgHappiness = 0, avgEnergy = 0;
|
avgMorale = 0, avgHappiness = 0, avgEnergy = 0;
|
||||||
for (var i = 0; i < office.employees.length; ++i) {
|
for (let i = 0; i < office.employees.length; ++i) {
|
||||||
totalMorale += office.employees[i].mor;
|
totalMorale += office.employees[i].mor;
|
||||||
totalHappiness += office.employees[i].hap;
|
totalHappiness += office.employees[i].hap;
|
||||||
totalEnergy += office.employees[i].ene;
|
totalEnergy += office.employees[i].ene;
|
||||||
|
totalSalary += office.employees[i].sal;
|
||||||
}
|
}
|
||||||
if (office.employees.length > 0) {
|
if (office.employees.length > 0) {
|
||||||
avgMorale = totalMorale / office.employees.length;
|
avgMorale = totalMorale / office.employees.length;
|
||||||
@ -4672,7 +4673,8 @@ Corporation.prototype.updateDivisionContent = function(division) {
|
|||||||
industryEmployeeInfo.innerHTML =
|
industryEmployeeInfo.innerHTML =
|
||||||
"Avg Employee Morale: " + formatNumber(avgMorale, 3) + "<br>" +
|
"Avg Employee Morale: " + formatNumber(avgMorale, 3) + "<br>" +
|
||||||
"Avg Employee Happiness: " + formatNumber(avgHappiness, 3) + "<br>" +
|
"Avg Employee Happiness: " + formatNumber(avgHappiness, 3) + "<br>" +
|
||||||
"Avg Employee Energy: " + formatNumber(avgEnergy, 3);
|
"Avg Employee Energy: " + formatNumber(avgEnergy, 3) + "<br>" +
|
||||||
|
"Total Employee Salary: " + numeralWrapper.format(totalSalary, "$0.000a");
|
||||||
if (vechain) { //VeChain - Statistics
|
if (vechain) { //VeChain - Statistics
|
||||||
industryEmployeeInfo.appendChild(createElement("br", {}));
|
industryEmployeeInfo.appendChild(createElement("br", {}));
|
||||||
industryEmployeeInfo.appendChild(createElement("p", {
|
industryEmployeeInfo.appendChild(createElement("p", {
|
||||||
|
@ -408,6 +408,46 @@ function NetscriptFunctions(workerScript) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
hackAnalyzeThreads : function(ip, hackAmount) {
|
||||||
|
if (workerScript.checkingRam) {
|
||||||
|
return updateStaticRam("hackAnalyzeThreads", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
}
|
||||||
|
updateDynamicRam("hackAnalyzeThreads", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
|
||||||
|
// Check argument validity
|
||||||
|
const server = safeGetServer(ip, 'hackAnalyzeThreads');
|
||||||
|
if (isNaN(hackAmount)) {
|
||||||
|
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${hackAmount}. Must be numeric`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hackAmount < 0 || hackAmount > server.moneyAvailable) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const percentHacked = calculatePercentMoneyHacked(server);
|
||||||
|
|
||||||
|
return hackAmount / Math.floor(server.moneyAvailable * percentHacked);
|
||||||
|
},
|
||||||
|
hackAnalyzePercent : function(ip) {
|
||||||
|
if (workerScript.checkingRam) {
|
||||||
|
return updateStaticRam("hackAnalyzePercent", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
}
|
||||||
|
updateDynamicRam("hackAnalyzePercent", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
|
||||||
|
const server = safeGetServer(ip, 'hackAnalyzePercent');
|
||||||
|
|
||||||
|
return calculatePercentMoneyHacked(server) * 100;
|
||||||
|
},
|
||||||
|
hackChance : function(ip) {
|
||||||
|
if (workerScript.checkingRam) {
|
||||||
|
return updateStaticRam("hackChance", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
}
|
||||||
|
updateDynamicRam("hackChance", CONSTANTS.ScriptHackAnalyzeRamCost);
|
||||||
|
|
||||||
|
const server = safeGetServer(ip, 'hackChance');
|
||||||
|
|
||||||
|
return calculateHackingChance(server);
|
||||||
|
},
|
||||||
sleep : function(time){
|
sleep : function(time){
|
||||||
if (workerScript.checkingRam) {return 0;}
|
if (workerScript.checkingRam) {return 0;}
|
||||||
if (time === undefined) {
|
if (time === undefined) {
|
||||||
@ -2626,7 +2666,7 @@ function NetscriptFunctions(workerScript) {
|
|||||||
costMult = 20;
|
costMult = 20;
|
||||||
expMult = 10;
|
expMult = 10;
|
||||||
break;
|
break;
|
||||||
case Locations.VolhavenMilleniumFitnessGym:
|
case Locations.VolhavenMilleniumFitnessGym.toLowerCase():
|
||||||
if (Player.city != Locations.Volhaven) {
|
if (Player.city != Locations.Volhaven) {
|
||||||
workerScript.scriptRef.log("ERROR: You cannot workout at Millenium Fitness Gym because you are not in Volhaven. gymWorkout() failed");
|
workerScript.scriptRef.log("ERROR: You cannot workout at Millenium Fitness Gym because you are not in Volhaven. gymWorkout() failed");
|
||||||
return false;
|
return false;
|
||||||
|
@ -325,7 +325,7 @@ const Engine = {
|
|||||||
loadAugmentationsContent: function() {
|
loadAugmentationsContent: function() {
|
||||||
Engine.hideAllContent();
|
Engine.hideAllContent();
|
||||||
Engine.Display.augmentationsContent.style.display = "block";
|
Engine.Display.augmentationsContent.style.display = "block";
|
||||||
displayAugmentationsContent();
|
displayAugmentationsContent(Engine.Display.augmentationsContent);
|
||||||
routing.navigateTo(Page.Augmentations);
|
routing.navigateTo(Page.Augmentations);
|
||||||
document.getElementById("augmentations-menu-link").classList.add("active");
|
document.getElementById("augmentations-menu-link").classList.add("active");
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user