mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 05:33:54 +01:00
merge dev
This commit is contained in:
commit
080b5a4a1f
@ -106,6 +106,21 @@ Fork and clone the repo
|
||||
# Makes sure you always start from `danielyxie/dev` to avoid merge conflicts.
|
||||
```
|
||||
|
||||
### Running locally.
|
||||
|
||||
Install
|
||||
|
||||
- `npm` (maybe via `nvm`)
|
||||
- Github Desktop (windows only)
|
||||
- Visual Studio code (optional)
|
||||
|
||||
Inside the root of the repo run
|
||||
`npm install` to install all the dependencies
|
||||
`npm run start:dev` to launch the game in dev mode.
|
||||
|
||||
After that you can open any browser and naviguate to `localhost:8000` and play the game.
|
||||
Saving a file will reload the game automatically.
|
||||
|
||||
#### Submitting a Pull Request
|
||||
|
||||
When submitting a pull request with your code contributions, please abide by
|
||||
|
64
dist/vendor.bundle.js
vendored
64
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -27,6 +27,10 @@ to attempt infiltrations above mid-normal.
|
||||
** Slash when his guard is down! **
|
||||
|
||||
Press space when the guard is attacking you.
|
||||
There's 3 phase
|
||||
The first is guarding, where attacking back will result in failure.
|
||||
The 2nd is preparing, this informs you that in 250ms there will be an opening window to attack.
|
||||
The 3rd is attack, during this phase you can press space to slash and kill the enemy.
|
||||
|
||||
** Close the brackets **
|
||||
|
||||
|
35
doc/source/netscript/advancedfunctions/inject_html.rst
Normal file
35
doc/source/netscript/advancedfunctions/inject_html.rst
Normal file
@ -0,0 +1,35 @@
|
||||
Injecting HTML in the game
|
||||
==========================
|
||||
|
||||
Bitburner uses React and Material-UI to render everything. Modifying the UI is possible but
|
||||
not officially supported.
|
||||
|
||||
To automatically enter commands in the terminal (only works if looking at the terminal):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// Acquire a reference to the terminal text field
|
||||
const terminalInput = document.getElementById("terminal-input");
|
||||
|
||||
// Set the value to the command you want to run.
|
||||
terminalInput.value="home;connect n00dles;home;connect n00dles;home;";
|
||||
|
||||
// Get a reference to the React event handler.
|
||||
const handler = Object.keys(terminalInput)[1];
|
||||
|
||||
// Perform an onChange event to set some internal values.
|
||||
terminalInput[handler].onChange({target:terminalInput});
|
||||
|
||||
// Simulate an enter press
|
||||
terminalInput[handler].onKeyDown({keyCode:13,preventDefault:()=>null});
|
||||
|
||||
|
||||
To add lines to the terminal (only works if looking at the terminal):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// Acquire a reference to the terminal list of lines.
|
||||
const list = document.getElementById("generic-react-container").querySelector("ul");
|
||||
|
||||
// Inject some HTML.
|
||||
list.insertAdjacentHTML('beforeend',`<li><p color=lime>whatever custom html</p></li>`)
|
@ -7,7 +7,7 @@ toast() Netscript Function
|
||||
:param string message: message to display
|
||||
:param success|info|warning|error variant: color of the toast
|
||||
|
||||
Spawns a toast (those bottom right notifications).
|
||||
Spawns a toast (those bottom right notifications, like "Game Saved!" ).
|
||||
|
||||
Example:
|
||||
|
||||
|
15
doc/source/netscript/bladeburnerapi/getCityCommunities.rst
Normal file
15
doc/source/netscript/bladeburnerapi/getCityCommunities.rst
Normal file
@ -0,0 +1,15 @@
|
||||
getCityCommunities() Netscript Function
|
||||
================================================
|
||||
|
||||
.. js:function:: getCityCommunities(cityName)
|
||||
|
||||
:RAM cost: 4 GB
|
||||
:param string cityName: Name of city. Case-sensitive
|
||||
:returns: Confirmed number of Synthoid communities in the specified city,
|
||||
or -1 if an invalid city was specified.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
bladeburner.getCityCommunities("Sector-12"); // returns: 76
|
@ -1,15 +0,0 @@
|
||||
getCityEstimatedCommunities() Netscript Function
|
||||
================================================
|
||||
|
||||
.. js:function:: getCityEstimatedCommunities(cityName)
|
||||
|
||||
:RAM cost: 4 GB
|
||||
:param string cityName: Name of city. Case-sensitive
|
||||
:returns: Estimated number of Synthoid communities in the specified city,
|
||||
or -1 if an invalid city was specified.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
bladeburner.getCityEstimatedCommunities("Sector-12"); // returns: 76
|
@ -11,3 +11,5 @@ they contain spoilers for the game.
|
||||
getBitNodeMultipliers() <advancedfunctions/getBitNodeMultipliers>
|
||||
getServer() <advancedfunctions/getServer>
|
||||
autocomplete() <advancedfunctions/autocomplete>
|
||||
atExit() <advancedfunctions/atExit>
|
||||
Injecting HTML <advancedfunctions/inject_html.rst>
|
||||
|
@ -17,7 +17,7 @@ getInformation() Netscript Function
|
||||
maxHp: max hp of the sleeve,
|
||||
jobs: jobs available to the sleeve,
|
||||
jobTitle: job titles available to the sleeve,
|
||||
tor: does this sleeve have access to the tor router,
|
||||
|
||||
mult: {
|
||||
agility: agility multiplier,
|
||||
agilityExp: agility exp multiplier,
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
package-lock.json
generated
1
package-lock.json
generated
@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "0.56.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
|
@ -17,7 +17,6 @@
|
||||
"@mui/material": "^5.0.3",
|
||||
"@mui/styles": "^5.0.1",
|
||||
"@types/escodegen": "^0.0.7",
|
||||
"@types/js-beautify": "^1.13.2",
|
||||
"@types/numeral": "0.0.25",
|
||||
"@types/react": "^17.0.21",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
@ -64,7 +63,6 @@
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"http-server": "^13.0.1",
|
||||
"jest": "^27.1.0",
|
||||
"js-beautify": "^1.5.10",
|
||||
"jsdom": "^15.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
|
@ -32,7 +32,7 @@ export function SourceFileMinus1(): React.ReactElement {
|
||||
<Typography style={{ whiteSpace: "pre-wrap" }}>
|
||||
Source-File -1: Exploits in the BitNodes
|
||||
<br />
|
||||
Level {exploits.length} / ?
|
||||
Level {exploits.length} / {Object.keys(Exploit).length}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
|
@ -598,10 +598,9 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.InfiltrationMoney = 3;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 0;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.4;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -6;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
break;
|
||||
case 3: // Corporatocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.8;
|
||||
@ -617,10 +616,10 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.HacknetNodeMoney = 0.25;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 3;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.4;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -2;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||
break;
|
||||
case 4: // The Singularity
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||
@ -635,9 +634,9 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.HackExpGain = 0.4;
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||
break;
|
||||
case 5: // Artificial intelligence
|
||||
BitNodeMultipliers.ServerMaxMoney = 2;
|
||||
@ -651,9 +650,9 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.3;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||
break;
|
||||
case 6: // Bladeburner
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.35;
|
||||
@ -669,10 +668,10 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
BitNodeMultipliers.HackExpGain = 0.25;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
BitNodeMultipliers.GangKarmaRequirement = 5;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||
break;
|
||||
case 7: // Bladeburner 2079
|
||||
BitNodeMultipliers.BladeburnerRank = 0.6;
|
||||
@ -693,10 +692,10 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 2;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
BitNodeMultipliers.GangKarmaRequirement = 5;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.9;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -1;
|
||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||
break;
|
||||
case 8: // Ghost of Wall Street
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.3;
|
||||
@ -709,9 +708,9 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.RepToDonateToFaction = 0;
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 10;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -7;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 4;
|
||||
BitNodeMultipliers.GangSoftcap = 0;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
|
||||
@ -733,9 +732,9 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 3;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||
BitNodeMultipliers.GangSoftcap = 0.8;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
@ -759,10 +758,10 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0.6;
|
||||
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 3;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -3;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.1;
|
||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
|
||||
@ -781,7 +780,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.CodingContractMoney = 0.25;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2.2;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||
break;
|
||||
case 12: {
|
||||
//The Recursion
|
||||
@ -853,6 +852,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = inc;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = inc;
|
||||
BitNodeMultipliers.GangSoftcap = dec;
|
||||
break;
|
||||
}
|
||||
case 13: {
|
||||
@ -893,7 +893,6 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
|
||||
BitNodeMultipliers.BladeburnerRank = 0.1;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 5;
|
||||
BitNodeMultipliers.GangKarmaRequirement = 20;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 1;
|
||||
break;
|
||||
|
@ -110,9 +110,9 @@ interface IBitNodeMultipliers {
|
||||
FourSigmaMarketDataCost: number;
|
||||
|
||||
/**
|
||||
* Influences how much negative karma is required to create a gang in this bitnode.
|
||||
* Reduces gangs earning.
|
||||
*/
|
||||
GangKarmaRequirement: number;
|
||||
GangSoftcap: number;
|
||||
|
||||
/**
|
||||
* Influences the experienced gained when hacking a server.
|
||||
@ -288,8 +288,9 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
|
||||
BladeburnerRank: 1,
|
||||
BladeburnerSkillCost: 1,
|
||||
|
||||
GangSoftcap: 1,
|
||||
|
||||
DaedalusAugsRequirement: 1,
|
||||
GangKarmaRequirement: 1,
|
||||
|
||||
StaneksGiftPowerMultiplier: 1,
|
||||
StaneksGiftExtraSize: 0,
|
||||
|
@ -206,7 +206,7 @@ export class Action implements IAction {
|
||||
const city = inst.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.1);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
|
@ -1503,18 +1503,22 @@ export class Bladeburner implements IBladeburner {
|
||||
for (const contract of Object.keys(this.contracts)) {
|
||||
const growthF = Growths[contract];
|
||||
if (!growthF) throw new Error("trying to generate count for action that doesn't exist? " + contract);
|
||||
this.contracts[contract].count += (60 * 6 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
|
||||
this.contracts[contract].count += (60 * 3 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
|
||||
}
|
||||
for (const operation of Object.keys(this.operations)) {
|
||||
const growthF = Growths[operation];
|
||||
if (!growthF) throw new Error("trying to generate count for action that doesn't exist? " + operation);
|
||||
this.operations[operation].count += (60 * 6 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
|
||||
this.operations[operation].count += (60 * 3 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
|
||||
}
|
||||
if (this.logging.general) {
|
||||
this.log(`Incited violence in the synthoid communities.`);
|
||||
}
|
||||
const city = this.cities[this.city];
|
||||
city.chaos *= (city.chaos + 100) * 2;
|
||||
for (const cityName of Object.keys(this.cities)) {
|
||||
const city = this.cities[cityName];
|
||||
city.chaos += 10;
|
||||
city.chaos += city.chaos / (Math.log(city.chaos) / Math.log(10));
|
||||
}
|
||||
|
||||
this.startAction(player, this.action);
|
||||
break;
|
||||
}
|
||||
|
@ -3,35 +3,17 @@ import { IMap } from "../types";
|
||||
|
||||
export const GeneralActions: IMap<Action> = {};
|
||||
|
||||
(function () {
|
||||
// General Actions
|
||||
let actionName;
|
||||
actionName = "Training";
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
let actionNames : Array<string> = [
|
||||
"Training",
|
||||
"Field Analysis",
|
||||
"Recruitment",
|
||||
"Diplomacy",
|
||||
"Hyperbolic Regeneration Chamber",
|
||||
"Incite Violence"
|
||||
];
|
||||
|
||||
actionName = "Field Analysis";
|
||||
for (let actionName of actionNames){
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
|
||||
actionName = "Recruitment";
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
|
||||
actionName = "Diplomacy";
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
|
||||
actionName = "Hyperbolic Regeneration Chamber";
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
actionName = "Incite Violence";
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
})();
|
||||
}
|
@ -37,7 +37,7 @@ export class Operation extends Action {
|
||||
const city = inst.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.1);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
|
@ -12,13 +12,13 @@ export const Growths: {
|
||||
["Stealth Retirement Operation"]: () => number;
|
||||
["Assassination"]: () => number;
|
||||
} = {
|
||||
Tracking: () => getRandomInt(5, 75) / 20,
|
||||
"Bounty Hunter": () => getRandomInt(5, 75) / 20,
|
||||
Retirement: () => getRandomInt(5, 75) / 20,
|
||||
Investigation: () => getRandomInt(10, 40) / 20,
|
||||
"Undercover Operation": () => getRandomInt(10, 40) / 20,
|
||||
"Sting Operation": () => getRandomInt(3, 40) / 20,
|
||||
Raid: () => getRandomInt(2, 40) / 20,
|
||||
"Stealth Retirement Operation": () => getRandomInt(1, 20) / 20,
|
||||
Assassination: () => getRandomInt(1, 20) / 20,
|
||||
Tracking: () => getRandomInt(5, 75) / 10,
|
||||
"Bounty Hunter": () => getRandomInt(5, 75) / 10,
|
||||
Retirement: () => getRandomInt(5, 75) / 10,
|
||||
Investigation: () => getRandomInt(10, 40) / 10,
|
||||
"Undercover Operation": () => getRandomInt(10, 40) / 10,
|
||||
"Sting Operation": () => getRandomInt(3, 40) / 10,
|
||||
Raid: () => getRandomInt(2, 40) / 10,
|
||||
"Stealth Retirement Operation": () => getRandomInt(1, 20) / 10,
|
||||
Assassination: () => getRandomInt(1, 20) / 10,
|
||||
};
|
||||
|
@ -153,7 +153,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
<Typography>This industry uses the following equation for it's production: </Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
<IndustryProductEquation division={division} />
|
||||
<IndustryProductEquation key={division.name} division={division} />
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
|
@ -53,8 +53,8 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
In order to issue dividends, simply allocate some percentage of your corporation's profits to dividends. This
|
||||
percentage must be an integer between 0 and {CorporationConstants.DividendMaxPercentage}. (A percentage of 0
|
||||
means no dividends will be issued
|
||||
percentage must be an integer between 0 and 50. (A percentage of 0
|
||||
means no dividends will be issued)
|
||||
<br />
|
||||
<br />
|
||||
Two important things to note:
|
||||
|
@ -16,7 +16,6 @@ interface IMarketTA2Props {
|
||||
function MarketTA2(props: IMarketTA2Props): React.ReactElement {
|
||||
const division = useDivision();
|
||||
if (!division.hasResearch("Market-TA.II")) return <></>;
|
||||
|
||||
const [newCost, setNewCost] = useState<number>(props.mat.bCost);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
@ -88,7 +87,6 @@ interface IProps {
|
||||
|
||||
// Create a popup that lets the player use the Market TA research for Materials
|
||||
export function MaterialMarketTaModal(props: IProps): React.ReactElement {
|
||||
const division = useDivision();
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
@ -102,32 +100,31 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
{!division.hasResearch("Market-TA.II") && (
|
||||
<>
|
||||
<Typography variant="h4">Market-TA.I</Typography>
|
||||
<Typography>
|
||||
The maximum sale price you can mark this up to is{" "}
|
||||
{numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. This means that if you set the sale price
|
||||
higher than this, you will begin to experience a loss in number of sales
|
||||
</Typography>
|
||||
<>
|
||||
<Typography variant="h4">Market-TA.I</Typography>
|
||||
<Typography>
|
||||
The maximum sale price you can mark this up to is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}.
|
||||
This means that if you set the sale price higher than this, you will begin to experience a loss in number of
|
||||
sales
|
||||
</Typography>
|
||||
|
||||
<FormControlLabel
|
||||
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is enabled, then this Material will automatically be sold at the price identified by
|
||||
Market-TA.I (i.e. the price shown above)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
|
||||
<FormControlLabel
|
||||
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is enabled, then this Material will automatically be sold at the price identified by
|
||||
Market-TA.I (i.e. the price shown above)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<MarketTA2 mat={props.mat} />
|
||||
</Modal>
|
||||
);
|
||||
|
@ -221,7 +221,7 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
|
||||
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
|
||||
<Tooltip title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}>
|
||||
<span>
|
||||
<Button disabled={corp.issuedShares > 0.5} onClick={() => setBuybackSharesOpen(true)}>
|
||||
<Button disabled={corp.issuedShares <1} onClick={() => setBuybackSharesOpen(true)}>
|
||||
Buyback shares
|
||||
</Button>
|
||||
</span>
|
||||
|
@ -92,32 +92,31 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
{!division.hasResearch("Market-TA.II") && (
|
||||
<>
|
||||
<Typography variant="h4">Market-TA.I</Typography>
|
||||
<Typography>
|
||||
The maximum sale price you can mark this up to is{" "}
|
||||
{numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price
|
||||
higher than this, you will begin to experience a loss in number of sales
|
||||
</Typography>
|
||||
<>
|
||||
<Typography variant="h4">Market-TA.I</Typography>
|
||||
<Typography>
|
||||
The maximum sale price you can mark this up to is{" "}
|
||||
{numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price
|
||||
higher than this, you will begin to experience a loss in number of sales
|
||||
</Typography>
|
||||
|
||||
<FormControlLabel
|
||||
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is enabled, then this Material will automatically be sold at the price identified by
|
||||
Market-TA.I (i.e. the price shown above)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
|
||||
<FormControlLabel
|
||||
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is enabled, then this Material will automatically be sold at the price identified by
|
||||
Market-TA.I (i.e. the price shown above)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<MarketTA2 product={props.product} />
|
||||
</Modal>
|
||||
);
|
||||
|
@ -23,7 +23,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||
The gift is a grid on which you can place upgrades called fragments. The main type of fragment increases a stat,
|
||||
like your hacking skill or agility exp. Once a stat fragment is placed it then needs to be charged via scripts
|
||||
in order to become useful. The other kind of fragment is called booster fragments. They increase the efficiency
|
||||
of the charged happening on fragments neighboring them (no diagonal)
|
||||
of the charged happening on fragments neighboring them (no diagonal). Q/E to rotate fragments.
|
||||
</Typography>
|
||||
{staneksGift.storedCycles > 5 && (
|
||||
<Typography>
|
||||
|
@ -31,6 +31,7 @@ export function determineCrimeSuccess(p: IPlayer, type: string): boolean {
|
||||
}
|
||||
|
||||
export function findCrime(roughName: string): Crime | null {
|
||||
roughName = roughName.toLowerCase();
|
||||
if (roughName.includes("shoplift")) {
|
||||
return Crimes.Shoplift;
|
||||
} else if (roughName.includes("rob") && roughName.includes("store")) {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React from "react";
|
||||
import { DarkWebItems } from "./DarkWebItems";
|
||||
|
||||
import { Player } from "../Player";
|
||||
import { Terminal } from "../Terminal";
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { Money } from "../ui/React/Money";
|
||||
|
||||
//Posts a "help" message if connected to DarkWeb
|
||||
export function checkIfConnectedToDarkweb(): void {
|
||||
@ -20,7 +21,11 @@ export function checkIfConnectedToDarkweb(): void {
|
||||
export function listAllDarkwebItems(): void {
|
||||
for (const key in DarkWebItems) {
|
||||
const item = DarkWebItems[key];
|
||||
Terminal.print(`${item.program} - ${numeralWrapper.formatMoney(item.price)} - ${item.description}`);
|
||||
Terminal.printRaw(
|
||||
<>
|
||||
{item.program} - <Money money={item.price} /> - {item.description}`
|
||||
</>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +43,7 @@ export function buyDarkwebItem(itemName: string): void {
|
||||
|
||||
// return if invalid
|
||||
if (item === null) {
|
||||
Terminal.print("Unrecognized item: " + itemName);
|
||||
Terminal.error("Unrecognized item: " + itemName);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -50,7 +55,7 @@ export function buyDarkwebItem(itemName: string): void {
|
||||
|
||||
// return if the player doesn't have enough money
|
||||
if (Player.money.lt(item.price)) {
|
||||
Terminal.print("Not enough money to purchase " + item.program);
|
||||
Terminal.error("Not enough money to purchase " + item.program);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ export enum Exploit {
|
||||
Unclickable = "Unclickable",
|
||||
UndocumentedFunctionCall = "UndocumentedFunctionCall",
|
||||
TimeCompression = "TimeCompression",
|
||||
RealityAlteration = "RealityAlteration",
|
||||
// To the players reading this. Yes you're supposed to add EditSaveFile by
|
||||
// editing your save file, yes you could add them all, no we don't care
|
||||
// that's not the point.
|
||||
@ -31,6 +32,7 @@ const names: {
|
||||
TimeCompression: "by compressing time",
|
||||
Unclickable: "by clicking the unclickable.",
|
||||
UndocumentedFunctionCall: "by looking beyond the documentation.",
|
||||
RealityAlteration: "by altering reality to suit your whims.",
|
||||
};
|
||||
|
||||
export function ExploitName(exploit: string): string {
|
||||
|
@ -42,6 +42,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
const augs: string[] = [];
|
||||
for (const augName in Augmentations) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
|
||||
if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue;
|
||||
const aug = Augmentations[augName];
|
||||
if (!aug.isSpecial) {
|
||||
augs.push(augName);
|
||||
|
@ -13,6 +13,8 @@ import TableBody from "@mui/material/TableBody";
|
||||
import { Table, TableCell } from "../../ui/React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
|
||||
export const InvitationsSeen: string[] = [];
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
router: IRouter;
|
||||
@ -27,6 +29,14 @@ export function FactionsRoot(props: IProps): React.ReactElement {
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
props.player.factionInvitations.forEach((faction) => {
|
||||
if (InvitationsSeen.includes(faction)) return;
|
||||
InvitationsSeen.push(faction);
|
||||
});
|
||||
}, []);
|
||||
|
||||
function openFaction(faction: Faction): void {
|
||||
props.router.toFaction(faction);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import { GangMember } from "./GangMember";
|
||||
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
export class Gang {
|
||||
facName: string;
|
||||
@ -115,9 +116,10 @@ export class Gang {
|
||||
wantedLevelGains += wantedLevelGain;
|
||||
if (this.members[i].getTask().baseWanted < 0) justice++; // this member is lowering wanted.
|
||||
}
|
||||
this.respectGainRate = respectGains;
|
||||
this.wantedGainRate = wantedLevelGains;
|
||||
this.moneyGainRate = moneyGains;
|
||||
const territoryPenalty = (0.2 * this.getTerritory() + 0.8) * BitNodeMultipliers.GangSoftcap;
|
||||
this.respectGainRate = Math.pow(respectGains, territoryPenalty);
|
||||
this.wantedGainRate = Math.pow(wantedLevelGains, territoryPenalty);
|
||||
this.moneyGainRate = Math.pow(moneyGains, territoryPenalty);
|
||||
const gain = respectGains * numCycles;
|
||||
this.respect += gain;
|
||||
// Faction reputation gains is respect gain divided by some constant
|
||||
|
@ -63,7 +63,7 @@ export class GangMember {
|
||||
}
|
||||
|
||||
calculateAscensionMult(points: number): number {
|
||||
return Math.max(Math.pow(points / 4000, 0.7), 1);
|
||||
return Math.max(Math.pow(points / 2000, 0.7), 1);
|
||||
}
|
||||
|
||||
updateSkillLevels(): void {
|
||||
|
@ -6,7 +6,7 @@ export const GangConstants: {
|
||||
Names: string[];
|
||||
} = {
|
||||
// Respect is divided by this to get rep gain
|
||||
GangRespectToReputationRatio: 25,
|
||||
GangRespectToReputationRatio: 75,
|
||||
MaximumGangMembers: 12,
|
||||
CyclesPerTerritoryAndPowerUpdate: 100,
|
||||
// Portion of upgrade multiplier that is kept after ascending
|
||||
|
@ -53,7 +53,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00005,
|
||||
baseWanted: 0.0001,
|
||||
baseMoney: 1,
|
||||
baseMoney: 3,
|
||||
hackWeight: 100,
|
||||
difficulty: 1,
|
||||
},
|
||||
@ -66,7 +66,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00008,
|
||||
baseWanted: 0.003,
|
||||
baseMoney: 2.5,
|
||||
baseMoney: 7.5,
|
||||
hackWeight: 85,
|
||||
chaWeight: 15,
|
||||
difficulty: 3.5,
|
||||
@ -80,7 +80,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.0001,
|
||||
baseWanted: 0.075,
|
||||
baseMoney: 6,
|
||||
baseMoney: 18,
|
||||
hackWeight: 80,
|
||||
chaWeight: 20,
|
||||
difficulty: 5,
|
||||
@ -118,7 +118,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.0004,
|
||||
baseWanted: 0.3,
|
||||
baseMoney: 15,
|
||||
baseMoney: 45,
|
||||
hackWeight: 80,
|
||||
chaWeight: 20,
|
||||
difficulty: 20,
|
||||
@ -132,7 +132,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.001,
|
||||
baseWanted: 1.25,
|
||||
baseMoney: 120,
|
||||
baseMoney: 360,
|
||||
hackWeight: 75,
|
||||
chaWeight: 25,
|
||||
difficulty: 25,
|
||||
@ -158,7 +158,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
name: "Ethical Hacking",
|
||||
params: {
|
||||
baseWanted: -0.001,
|
||||
baseMoney: 1,
|
||||
baseMoney: 3,
|
||||
hackWeight: 90,
|
||||
chaWeight: 10,
|
||||
difficulty: 1,
|
||||
@ -172,7 +172,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00005,
|
||||
baseWanted: 0.00005,
|
||||
baseMoney: 1.2,
|
||||
baseMoney: 3.6,
|
||||
strWeight: 25,
|
||||
defWeight: 25,
|
||||
dexWeight: 25,
|
||||
@ -189,7 +189,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00006,
|
||||
baseWanted: 0.002,
|
||||
baseMoney: 5,
|
||||
baseMoney: 15,
|
||||
agiWeight: 20,
|
||||
dexWeight: 20,
|
||||
chaWeight: 60,
|
||||
@ -209,7 +209,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00004,
|
||||
baseWanted: 0.02,
|
||||
baseMoney: 2.5,
|
||||
baseMoney: 7.5,
|
||||
hackWeight: 10,
|
||||
strWeight: 25,
|
||||
defWeight: 25,
|
||||
@ -232,7 +232,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00012,
|
||||
baseWanted: 0.05,
|
||||
baseMoney: 15,
|
||||
baseMoney: 45,
|
||||
strWeight: 5,
|
||||
defWeight: 5,
|
||||
agiWeight: 25,
|
||||
@ -249,7 +249,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.00014,
|
||||
baseWanted: 0.1,
|
||||
baseMoney: 38,
|
||||
baseMoney: 114,
|
||||
hackWeight: 20,
|
||||
strWeight: 15,
|
||||
defWeight: 15,
|
||||
@ -267,7 +267,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.0002,
|
||||
baseWanted: 0.24,
|
||||
baseMoney: 58,
|
||||
baseMoney: 174,
|
||||
hackWeight: 15,
|
||||
strWeight: 20,
|
||||
defWeight: 20,
|
||||
@ -289,7 +289,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.0002,
|
||||
baseWanted: 0.125,
|
||||
baseMoney: 24,
|
||||
baseMoney: 72,
|
||||
hackWeight: 25,
|
||||
strWeight: 25,
|
||||
dexWeight: 25,
|
||||
@ -305,7 +305,7 @@ export const gangMemberTasksMetadata: IGangMemberTaskMetadata[] = [
|
||||
params: {
|
||||
baseRespect: 0.004,
|
||||
baseWanted: 1.25,
|
||||
baseMoney: 120,
|
||||
baseMoney: 360,
|
||||
hackWeight: 30,
|
||||
strWeight: 5,
|
||||
defWeight: 5,
|
||||
|
@ -20,6 +20,8 @@ import { createRandomIp } from "../utils/IPAddress";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
|
||||
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
hostname: string;
|
||||
@ -35,7 +37,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
|
||||
// Number of cores. Improves hash production
|
||||
cores = 1;
|
||||
|
||||
|
||||
// Number of hashes that can be stored by this Hacknet Server
|
||||
hashCapacity = 0;
|
||||
|
||||
@ -93,6 +95,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
upgradeCore(levels: number, prodMult: number): void {
|
||||
this.cores = Math.min(HacknetServerConstants.MaxCores, Math.round(this.cores + levels));
|
||||
this.updateHashRate(prodMult);
|
||||
this.cpuCores=this.cores;
|
||||
}
|
||||
|
||||
upgradeLevel(levels: number, prodMult: number): void {
|
||||
|
@ -3,6 +3,7 @@ import { Location } from "../../Locations/Location";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
interface IProps {
|
||||
Location: Location;
|
||||
@ -60,6 +61,21 @@ export function Intro(props: IProps): React.ReactElement {
|
||||
Maximum level: {props.MaxLevel}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
Difficulty: {numeralWrapper.format(props.Difficulty * 33.3333, "0")} / 100
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
{props.Difficulty > 1.5 && (
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
Warning: This location is too heavily guarded for your current stats, try training or finding an easier
|
||||
location.
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid item xs={10}>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
|
||||
<Typography
|
||||
|
@ -26,12 +26,12 @@ const difficulties: {
|
||||
export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = { window: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const [guarding, setGuarding] = useState(true);
|
||||
const [phase, setPhase] = useState(0);
|
||||
|
||||
function press(this: Document, event: KeyboardEvent): void {
|
||||
event.preventDefault();
|
||||
if (event.keyCode !== 32) return;
|
||||
if (guarding) {
|
||||
if (phase !== 2) {
|
||||
props.onFailure();
|
||||
} else {
|
||||
props.onSuccess();
|
||||
@ -39,14 +39,15 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let id2 = -1;
|
||||
const id = window.setTimeout(() => {
|
||||
setGuarding(false);
|
||||
id2 = window.setTimeout(() => setGuarding(true), difficulty.window);
|
||||
let id = window.setTimeout(() => {
|
||||
setPhase(1);
|
||||
id = window.setTimeout(() => {
|
||||
setPhase(2);
|
||||
id = window.setTimeout(() => setPhase(0), difficulty.window);
|
||||
}, 250);
|
||||
}, Math.random() * 3250 + 1500);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
if (id2 !== -1) clearInterval(id2);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -55,7 +56,9 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4">Slash when his guard is down!</Typography>
|
||||
<Typography variant="h4">{guarding ? "!Guarding!" : "!ATTACKING!"}</Typography>
|
||||
{phase === 0 && <Typography variant="h4">Guarding ...</Typography>}
|
||||
{phase === 1 && <Typography variant="h4">Preparing?</Typography>}
|
||||
{phase === 2 && <Typography variant="h4">ATTACKING!</Typography>}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -8,11 +8,12 @@ import { Cities } from "./Cities";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export function createCityMap<T>(initValue: T): IMap<T> {
|
||||
const map: IMap<any> = {};
|
||||
const map: IMap<T> = {};
|
||||
const cities = Object.keys(Cities);
|
||||
for (let i = 0; i < cities.length; ++i) {
|
||||
map[cities[i]] = initValue;
|
||||
}
|
||||
|
||||
return map;
|
||||
// round try JSON so to make sure none of the initial values have the same references.
|
||||
return JSON.parse(JSON.stringify(map));
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import { joinFaction } from "../../Faction/FactionHelpers";
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -78,7 +79,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
|
||||
function renderNoodleBar(): React.ReactElement {
|
||||
function EatNoodles(): void {
|
||||
dialogBoxCreate(<>You ate some delicious noodles and feel refreshed.</>);
|
||||
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", "success");
|
||||
}
|
||||
|
||||
return <Button onClick={EatNoodles}>Eat noodles</Button>;
|
||||
|
27
src/Netscript/RecentScripts.ts
Normal file
27
src/Netscript/RecentScripts.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { RunningScript } from "src/Script/RunningScript";
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
|
||||
export const recentScripts: RecentScript[] = [];
|
||||
|
||||
export function AddRecentScript(workerScript: WorkerScript): void {
|
||||
if (recentScripts.find((r) => r.pid === workerScript.pid)) return;
|
||||
recentScripts.push({
|
||||
filename: workerScript.name,
|
||||
args: workerScript.args,
|
||||
pid: workerScript.pid,
|
||||
timestamp: new Date(),
|
||||
|
||||
runningScript: workerScript.scriptRef,
|
||||
});
|
||||
while (recentScripts.length > 50) {
|
||||
recentScripts.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export interface RecentScript {
|
||||
filename: string;
|
||||
args: string[];
|
||||
pid: number;
|
||||
timestamp: Date;
|
||||
runningScript: RunningScript;
|
||||
}
|
@ -11,6 +11,7 @@ import { GetServer } from "../Server/AllServers";
|
||||
|
||||
import { compareArrays } from "../utils/helpers/compareArrays";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { AddRecentScript } from "./RecentScripts";
|
||||
|
||||
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
|
||||
export function killWorkerScript(workerScript: WorkerScript): boolean;
|
||||
@ -66,8 +67,6 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
|
||||
}
|
||||
|
||||
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
|
||||
workerScript.env.stopFlag = true;
|
||||
killNetscriptDelay(workerScript);
|
||||
if (typeof workerScript.atExit === "function") {
|
||||
try {
|
||||
workerScript.atExit();
|
||||
@ -78,6 +77,8 @@ function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = tru
|
||||
}
|
||||
workerScript.atExit = undefined;
|
||||
}
|
||||
workerScript.env.stopFlag = true;
|
||||
killNetscriptDelay(workerScript);
|
||||
removeWorkerScript(workerScript, rerenderUi);
|
||||
}
|
||||
|
||||
@ -85,49 +86,44 @@ function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = tru
|
||||
* Helper function that removes the script being killed from the global pool.
|
||||
* Also handles other cleanup-time operations
|
||||
*
|
||||
* @param {WorkerScript | number} - Identifier for WorkerScript. Either the object itself, or
|
||||
* @param {WorkerScript} - Identifier for WorkerScript. Either the object itself, or
|
||||
* its index in the global workerScripts array
|
||||
*/
|
||||
function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
|
||||
if (workerScript instanceof WorkerScript) {
|
||||
const ip = workerScript.hostname;
|
||||
const name = workerScript.name;
|
||||
const ip = workerScript.hostname;
|
||||
const name = workerScript.name;
|
||||
|
||||
// Get the server on which the script runs
|
||||
const server = GetServer(ip);
|
||||
if (server == null) {
|
||||
console.error(`Could not find server on which this script is running: ${ip}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the RunningScript object from that server
|
||||
for (let i = 0; i < server.runningScripts.length; ++i) {
|
||||
const runningScript = server.runningScripts[i];
|
||||
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
|
||||
server.runningScripts.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate ram used on that server
|
||||
server.ramUsed = 0;
|
||||
for (const rs of server.runningScripts) server.ramUsed += rs.ramUsage * rs.threads;
|
||||
|
||||
// Delete script from global pool (workerScripts)
|
||||
const res = workerScripts.delete(workerScript.pid);
|
||||
if (!res) {
|
||||
console.warn(`removeWorkerScript() called with WorkerScript that wasn't in the global map:`);
|
||||
console.warn(workerScript);
|
||||
}
|
||||
|
||||
if (rerenderUi) {
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
}
|
||||
} else {
|
||||
console.error(`Invalid argument passed into removeWorkerScript():`);
|
||||
console.error(workerScript);
|
||||
// Get the server on which the script runs
|
||||
const server = GetServer(ip);
|
||||
if (server == null) {
|
||||
console.error(`Could not find server on which this script is running: ${ip}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the RunningScript object from that server
|
||||
for (let i = 0; i < server.runningScripts.length; ++i) {
|
||||
const runningScript = server.runningScripts[i];
|
||||
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
|
||||
server.runningScripts.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Recalculate ram used on that server
|
||||
server.ramUsed = 0;
|
||||
for (const rs of server.runningScripts) server.ramUsed += rs.ramUsage * rs.threads;
|
||||
|
||||
// Delete script from global pool (workerScripts)
|
||||
const res = workerScripts.delete(workerScript.pid);
|
||||
if (!res) {
|
||||
console.warn(`removeWorkerScript() called with WorkerScript that wasn't in the global map:`);
|
||||
console.warn(workerScript);
|
||||
}
|
||||
AddRecentScript(workerScript);
|
||||
|
||||
if (rerenderUi) {
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): s
|
||||
throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.hostname}`);
|
||||
}
|
||||
|
||||
return "|" + server.hostname + "|" + workerScript.name + "|" + msg;
|
||||
return "|DELIMITER|" + server.hostname + "|DELIMITER|" + workerScript.name + "|DELIMITER|" + msg;
|
||||
}
|
||||
|
||||
export function resolveNetscriptRequestedThreads(
|
||||
@ -50,7 +50,7 @@ export function isScriptErrorMessage(msg: string): boolean {
|
||||
if (!isString(msg)) {
|
||||
return false;
|
||||
}
|
||||
const splitMsg = msg.split("|");
|
||||
const splitMsg = msg.split("|DELIMITER|");
|
||||
if (splitMsg.length != 4) {
|
||||
return false;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,7 +32,7 @@ export interface INetscriptBladeburner {
|
||||
getTeamSize(type?: any, name?: any): any;
|
||||
setTeamSize(type?: any, name?: any, size?: any): any;
|
||||
getCityEstimatedPopulation(cityName: any): any;
|
||||
getCityEstimatedCommunities(cityName: any): any;
|
||||
getCityCommunities(cityName: any): any;
|
||||
getCityChaos(cityName: any): any;
|
||||
getCity(): any;
|
||||
switchCity(cityName: any): any;
|
||||
@ -320,13 +320,13 @@ export function NetscriptBladeburner(
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].popEst;
|
||||
},
|
||||
getCityEstimatedCommunities: function (cityName: any): any {
|
||||
helper.updateDynamicRam("getCityEstimatedCommunities", getRamCost("bladeburner", "getCityEstimatedCommunities"));
|
||||
checkBladeburnerAccess("getCityEstimatedCommunities");
|
||||
checkBladeburnerCity("getCityEstimatedCommunities", cityName);
|
||||
getCityCommunities: function (cityName: any): any {
|
||||
helper.updateDynamicRam("getCityCommunities", getRamCost("bladeburner", "getCityCommunities"));
|
||||
checkBladeburnerAccess("getCityCommunities");
|
||||
checkBladeburnerCity("getCityCommunities", cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].commsEst;
|
||||
return bladeburner.cities[cityName].comms;
|
||||
},
|
||||
getCityChaos: function (cityName: any): any {
|
||||
helper.updateDynamicRam("getCityChaos", getRamCost("bladeburner", "getCityChaos"));
|
||||
|
@ -8,6 +8,7 @@ export interface INetscriptExtra {
|
||||
};
|
||||
exploit(): void;
|
||||
bypass(doc: Document): void;
|
||||
alterReality(): void;
|
||||
}
|
||||
|
||||
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INetscriptExtra {
|
||||
@ -34,5 +35,13 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INe
|
||||
doc.completely_unused_field = undefined;
|
||||
real_document.completely_unused_field = undefined;
|
||||
},
|
||||
alterReality: function (): void {
|
||||
const x = false;
|
||||
console.warn("I am sure that this variable is false");
|
||||
if (x !== false) {
|
||||
console.warn("Reality has been altered!");
|
||||
player.giveExploit(Exploit.RealityAlteration);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -540,7 +540,7 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
const errorTextArray = w.errorMessage.split("|");
|
||||
const errorTextArray = w.errorMessage.split("|DELIMITER|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.error("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.error("Error text: " + w.errorMessage);
|
||||
@ -647,9 +647,9 @@ export function runScriptFromScript(
|
||||
return 0;
|
||||
}
|
||||
|
||||
args = args.map((arg) => {
|
||||
if (typeof arg === "number") return arg;
|
||||
return arg + ""; // force cast to string
|
||||
args.forEach((arg) => {
|
||||
if (typeof arg !== "string" && typeof arg !== "number")
|
||||
throw new Error("Only strings and numbers can be passed as arguments to otherscripts.");
|
||||
});
|
||||
|
||||
// Check if the script is already running
|
||||
|
@ -2,7 +2,6 @@ import { Factions } from "../../Faction/Factions";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Gang } from "../../Gang/Gang";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
// Amount of negative karma needed to manage a gang in BitNodes other than 2
|
||||
@ -16,7 +15,7 @@ export function canAccessGang(this: IPlayer): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.karma <= BitNodeMultipliers.GangKarmaRequirement * GangKarmaRequirement;
|
||||
return this.karma <= GangKarmaRequirement;
|
||||
}
|
||||
|
||||
export function getGangFaction(this: IPlayer): Faction {
|
||||
|
@ -59,6 +59,7 @@ import { Money } from "../../ui/React/Money";
|
||||
|
||||
import React from "react";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
|
||||
export function init(this: IPlayer): void {
|
||||
/* Initialize Player's home computer */
|
||||
@ -629,6 +630,7 @@ export function work(this: IPlayer, numCycles: number): boolean {
|
||||
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
|
||||
|
||||
this.workRepGainRate = this.getWorkRepGain();
|
||||
this.workMoneyGainRate = this.getWorkMoneyGain();
|
||||
this.processWorkEarnings(numCycles);
|
||||
|
||||
const comp = Companies[this.companyName];
|
||||
@ -1697,13 +1699,7 @@ export function regenerateHp(this: IPlayer, amt: number): void {
|
||||
export function hospitalize(this: IPlayer): number {
|
||||
const cost = getHospitalizationCost(this);
|
||||
if (Settings.SuppressHospitalizationPopup === false) {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
You were in critical condition! You were taken to the hospital where luckily they were able to save your life.
|
||||
You were charged
|
||||
<Money money={cost} />
|
||||
</>,
|
||||
);
|
||||
SnackbarEvents.emit(`You've been Hospitalized for ${numeralWrapper.formatMoney(cost)}`, "warning");
|
||||
}
|
||||
|
||||
this.loseMoney(cost);
|
||||
@ -1785,6 +1781,7 @@ export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing =
|
||||
}
|
||||
|
||||
this.jobs[company.name] = pos.name;
|
||||
if (!this.focus && this.isWorking && this.companyName !== this.location) this.resetWorkStatus();
|
||||
this.companyName = this.location;
|
||||
|
||||
if (!sing) {
|
||||
|
@ -143,8 +143,8 @@ export class Sleeve extends Person {
|
||||
* Commit crimes
|
||||
*/
|
||||
commitCrime(p: IPlayer, crimeKey: string): boolean {
|
||||
const crime: Crime | null = Crimes[crimeKey];
|
||||
if (!(crime instanceof Crime)) {
|
||||
const crime: Crime | null = Crimes[crimeKey] || Object.values(Crimes).find((crime) => crime.name === crimeKey);
|
||||
if (!crime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ export class Sleeve extends Person {
|
||||
|
||||
this.currentTaskLocation = String(this.gainRatesForTask.money);
|
||||
|
||||
this.crimeType = crimeKey;
|
||||
this.crimeType = crime.name;
|
||||
this.currentTaskMaxTime = crime.time;
|
||||
this.currentTask = SleeveTaskType.Crime;
|
||||
return true;
|
||||
@ -179,8 +179,8 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
// For crimes, all experience and money is gained at the end
|
||||
if (this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
const crime: Crime | null = Crimes[this.crimeType];
|
||||
if (!(crime instanceof Crime)) {
|
||||
const crime: Crime | undefined = Object.values(Crimes).find((crime) => crime.name === this.crimeType);
|
||||
if (!crime) {
|
||||
console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`);
|
||||
this.resetTaskStatus();
|
||||
return retValue;
|
||||
|
@ -104,14 +104,17 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SleeveTaskType.Crime:
|
||||
case SleeveTaskType.Crime: {
|
||||
const crime = Object.values(Crimes).find((crime) => crime.name === props.sleeve.crimeType);
|
||||
if (!crime) throw new Error("crime should not be undefined");
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently attempting to {Crimes[props.sleeve.crimeType].type} (Success Rate:{" "}
|
||||
{numeralWrapper.formatPercentage(Crimes[props.sleeve.crimeType].successRate(props.sleeve))}).
|
||||
This sleeve is currently attempting to {crime.type} (Success Rate:{" "}
|
||||
{numeralWrapper.formatPercentage(crime.successRate(props.sleeve))}).
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SleeveTaskType.Class:
|
||||
desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
|
@ -122,7 +122,7 @@ const tasks: {
|
||||
};
|
||||
},
|
||||
"Commit Crime": (): ITaskDetails => {
|
||||
return { first: Object.keys(Crimes), second: () => ["------"] };
|
||||
return { first: Object.values(Crimes).map((crime) => crime.name), second: () => ["------"] };
|
||||
},
|
||||
"Take University Course": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
let universities: string[] = [];
|
||||
|
@ -28,11 +28,13 @@ import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
import { staneksGift } from "./CotMG/Helper";
|
||||
import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
|
||||
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
|
||||
|
||||
const BitNode8StartingMoney = 250e6;
|
||||
|
||||
// Prestige by purchasing augmentation
|
||||
function prestigeAugmentation(): void {
|
||||
export function prestigeAugmentation(): void {
|
||||
initBitNodeMultipliers(Player);
|
||||
|
||||
const maintainMembership = Player.factions.filter(function (faction) {
|
||||
@ -147,10 +149,12 @@ function prestigeAugmentation(): void {
|
||||
staneksGift.prestigeAugmentation();
|
||||
|
||||
resetPidCounter();
|
||||
ProgramsSeen.splice(0, ProgramsSeen.length);
|
||||
InvitationsSeen.splice(0, InvitationsSeen.length);
|
||||
}
|
||||
|
||||
// Prestige by destroying Bit Node and gaining a Source File
|
||||
function prestigeSourceFile(flume: boolean): void {
|
||||
export function prestigeSourceFile(flume: boolean): void {
|
||||
initBitNodeMultipliers(Player);
|
||||
updateSourceFileFlags(Player);
|
||||
|
||||
@ -280,5 +284,3 @@ function prestigeSourceFile(flume: boolean): void {
|
||||
|
||||
resetPidCounter();
|
||||
}
|
||||
|
||||
export { prestigeAugmentation, prestigeSourceFile };
|
||||
|
@ -190,7 +190,7 @@ export const programsMetadata: IProgramCreationParams[] = [
|
||||
time: CONSTANTS.MillisecondsPerQuarterHour,
|
||||
},
|
||||
run: (router: IRouter, terminal: ITerminal): void => {
|
||||
terminal.print("This executable cannot be run.");
|
||||
terminal.error("This executable cannot be run.");
|
||||
terminal.print("DeepscanV1.exe lets you run 'scan-analyze' with a depth up to 5.");
|
||||
},
|
||||
},
|
||||
@ -204,7 +204,7 @@ export const programsMetadata: IProgramCreationParams[] = [
|
||||
time: CONSTANTS.MillisecondsPer2Hours,
|
||||
},
|
||||
run: (router: IRouter, terminal: ITerminal): void => {
|
||||
terminal.print("This executable cannot be run.");
|
||||
terminal.error("This executable cannot be run.");
|
||||
terminal.print("DeepscanV2.exe lets you run 'scan-analyze' with a depth up to 10.");
|
||||
},
|
||||
},
|
||||
@ -219,18 +219,18 @@ export const programsMetadata: IProgramCreationParams[] = [
|
||||
},
|
||||
run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]): void => {
|
||||
if (args.length !== 1) {
|
||||
terminal.print("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
|
||||
terminal.error("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
|
||||
return;
|
||||
}
|
||||
|
||||
const targetServer = GetServer(args[0]);
|
||||
if (targetServer == null) {
|
||||
terminal.print("Invalid server IP/hostname");
|
||||
terminal.error("Invalid server IP/hostname");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(targetServer instanceof Server)) {
|
||||
terminal.print(`ServerProfiler.exe can only be run on normal servers.`);
|
||||
terminal.error(`ServerProfiler.exe can only be run on normal servers.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ export const programsMetadata: IProgramCreationParams[] = [
|
||||
time: CONSTANTS.MillisecondsPerQuarterHour,
|
||||
},
|
||||
run: (router: IRouter, terminal: ITerminal): void => {
|
||||
terminal.print("This executable cannot be run.");
|
||||
terminal.error("This executable cannot be run.");
|
||||
terminal.print("AutoLink.exe lets you automatically connect to other servers when using 'scan-analyze'.");
|
||||
terminal.print("When using scan-analyze, click on a server's hostname to connect to it.");
|
||||
},
|
||||
|
@ -5,6 +5,8 @@ import { getAvailableCreatePrograms } from "../ProgramHelpers";
|
||||
import { Tooltip, Typography } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
export const ProgramsSeen: string[] = [];
|
||||
|
||||
export function ProgramsRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
@ -13,6 +15,15 @@ export function ProgramsRoot(): React.ReactElement {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
const programs = getAvailableCreatePrograms(player);
|
||||
|
||||
useEffect(() => {
|
||||
programs.forEach((p) => {
|
||||
if (ProgramsSeen.includes(p.name)) return;
|
||||
ProgramsSeen.push(p.name);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
@ -27,7 +38,7 @@ export function ProgramsRoot(): React.ReactElement {
|
||||
time. Your progress will be saved and you can continue later.
|
||||
</Typography>
|
||||
|
||||
{getAvailableCreatePrograms(player).map((program) => {
|
||||
{programs.map((program) => {
|
||||
const create = program.create;
|
||||
if (create === null) return <></>;
|
||||
|
||||
|
@ -41,10 +41,12 @@ export function OptionsModal(props: IProps): React.ReactElement {
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>Theme: </Typography>
|
||||
<Select onChange={(event) => setTheme(event.target.value)} defaultValue={props.options.theme}>
|
||||
<Select onChange={(event) => setTheme(event.target.value)} value={theme}>
|
||||
<MenuItem value="vs-dark">dark</MenuItem>
|
||||
<MenuItem value="light">light</MenuItem>
|
||||
<MenuItem value="monokai">monokai</MenuItem>
|
||||
<MenuItem value="solarized-dark">solarized-dark</MenuItem>
|
||||
<MenuItem value="solarized-light">solarized-light</MenuItem>
|
||||
</Select>
|
||||
</Box>
|
||||
|
||||
|
@ -4,7 +4,6 @@ import * as monaco from "monaco-editor";
|
||||
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||
import { OptionsModal } from "./OptionsModal";
|
||||
import { Options } from "./Options";
|
||||
import { js_beautify as beautifyCode } from "js-beautify";
|
||||
import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
@ -217,12 +216,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
function beautify(): void {
|
||||
if (editorRef.current === null) return;
|
||||
const pretty = beautifyCode(code, {
|
||||
indent_with_tabs: !options.insertSpaces,
|
||||
indent_size: 4,
|
||||
brace_style: "preserve-inline",
|
||||
});
|
||||
editorRef.current.setValue(pretty);
|
||||
editorRef.current.getAction("editor.action.formatDocument").run();
|
||||
}
|
||||
|
||||
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
|
@ -63,4 +63,159 @@ export async function loadThemes(monaco: { editor: any }): Promise<void> {
|
||||
"editor.selectionHighlightBorder": "#222218",
|
||||
},
|
||||
});
|
||||
|
||||
monaco.editor.defineTheme("solarish-dark", {
|
||||
base: "vs-dark",
|
||||
inherit: true,
|
||||
rules: [
|
||||
{
|
||||
background: "002b36",
|
||||
token: "",
|
||||
},
|
||||
{
|
||||
foreground: "586e75",
|
||||
token: "comment",
|
||||
},
|
||||
{
|
||||
foreground: "00afaf",
|
||||
token: "string",
|
||||
},
|
||||
{
|
||||
token: "number",
|
||||
foreground: "00afaf",
|
||||
},
|
||||
{
|
||||
token: "otherkeyvars",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
foreground: "268bd2",
|
||||
token: "function",
|
||||
},
|
||||
{
|
||||
foreground: "859900",
|
||||
token: "keyword",
|
||||
},
|
||||
{
|
||||
token: "storage.type.function.js",
|
||||
foreground: "cb4b16",
|
||||
},
|
||||
{
|
||||
token: "ns",
|
||||
foreground: "cb4b16",
|
||||
},
|
||||
{
|
||||
token: "netscriptfunction",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
token: "otherkeywords",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
token: "type.identifier.js",
|
||||
foreground: "b58900",
|
||||
},
|
||||
{
|
||||
token: "delimiter.square.js",
|
||||
foreground: "0087ff",
|
||||
},
|
||||
{
|
||||
token: "delimiter.bracket.js",
|
||||
foreground: "0087ff",
|
||||
},
|
||||
{
|
||||
token: "this",
|
||||
foreground: "cb4b16",
|
||||
},
|
||||
],
|
||||
colors: {
|
||||
"editor.foreground": "#839496",
|
||||
"editor.background": "#002b36",
|
||||
"editor.selectionBackground": "#073642",
|
||||
"editor.lineHighlightBackground": "#073642",
|
||||
"editorCursor.foreground": "#819090",
|
||||
"editorWhitespace.foreground": "#073642",
|
||||
"editorIndentGuide.activeBackground": "#9D550FB0",
|
||||
"editor.selectionHighlightBorder": "#222218",
|
||||
},
|
||||
});
|
||||
|
||||
monaco.editor.defineTheme("solarish-light", {
|
||||
base: "vs",
|
||||
inherit: true,
|
||||
rules: [
|
||||
{
|
||||
foreground: "657b83",
|
||||
background: "fdf6e3",
|
||||
token: "",
|
||||
},
|
||||
{
|
||||
foreground: "586e75",
|
||||
token: "comment",
|
||||
},
|
||||
{
|
||||
foreground: "2aa198",
|
||||
token: "string",
|
||||
},
|
||||
{
|
||||
token: "number",
|
||||
foreground: "2aa198",
|
||||
},
|
||||
{
|
||||
token: "otherkeyvars",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
foreground: "268bd2",
|
||||
token: "function",
|
||||
},
|
||||
{
|
||||
foreground: "859900",
|
||||
token: "keyword",
|
||||
},
|
||||
{
|
||||
token: "storage.type.function.js",
|
||||
foreground: "bc4b16",
|
||||
},
|
||||
{
|
||||
token: "ns",
|
||||
foreground: "cb4b16",
|
||||
},
|
||||
{
|
||||
token: "netscriptfunction",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
token: "otherkeywords",
|
||||
foreground: "268bd2",
|
||||
},
|
||||
{
|
||||
token: "type.identifier.js",
|
||||
foreground: "b58900",
|
||||
},
|
||||
{
|
||||
token: "delimiter.square.js",
|
||||
foreground: "0087ff",
|
||||
},
|
||||
{
|
||||
token: "delimiter.bracket.js",
|
||||
foreground: "0087ff",
|
||||
},
|
||||
{
|
||||
token: "this",
|
||||
foreground: "cb4b16",
|
||||
},
|
||||
],
|
||||
colors: {
|
||||
"editor.foreground": "#657b83",
|
||||
"editor.background": "#fdf6e3",
|
||||
"editor.selectionBackground": "#eee8d5",
|
||||
"editor.lineHighlightBackground": "#eee8d5",
|
||||
"editorCursor.foreground": "#657b83",
|
||||
"editorWhitespace.foreground": "#eee8d5",
|
||||
"editorIndentGuide.activeBackground": "#eee8d5",
|
||||
"editor.selectionHighlightBorder": "#073642",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ export function getPurchaseServerCost(ram: number): number {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const upg = Math.max(0, Math.log(sanitizedRam) / Math.log(2) - 9);
|
||||
const upg = Math.max(0, Math.log(sanitizedRam) / Math.log(2) - 6);
|
||||
|
||||
return (
|
||||
sanitizedRam *
|
||||
|
@ -52,6 +52,8 @@ import { redPillFlag } from "../../RedPill";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
||||
import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
|
||||
|
||||
const openedMixin = (theme: Theme): CSSObject => ({
|
||||
width: theme.spacing(31),
|
||||
@ -134,10 +136,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
const flashTutorial = ITutorial.currStep === iTutorialSteps.WorldDescription;
|
||||
|
||||
const augmentationCount = props.player.queuedAugmentations.length;
|
||||
const invitationsCount = props.player.factionInvitations.length;
|
||||
const programCount = getAvailableCreatePrograms(props.player).length;
|
||||
const invitationsCount = props.player.factionInvitations.filter((f) => !InvitationsSeen.includes(f)).length;
|
||||
const programCount = getAvailableCreatePrograms(props.player).length - ProgramsSeen.length;
|
||||
const canCreateProgram =
|
||||
programCount > 0 ||
|
||||
getAvailableCreatePrograms(props.player).length > 0 ||
|
||||
props.player.augmentations.length > 0 ||
|
||||
props.player.queuedAugmentations.length > 0 ||
|
||||
props.player.sourceFiles.length > 0;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import { TextFile } from "../TextFile";
|
||||
import { Script } from "../Script/Script";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
@ -18,6 +19,13 @@ export class Output {
|
||||
}
|
||||
}
|
||||
|
||||
export class RawOutput {
|
||||
raw: React.ReactNode;
|
||||
constructor(node: React.ReactNode) {
|
||||
this.raw = node;
|
||||
}
|
||||
}
|
||||
|
||||
export class Link {
|
||||
hostname: string;
|
||||
dashes: string;
|
||||
@ -46,7 +54,7 @@ export interface ITerminal {
|
||||
commandHistory: string[];
|
||||
commandHistoryIndex: number;
|
||||
|
||||
outputHistory: (Output | Link)[];
|
||||
outputHistory: (Output | Link | RawOutput)[];
|
||||
|
||||
// True if a Coding Contract prompt is opened
|
||||
contractOpen: boolean;
|
||||
@ -56,6 +64,7 @@ export interface ITerminal {
|
||||
currDir: string;
|
||||
|
||||
print(s: string): void;
|
||||
printRaw(node: React.ReactNode): void;
|
||||
error(s: string): void;
|
||||
|
||||
clear(): void;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ITerminal, Output, Link, TTimer } from "./ITerminal";
|
||||
import { ITerminal, Output, Link, RawOutput, TTimer } from "./ITerminal";
|
||||
import { IRouter } from "../ui/Router";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
@ -77,7 +77,7 @@ export class Terminal implements ITerminal {
|
||||
commandHistory: string[] = [];
|
||||
commandHistoryIndex = 0;
|
||||
|
||||
outputHistory: (Output | Link)[] = [new Output(`Bitburner v${CONSTANTS.Version}`, "primary")];
|
||||
outputHistory: (Output | Link | RawOutput)[] = [new Output(`Bitburner v${CONSTANTS.Version}`, "primary")];
|
||||
|
||||
// True if a Coding Contract prompt is opened
|
||||
contractOpen = false;
|
||||
@ -93,7 +93,7 @@ export class Terminal implements ITerminal {
|
||||
TerminalEvents.emit();
|
||||
}
|
||||
|
||||
append(item: Output | Link): void {
|
||||
append(item: Output | Link | RawOutput): void {
|
||||
this.outputHistory.push(item);
|
||||
if (this.outputHistory.length > Settings.MaxTerminalCapacity) {
|
||||
this.outputHistory.splice(0, this.outputHistory.length - Settings.MaxTerminalCapacity);
|
||||
@ -105,6 +105,10 @@ export class Terminal implements ITerminal {
|
||||
this.append(new Output(s, "primary"));
|
||||
}
|
||||
|
||||
printRaw(node: React.ReactNode): void {
|
||||
this.append(new RawOutput(node));
|
||||
}
|
||||
|
||||
error(s: string): void {
|
||||
this.append(new Output(s, "error"));
|
||||
}
|
||||
@ -428,10 +432,10 @@ export class Terminal implements ITerminal {
|
||||
case CodingContractResult.Failure:
|
||||
++contract.tries;
|
||||
if (contract.tries >= contract.getMaxNumTries()) {
|
||||
this.print("Contract FAILED - Contract is now self-destructing");
|
||||
this.error("Contract FAILED - Contract is now self-destructing");
|
||||
serv.removeContract(contract);
|
||||
} else {
|
||||
this.print(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`);
|
||||
this.error(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`);
|
||||
}
|
||||
break;
|
||||
case CodingContractResult.Cancelled:
|
||||
@ -580,7 +584,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length === 1 && commandArray[0] == "help") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -588,7 +592,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length === 1 && commandArray[0] == "ls") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -596,7 +600,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length === 1 && commandArray[0] == "scan") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -604,7 +608,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 1 && commandArray[0] == "scan-analyze") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -612,7 +616,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 2 && commandArray[0] == "scan-analyze" && commandArray[1] === 2) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -624,11 +628,11 @@ export class Terminal implements ITerminal {
|
||||
) {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Wrong command! Try again!");
|
||||
this.error("Wrong command! Try again!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -636,7 +640,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length === 1 && commandArray[0] === "analyze") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -644,7 +648,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "NUKE.exe") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -652,7 +656,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 1 && commandArray[0] == "hack") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -660,7 +664,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 1 && commandArray[0] == "home") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -668,7 +672,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -676,7 +680,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 1 && commandArray[0] == "free") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -684,7 +688,7 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -692,12 +696,12 @@ export class Terminal implements ITerminal {
|
||||
if (commandArray.length == 2 && commandArray[0] == "tail" && commandArray[1] == "n00dles.script") {
|
||||
iTutorialNextStep();
|
||||
} else {
|
||||
this.print("Bad command. Please follow the tutorial");
|
||||
this.error("Bad command. Please follow the tutorial");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.print("Please follow the tutorial, or click 'EXIT' if you'd like to skip it");
|
||||
this.error("Please follow the tutorial, or click 'EXIT' if you'd like to skip it");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ export function analyze(
|
||||
args: (string | number)[],
|
||||
): void {
|
||||
if (args.length !== 0) {
|
||||
terminal.print("Incorrect usage of analyze command. Usage: analyze");
|
||||
terminal.error("Incorrect usage of analyze command. Usage: analyze");
|
||||
return;
|
||||
}
|
||||
terminal.startAnalyze();
|
||||
|
@ -13,7 +13,7 @@ export function backdoor(
|
||||
args: (string | number)[],
|
||||
): void {
|
||||
if (args.length !== 0) {
|
||||
terminal.print("Incorrect usage of backdoor command. Usage: backdoor");
|
||||
terminal.error("Incorrect usage of backdoor command. Usage: backdoor");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ export function kill(
|
||||
if (res) {
|
||||
terminal.print(`Killing script with PID ${pid}`);
|
||||
} else {
|
||||
terminal.print(`Failed to kill script with PID ${pid}. No such script exists`);
|
||||
terminal.error(`Failed to kill script with PID ${pid}. No such script exists`);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React from "react";
|
||||
import { ITerminal } from "../ITerminal";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath } from "../../Terminal/DirectoryHelpers";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
export function ls(
|
||||
terminal: ITerminal,
|
||||
@ -112,7 +114,7 @@ export function ls(
|
||||
allMessages.sort();
|
||||
folders.sort();
|
||||
|
||||
function postSegments(segments: string[]): void {
|
||||
function postSegments(segments: string[], style?: any): void {
|
||||
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
|
||||
const filesPerRow = Math.floor(80 / maxLength);
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
@ -124,23 +126,27 @@ export function ls(
|
||||
i++;
|
||||
}
|
||||
i--;
|
||||
terminal.print(row);
|
||||
if (!style) {
|
||||
terminal.print(row);
|
||||
} else {
|
||||
terminal.printRaw(<span style={style}>{row}</span>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const groups = [
|
||||
{ segments: folders },
|
||||
{ segments: folders, style: { color: "cyan" } },
|
||||
{ segments: allMessages },
|
||||
{ segments: allTextFiles },
|
||||
{ segments: allPrograms },
|
||||
{ segments: allContracts },
|
||||
{ segments: allScripts },
|
||||
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" } },
|
||||
].filter((g) => g.segments.length > 0);
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
if (i !== 0) {
|
||||
terminal.print("");
|
||||
terminal.print("");
|
||||
}
|
||||
postSegments(groups[i].segments);
|
||||
postSegments(groups[i].segments, groups[i].style);
|
||||
}
|
||||
}
|
||||
|
@ -41,5 +41,5 @@ export function runProgram(
|
||||
}
|
||||
}
|
||||
|
||||
terminal.print("Invalid executable. Cannot be run");
|
||||
terminal.error("Invalid executable. Cannot be run");
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export function runScript(
|
||||
|
||||
// Check if this script is already running
|
||||
if (findRunningScript(scriptName, args, server) != null) {
|
||||
terminal.print("ERROR: This script is already running. Cannot run multiple instances");
|
||||
terminal.error("This script is already running. Cannot run multiple instances");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -56,12 +56,12 @@ export function runScript(
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
|
||||
if (!server.hasAdminRights) {
|
||||
terminal.print("Need root access to run script");
|
||||
terminal.error("Need root access to run script");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ramUsage > ramAvailable) {
|
||||
terminal.print(
|
||||
terminal.error(
|
||||
"This machine does not have enough RAM to run this script with " +
|
||||
numThreads +
|
||||
" threads. Script requires " +
|
||||
@ -90,5 +90,5 @@ export function runScript(
|
||||
return;
|
||||
}
|
||||
|
||||
terminal.print("ERROR: No such script");
|
||||
terminal.error("No such script");
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export function wget(
|
||||
const url = args[0] + "";
|
||||
const target = terminal.getFilepath(args[1] + "");
|
||||
if (!isScriptFilename(target) && !target.endsWith(".txt")) {
|
||||
return terminal.print(`wget failed: Invalid target file. Target file must be script or text file`);
|
||||
return terminal.error(`wget failed: Invalid target file. Target file must be script or text file`);
|
||||
}
|
||||
$.get(
|
||||
url,
|
||||
@ -31,7 +31,7 @@ export function wget(
|
||||
res = server.writeToTextFile(target, data);
|
||||
}
|
||||
if (!res.success) {
|
||||
return terminal.print("wget failed");
|
||||
return terminal.error("wget failed");
|
||||
}
|
||||
if (res.overwritten) {
|
||||
return terminal.print(`wget successfully retrieved content and overwrote ${target}`);
|
||||
|
@ -7,7 +7,7 @@ import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import Box from "@mui/material/Box";
|
||||
import { ITerminal, Output, Link } from "../ITerminal";
|
||||
import { ITerminal, Output, Link, RawOutput } from "../ITerminal";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { TerminalInput } from "./TerminalInput";
|
||||
@ -94,6 +94,14 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
|
||||
</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
if (item instanceof RawOutput)
|
||||
return (
|
||||
<ListItem key={i} classes={{ root: classes.nopadding }}>
|
||||
<Typography classes={{ root: classes.preformatted }} paragraph={false}>
|
||||
{item.raw}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
if (item instanceof Link)
|
||||
return (
|
||||
<ListItem key={i} classes={{ root: classes.nopadding }}>
|
||||
|
31
src/ui/ActiveScripts/ActiveScriptsPage.tsx
Normal file
31
src/ui/ActiveScripts/ActiveScriptsPage.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Root React Component for the "Active Scripts" UI page. This page displays
|
||||
* and provides information about all of the player's scripts that are currently running
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { ScriptProduction } from "./ScriptProduction";
|
||||
import { ServerAccordions } from "./ServerAccordions";
|
||||
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
workerScripts: Map<number, WorkerScript>;
|
||||
}
|
||||
|
||||
export function ActiveScriptsPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
This page displays a list of all of your scripts that are currently running across every machine. It also
|
||||
provides information about each script's production. The scripts are categorized by the hostname of the servers
|
||||
on which they are running.
|
||||
</Typography>
|
||||
|
||||
<ScriptProduction />
|
||||
<ServerAccordions {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -3,17 +3,16 @@
|
||||
* and provides information about all of the player's scripts that are currently running
|
||||
*/
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
|
||||
import { ScriptProduction } from "./ScriptProduction";
|
||||
import { ServerAccordions } from "./ServerAccordions";
|
||||
|
||||
import { ActiveScriptsPage } from "./ActiveScriptsPage";
|
||||
import { RecentScriptsPage } from "./RecentScriptsPage";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
type IProps = {
|
||||
interface IProps {
|
||||
workerScripts: Map<number, WorkerScript>;
|
||||
};
|
||||
}
|
||||
|
||||
export function ActiveScriptsRoot(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
@ -26,17 +25,19 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const [tab, setTab] = useState<"active" | "recent">("active");
|
||||
function handleChange(event: React.SyntheticEvent, tab: "active" | "recent"): void {
|
||||
setTab(tab);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">Active Scripts</Typography>
|
||||
<Typography>
|
||||
This page displays a list of all of your scripts that are currently running across every machine. It also
|
||||
provides information about each script's production. The scripts are categorized by the hostname of the servers
|
||||
on which they are running.
|
||||
</Typography>
|
||||
<Tabs variant="fullWidth" value={tab} onChange={handleChange}>
|
||||
<Tab label={"Active"} value={"active"} />
|
||||
<Tab label={"Recent"} value={"recent"} />
|
||||
</Tabs>
|
||||
|
||||
<ScriptProduction />
|
||||
<ServerAccordions {...props} />
|
||||
{tab === "active" && <ActiveScriptsPage workerScripts={props.workerScripts} />}
|
||||
{tab === "recent" && <RecentScriptsPage />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
165
src/ui/ActiveScripts/RecentScriptAccordion.tsx
Normal file
165
src/ui/ActiveScripts/RecentScriptAccordion.tsx
Normal file
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* React Component for displaying a single WorkerScript's info as an
|
||||
* Accordion element
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
|
||||
import Table from "@mui/material/Table";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { arrayToString } from "../../utils/helpers/arrayToString";
|
||||
import { Money } from "../React/Money";
|
||||
import { MoneyRate } from "../React/MoneyRate";
|
||||
import { RecentScript } from "../..//Netscript/RecentScripts";
|
||||
import { LogBoxEvents } from "../React/LogBoxManager";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
noborder: {
|
||||
borderBottom: "none",
|
||||
},
|
||||
});
|
||||
|
||||
interface IProps {
|
||||
recentScript: RecentScript;
|
||||
}
|
||||
|
||||
export function RecentScriptAccordion(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const recentScript = props.recentScript;
|
||||
|
||||
// Calculations for script stats
|
||||
const onlineMps = recentScript.runningScript.onlineMoneyMade / recentScript.runningScript.onlineRunningTime;
|
||||
const onlineEps = recentScript.runningScript.onlineExpGained / recentScript.runningScript.onlineRunningTime;
|
||||
|
||||
function logClickHandler(): void {
|
||||
LogBoxEvents.emit(recentScript.runningScript);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)} component={Paper}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography>
|
||||
└ {recentScript.filename} (died{" "}
|
||||
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timestamp.getTime())} ago)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Collapse in={open} timeout={0} unmountOnExit>
|
||||
<Box mx={6}>
|
||||
<Table padding="none" size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Threads:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>{numeralWrapper.formatThreads(recentScript.runningScript.threads)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder} colSpan={2}>
|
||||
<Typography>└ Args: {arrayToString(recentScript.args)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Online Time:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>
|
||||
{convertTimeMsToTimeElapsedString(recentScript.runningScript.onlineRunningTime * 1e3)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Offline Time:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>
|
||||
{convertTimeMsToTimeElapsedString(recentScript.runningScript.offlineRunningTime * 1e3)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Total online production:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography>
|
||||
<Money money={recentScript.runningScript.onlineMoneyMade} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder} colSpan={1} />
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography>
|
||||
{numeralWrapper.formatExp(recentScript.runningScript.onlineExpGained) + " hacking exp"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Online production rate:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography>
|
||||
<MoneyRate money={onlineMps} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder} colSpan={1} />
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography> {numeralWrapper.formatExp(onlineEps) + " hacking exp / sec"}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder}>
|
||||
<Typography>└ Total offline production:</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography>
|
||||
<Money money={recentScript.runningScript.offlineMoneyMade} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder} colSpan={1} />
|
||||
<TableCell className={classes.noborder} align="left">
|
||||
<Typography>
|
||||
{numeralWrapper.formatExp(recentScript.runningScript.offlineExpGained) + " hacking exp"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Button onClick={logClickHandler}>LOG</Button>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</>
|
||||
);
|
||||
}
|
20
src/ui/ActiveScripts/RecentScriptsPage.tsx
Normal file
20
src/ui/ActiveScripts/RecentScriptsPage.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Root React Component for the "Active Scripts" UI page. This page displays
|
||||
* and provides information about all of the player's scripts that are currently running
|
||||
*/
|
||||
import React from "react";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import { recentScripts } from "../../Netscript/RecentScripts";
|
||||
import { RecentScriptAccordion } from "./RecentScriptAccordion";
|
||||
|
||||
export function RecentScriptsPage(): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Typography>List of all recently killed scripts.</Typography>
|
||||
{recentScripts.map((r) => (
|
||||
<RecentScriptAccordion key={r.pid} recentScript={r} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
@ -76,6 +76,8 @@ import { InvitationModal } from "../Faction/ui/InvitationModal";
|
||||
import { enterBitNode } from "../RedPill";
|
||||
import { Context } from "./Context";
|
||||
|
||||
const htmlLocation = location;
|
||||
|
||||
interface IProps {
|
||||
terminal: ITerminal;
|
||||
player: IPlayer;
|
||||
@ -215,6 +217,14 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
return ITutorialEvents.subscribe(rerender);
|
||||
}, []);
|
||||
|
||||
function killAllScripts(): void {
|
||||
for (const server of GetAllServers()) {
|
||||
server.runningScripts = [];
|
||||
}
|
||||
saveObject.saveGame();
|
||||
setTimeout(() => htmlLocation.reload(), 2000);
|
||||
}
|
||||
|
||||
Router = {
|
||||
page: () => page,
|
||||
toActiveScripts: () => setPage(Page.ActiveScripts),
|
||||
@ -288,7 +298,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
<Context.Router.Provider value={Router}>
|
||||
<Overview>
|
||||
{!ITutorial.isRunning ? (
|
||||
<CharacterOverview save={() => saveObject.saveGame()} />
|
||||
<CharacterOverview save={() => saveObject.saveGame()} killScripts={killAllScripts} />
|
||||
) : (
|
||||
<InteractiveTutorialRoot />
|
||||
)}
|
||||
@ -372,12 +382,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
player={player}
|
||||
save={() => saveObject.saveGame()}
|
||||
export={() => saveObject.exportGame()}
|
||||
forceKill={() => {
|
||||
for (const server of GetAllServers()) {
|
||||
server.runningScripts = [];
|
||||
}
|
||||
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
|
||||
}}
|
||||
forceKill={killAllScripts}
|
||||
softReset={() => {
|
||||
dialogBoxCreate("Soft Reset!");
|
||||
prestigeAugmentation();
|
||||
|
@ -265,7 +265,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
content: (
|
||||
<>
|
||||
<Typography>You now have root access! You can hack the server using </Typography>
|
||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> hack"}</Typography>
|
||||
<Typography classes={{ root: classes.textfield }}>{"[n00dles ~/]> hack"}</Typography>
|
||||
|
||||
<Typography> Try doing that now.</Typography>
|
||||
</>
|
||||
@ -296,7 +296,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
content: (
|
||||
<>
|
||||
<Typography>From any server you can get back home using</Typography>
|
||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> home"}</Typography>
|
||||
<Typography classes={{ root: classes.textfield }}>{"[n00dles ~/]> home"}</Typography>
|
||||
|
||||
<Typography>Let's head home before creating our first script!</Typography>
|
||||
</>
|
||||
@ -468,7 +468,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
[iTutorialSteps.HacknetNodesIntroduction as number]: {
|
||||
content: (
|
||||
<Typography>
|
||||
here you can purchase new Hacknet Nodes and upgrade your existing ones. Let's purchase a new one now.
|
||||
Here you can purchase new Hacknet Nodes and upgrade your existing ones. Let's purchase a new one now.
|
||||
</Typography>
|
||||
),
|
||||
canNext: true,
|
||||
|
@ -6,23 +6,24 @@ import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Reputation } from "./Reputation";
|
||||
import { KillScriptsModal } from "./KillScriptsModal";
|
||||
|
||||
import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../Context";
|
||||
|
||||
interface IProps {
|
||||
save: () => void;
|
||||
killScripts: () => void;
|
||||
}
|
||||
|
||||
function Intelligence(): React.ReactElement {
|
||||
@ -138,7 +139,8 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
}),
|
||||
);
|
||||
|
||||
export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
export function CharacterOverview({ save, killScripts }: IProps): React.ReactElement {
|
||||
const [killOpen, setKillOpen] = useState(false);
|
||||
const player = use.Player();
|
||||
|
||||
const setRerender = useState(false)[1];
|
||||
@ -150,162 +152,162 @@ export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Paper square>
|
||||
<Box m={1}>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>HP </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>
|
||||
{numeralWrapper.formatHp(player.hp)} / {numeralWrapper.formatHp(player.max_hp)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-hp-hook" classes={{ root: classes.hp }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<>
|
||||
<Table sx={{ display: "block", m: 1 }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>HP </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.hp }}>
|
||||
{numeralWrapper.formatHp(player.hp)} / {numeralWrapper.formatHp(player.max_hp)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-hp-hook" classes={{ root: classes.hp }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>Money </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>
|
||||
{numeralWrapper.formatMoney(player.money.toNumber())}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-money-hook" classes={{ root: classes.money }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>Money </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.money }}>
|
||||
{numeralWrapper.formatMoney(player.money.toNumber())}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-money-hook" classes={{ root: classes.money }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>Hack </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>
|
||||
{numeralWrapper.formatSkill(player.hacking_skill)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-hack-hook" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>Hack </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.hack }}>
|
||||
{numeralWrapper.formatSkill(player.hacking_skill)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-hack-hook" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Str </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.strength)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-str-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Str </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.strength)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-str-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Def </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.defense)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-def-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Def </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.defense)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-def-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Dex </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>
|
||||
{numeralWrapper.formatSkill(player.dexterity)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-dex-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>Agi </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.agility)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-agi-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>Dex </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.dexterity)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-dex-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>Agi </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography classes={{ root: classes.combat }}>{numeralWrapper.formatSkill(player.agility)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-agi-hook" classes={{ root: classes.combat }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>Cha </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>{numeralWrapper.formatSkill(player.charisma)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-cha-hook" classes={{ root: classes.cha }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Intelligence />
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>Cha </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography classes={{ root: classes.cha }}>{numeralWrapper.formatSkill(player.charisma)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography id="overview-cha-hook" classes={{ root: classes.cha }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Intelligence />
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-0" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row" align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-1" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row" align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-2" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Work />
|
||||
<Bladeburner />
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-0" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row" align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-1" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell component="th" scope="row" align="right" classes={{ root: classes.cell }}>
|
||||
<Typography id="overview-extra-hook-2" classes={{ root: classes.hack }}>
|
||||
{/*Hook for player scripts*/}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Work />
|
||||
<Bladeburner />
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
||||
<IconButton onClick={save}>
|
||||
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
<TableRow>
|
||||
<TableCell align="center" classes={{ root: classes.cellNone }}>
|
||||
<IconButton onClick={save}>
|
||||
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<TableCell align="center" classes={{ root: classes.cellNone }}>
|
||||
<IconButton onClick={() => setKillOpen(true)}>
|
||||
<ClearAllIcon color="error" />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<KillScriptsModal open={killOpen} onClose={() => setKillOpen(false)} killScripts={killScripts} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
24
src/ui/React/KillScriptsModal.tsx
Normal file
24
src/ui/React/KillScriptsModal.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { Modal } from "./Modal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
killScripts: () => void;
|
||||
}
|
||||
|
||||
export function KillScriptsModal(props: IProps): React.ReactElement {
|
||||
function onClick(): void {
|
||||
props.killScripts();
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>Forcefully kill all running scripts? This will also save your game and reload the game.</Typography>
|
||||
<Button onClick={onClick}>KILL</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,17 +1,29 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import Box from "@mui/material/Box";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Fab from "@mui/material/Fab";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||
import { use } from "../Context";
|
||||
import { Page } from "../Router";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
nobackground: {
|
||||
backgroundColor: "#0000",
|
||||
visibilityToggle: {
|
||||
backgroundColor: "transparent",
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
right: 0,
|
||||
},
|
||||
overviewContainer: {
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
right: 0,
|
||||
zIndex: 1500,
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
flexDirection: "column",
|
||||
},
|
||||
});
|
||||
|
||||
@ -30,16 +42,13 @@ export function Overview({ children }: IProps): React.ReactElement {
|
||||
} else {
|
||||
icon = <VisibilityIcon color="primary" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, right: 0, zIndex: 1500 }}>
|
||||
<Box display="flex" justifyContent="flex-end" flexDirection={"column"}>
|
||||
<Collapse in={open}>{children}</Collapse>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Fab classes={{ root: classes.nobackground }} onClick={() => setOpen((old) => !old)}>
|
||||
{icon}
|
||||
</Fab>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
<Paper square classes={{ root: classes.overviewContainer }}>
|
||||
<Collapse in={open}>{children}</Collapse>
|
||||
<Fab size="small" classes={{ root: classes.visibilityToggle }} onClick={() => setOpen((old) => !old)}>
|
||||
{icon}
|
||||
</Fab>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
}
|
@ -302,7 +302,7 @@ export function refreshTheme(): void {
|
||||
border: "1px solid " + Settings.theme.well,
|
||||
},
|
||||
standardSuccess: {
|
||||
color: Settings.theme.primaryLight,
|
||||
color: Settings.theme.primarylight,
|
||||
},
|
||||
standardError: {
|
||||
color: Settings.theme.errorlight,
|
||||
|
@ -222,7 +222,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
|
||||
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
|
||||
function cancel(): void {
|
||||
player.finishWork(true);
|
||||
player.finishWorkPartTime(true);
|
||||
router.toJob();
|
||||
}
|
||||
function unfocus(): void {
|
||||
|
Loading…
Reference in New Issue
Block a user