Merge branch 'dev' into bugfix/corp-api-fixes

This commit is contained in:
hydroflame 2022-03-07 17:43:29 -05:00 committed by GitHub
commit e10571c0e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 701 additions and 229 deletions

4
dist/bitburner.d.ts vendored

@ -3748,7 +3748,7 @@ export declare interface NS extends Singularity {
* @remarks
* RAM cost: 0.3 GB
*
* Running with no args returns curent script.
* Running with no args returns current script.
* If you use a PID as the first parameter, the hostname and args parameters are unnecessary.
*
* @param filename - Optional. Filename or PID of the script.
@ -4807,7 +4807,7 @@ export declare interface Server {
/** IP Address. Must be unique */
ip: string;
/** Flag indicating whether player is curently connected to this server */
/** Flag indicating whether player is currently connected to this server */
isConnectedTo: boolean;
/** RAM (GB) available on this server */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -215,6 +215,7 @@ The list contains the name of (i.e. the value returned by
| | | The answer should be provided as an array of strings containing the valid expressions. |
| | | |
| | | NOTE: Numbers in an expression cannot have leading 0's |
| | | NOTE: The order of evaluation expects script operator precedence |
| | | |
| | | Examples: |
| | | Input: digits = "123", target = 6 |

@ -7,6 +7,16 @@ these companies, you can apply for jobs.
Working a job lets you earn money, experience, and reputation with that company.
While working for a company, you can click "Do something else simultaneously" to be able
to do things while you continue to work in the background. There is a 20% penalty to the
related gains. Clicking the "Focus" button under the overview will return you to the
current work.
Reputation is required to apply for a promotion. This reputation is not counted towards
your career until the shift ends, either due to the time spent or clicking the
"Stop Working" button. For most jobs there is a penalty of 50% of the reputation gained
if you stop your shift early.
Information about all Companies
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TODO

@ -107,3 +107,14 @@ starting security, rounded to the nearest integer. To be more precise::
This means that a server's security level will not fall below this
value if you are trying to weaken() it.
Backdoors
^^^^^^^^^
Servers that can be hacked can also have backdoors installed. These backdoors
will provide you with a benefit; the services may be cheaper, penalties may
be reduced or there may be other results. Honeypots exist and will let factions
know when you have succeeded at backdooring their system. Once you have a
backdoor installed, you can connect to that server directly.
When you visit a location in the city and see that the name is partially scrambled,
this indicates that you have backdoored the server related to the location.

@ -3,6 +3,153 @@
Changelog
=========
v1.5.0 - Steam Cloud integration
--------------------------------
** Misc. **
* The file API now allows GET and DELETE (@lordducky)
* Fix bug with async.
* Update documentation / typo (@lethern, @Meowdoleon, @JohnnyUrosevic, @JosephDavidTalbot,
@pd, @lethern, @lordducky, @zeddrak, @fearnlj01, @reasonablytall)
* Fix bug with corp API (@pigalot)
* background now matches game primary color (@nickofolas)
* page title contains version (@MartinFourier)
* Major text editor improvements (@nickofolas)
* Force achievement calculation on BN completion (@SagePtr)
* Cleanup in repository (@MartinFourier)
* Several improvements to the electron version (@MartinFourier)
* Add 'printf' ns function (@Ninetailed)
* Display bonus time on sleeve page (@MartinFourier)
* Several UI improvements (@nickofolas, @smolgumball)
* Fix bug with casino roulette (@jamie-mac)
* Remove blob caching.
* Fix formulas access check (@Ornedan)
* Fix bug in exp calculation (@qcorradi)
* Fix NaN comparison (@qcorradi)
* Terminal history persists in savefile (@MartinFourier)
* Fix travelToCity with bad argument (@SlyCedix)
* Fix aug display in alpha (@Dominik Winter)
* Add smart supply func to corp API (@pd)
* Fix tests (@jamie-mac)
* Nerf noodle bar.
v1.4.0 - 2022-01-18 Sharing is caring
-------------------------------------
** Computer sharing **
* A new mechanic has been added, it's is invoked by calling the new function 'share'.
This mechanic helps you farm reputation faster.
** gang **
* Installing augs means losing a little bit of ascension multipliers.
** Misc. **
* Prevent gang API from performing actions for the type of gang they are not. (@TheMas3212)
* Fix donation to gang faction. (@TheMas3212)
* Fix gang check crashing the game. (@TheMas3212)
* Make time compression more robust.
* Fix bug with scp.
* Add zoom to steam version. (@MartinFourier)
* Fix donateToFaction accepts donation of NaN. (@woody-lam-cwl)
* Show correct hash capacity gain on cache level upgrade tooltip. (@woody-lam-cwl)
* Fix tests (@woody-lam-cwl)
* Fix cache tooltip (@woody-lam-cwl)
* Added script to prettify save file for debugging (@MartinFourier)
* Update documentation / typos (@theit8514, @thadguidry, @tigercat2000, @SlyCedix, @Spacejoker, @KenJohansson,
@Ornedan, @JustAnOkapi, @nickofolas, @philarmstead, @TheMas3212, @dcragusa, @XxKingsxX-Pinu,
@paiv, @smolgumball, @zeddrak, @stinky-lizard, @nickofolas, @Feodoric, @daanflore,
@markusariliu, @mstruebing, @erplsf, @waffleattack, @Dexalt142, @AIT-OLPE, @deathly809, @BuckAMayzing,
@MartinFourier, @pigalot, @lethern)
* Fix BN3+ achievement (@SagePtr)
* Fix reputation carry over bug (@TheMas3212)
* Add button to exit infiltrations (@TheMas3212)
* Add dev menu achievement check (@TheMas3212)
* Add 'host' config for electron server (@MartinFourier)
* Suppress save toast only works for autosave (@MartinFourier)
* Fix some achievements not triggering with 'backdoor' (@SagePtr)
* Update Neuroflux Governor description.
* Fix bug with electron server.
* Fix bug with corporation employee assignment function (@Ornedan)
* Add detailed information to terminal 'mem' command (@MartinFourier)
* Add savestamp to savefile (@MartinFourier)
* Dev menu can apply export bonus (@MartinFourier)
* Icarus message no longer applies on top of itself (@Feodoric)
* purchase augment via API can no longer buy Neuroflux when it shouldn't (@Feodoric)
* Syntax highlighter should be smarter (@neuralsim)
* Fix some miscalculation when calculating money stolen (@zeddrak)
* Fix max cache achievement working with 0 cache (@MartinFourier)
* Add achievements in the game, not just steam (@MartinFourier)
* Overflow hash converts to money automatically (@MartinFourier)
* Make mathjax load locally (@MartinFourier)
* Make favor calculation more efficient (@kittycat2002)
* Fix some scripts crashing the game on startup (@MartinFourier)
* Toasts will appear above tail window (@MartinFourier)
* Fix issue that can cause terminal actions to start on one server and end on another (@MartinFourier)
* Fix 'fileExists' not correctly matching file names (@TheMas3212)
* Refactor some code to be more efficient (@TheMas3212)
* Fix exp gain for terminal grow and weaken (@nickofolas)
* Refactor script death code to reject waiting promises instead of resolving (@Ornedan)
* HP recalculates on defense exp gain (@TheMas3212)
* Fix log for ascendMember (@TheMas3212)
* Netscript ports clear on reset (@TheMas3212)
* Fix bug related to company (@TheMas3212)
* Fix bug where corporation handbook would not be correctly added (@TheMas3212)
* Servers in hash upgrades are sorted alpha (@MartinFourier)
* Fix very old save not properly migrating augmentation renamed in 0.56 (@MartinFourier)
* Add font height and line height in theme settings (@MartinFourier)
* Fix crash when quitting job (@MartinFourier)
* Added save file validation system (@TheMas3212)
* React and ReactDOM are now global objects (@pigalot)
* 'nano' supports globs (@smolgumball)
* Character overview can be dragged (@MartinFourier)
* Job page updates in real time (@nickofolas)
* Company favor gain uses the same calculation as faction, this is just performance
the value didn't change (@nickofolas)
* ns2 files work with more import options (@theit8514)
* Allow autocomplete for partial executables (@nickofolas)
* Add support for contract completion (@nickofolas)
* 'ls' link are clickable (@smolgumball)
* Prevent steam from opening external LOCAL files (@MartinFourier)
* Fix a bug with autocomplete (@Feodoric)
* Optimise achievement checks (@Feodoric)
* Hacknet server achievements grant associated hacknet node achievement (@Feodoric)
* Fix display bug with hacknet (@Feodoric)
* 'analyze' now says if the server is backdoored (@deathly809)
* Add option to exclude running script from save (@MartinFourier)
* Game now catches more errors and redirects to recovery page (@MartinFourier)
* Fix bug with autocomplete (@nickofolas)
* Add tooltip to unfocus work (@nickofolas)
* Add detailst overview (@MartinFourier)
* Fix focus bug (@deathly809)
* Fix some NaN handling (@deathly809)
* Added 'mv' ns function (@deathly809)
* Add focus argument to some singularity functions (@nickofolas)
* Fix some functions not disabling log correctly (@deathly809)
* General UI improvements (@nickofolas)
* Handle steamworks errors gravefully (@MartinFourier)
* Fix some react component not unmounting correctly (@MartinFourier)
* 'help' autocompletes (@nickofolas)
* No longer push all achievements to steam (@Ornedan)
* Recovery page has more information (@MartinFourier)
* Added 'getGameInfo' ns function (@MartinFourier)
* SF3.3 unlocks all corp API (@pigalot)
* Major improvements to corp API (@pigalot)
* Prevent seed money outside BN3 (@pigalot)
* Fix bug where using keyboard shortcuts would crash if the feature is not available (@MartinFourier)\
* Sidebar remains opened/closed on save (@MartinFourier)
* Added tooltip to sidebar when closed (@MartinFourier)
* Fix bug where Formulas.exe is not available when starting BN5 (@TheMas3212)
* Fix CI (@tvanderpol)
* Change shortcuts to match sidebar (@MartinFourier)
* Format gang respect (@attrib)
* Add modal to text editor with ram details (@nickofolas)
* Fix several bugs with singularity focus (@nickofolas)
* Nerf noodle bar.
v1.3.0 - 2022-01-04 Cleaning up
-------------------------------

@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
# built documents.
#
# The short X.Y version.
version = '1.3'
version = '1.4'
# The full version, including alpha/beta/rc tags.
release = '1.3.0'
release = '1.4.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

@ -117,7 +117,7 @@ Source-File
:Max Level: 3
This Source-File lets you access and use the Singularity Functions in other BitNodes.
Each level of this Source-File will open up more Singularity Functions that you can use.
Each level of this Source-File will reduce RAM costs.
Difficulty:
Depending on what Source-Files you have unlocked before attempting this BitNode,

@ -29,6 +29,11 @@ const windowTracker = (windowName) => {
};
const saveState = debounce(() => {
if (!window || window.isDestroyed()) {
log.silly(`Saving window state failed because window is not available`);
return;
}
if (!windowState.isMaximized) {
windowState = window.getBounds();
}
@ -41,7 +46,7 @@ const windowTracker = (windowName) => {
const track = (win) => {
window = win;
['resize', 'move', 'close'].forEach((event) => {
["resize", "move", "close"].forEach((event) => {
win.on(event, saveState);
});
};

@ -73,5 +73,5 @@
<link rel="shortcut icon" href="favicon.ico"></head>
<body>
<div id="root"/>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/main.bundle.js"></script></body>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
</html>

20
main.bundle.js Normal file

File diff suppressed because one or more lines are too long

1
main.bundle.js.map Normal file

File diff suppressed because one or more lines are too long

29
package-lock.json generated

@ -1,11 +1,12 @@
{
"name": "bitburner",
"version": "1.3.0",
"version": "1.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.3.0",
"name": "bitburner",
"version": "1.4.0",
"hasInstallScript": true,
"license": "SEE LICENSE IN license.txt",
"dependencies": {
@ -9645,9 +9646,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true,
"funding": [
{
@ -20380,9 +20381,9 @@
"dev": true
},
"node_modules/url-parse": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"dependencies": {
"querystringify": "^2.1.1",
@ -29959,9 +29960,9 @@
}
},
"follow-redirects": {
"version": "1.14.7",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
"version": "1.14.8",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"dev": true
},
"for-in": {
@ -38392,9 +38393,9 @@
"dev": true
},
"url-parse": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",

@ -1,7 +1,7 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "1.3.0",
"version": "1.4.0",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie & Olivier Gagnon"
@ -114,6 +114,7 @@
"lint": "eslint --fix . --ext js,jsx,ts,tsx",
"lint:report": "eslint --ext js,jsx,ts,tsx .",
"preinstall": "node ./tools/engines-check/engines-check.js",
"postinstall": "cd electron && npm install",
"test": "jest",
"test:watch": "jest --watch",
"watch": "webpack --watch --mode production",

@ -110,7 +110,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
<br />
<br />
It is recommended to install several Augmentations at once. Preferably everything from any faction of your
chosing.
choosing.
</>
}
/>

@ -670,7 +670,6 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.PurchasedServerSoftcap = 2;
@ -694,7 +693,6 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.InfiltrationMoney = 0.75;
BitNodeMultipliers.CorporationValuation = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.FourSigmaMarketDataCost = 2;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;

@ -8,38 +8,37 @@ import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import { Settings } from "../../Settings/Settings";
import Button from "@mui/material/Button";
const useStyles = makeStyles(() =>
createStyles({
level0: {
color: "red",
portal: {
cursor: "pointer",
fontFamily: "inherit",
fontSize: "1rem",
fontWeight: "bold",
lineHeight: 1,
padding: 0,
"&:hover": {
color: "#fff",
},
},
level0: {
color: "red",
},
level1: {
color: "yellow",
cursor: "pointer",
"&:hover": {
color: "#fff",
},
},
level2: {
color: "#48d1cc",
cursor: "pointer",
"&:hover": {
color: "#fff",
},
},
level3: {
color: "blue",
cursor: "pointer",
"&:hover": {
color: "#fff",
},
},
}),
);
@ -71,6 +70,7 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
if (props.level === 2) {
cssClass = classes.level2;
}
cssClass = `${classes.portal} ${cssClass}`
return (
<>
@ -85,9 +85,24 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
</Typography>
}
>
<span onClick={() => setPortalOpen(true)} className={cssClass} aria-label={`enter-bitnode-${bitNode.number.toString()}`}>
<b>O</b>
</span>
{Settings.DisableASCIIArt ? (
<Button
onClick={() => setPortalOpen(true)}
sx={{ m: 2 }}
aria-description={bitNode.desc}
>
<Typography>BitNode-{bitNode.number.toString()}: {bitNode.name}</Typography>
</Button>
) : (
<IconButton
onClick={() => setPortalOpen(true)}
className={cssClass}
aria-label={`BitNode-${bitNode.number.toString()}: ${bitNode.name}`}
aria-description={bitNode.desc}
>
O
</IconButton>
)}
</Tooltip>
<PortalModal
open={portalOpen}
@ -98,6 +113,10 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
destroyedBitNode={props.destroyedBitNode}
flume={props.flume}
/>
{Settings.DisableASCIIArt && (
<br/>
)}
</>
);
}
@ -151,63 +170,104 @@ export function BitverseRoot(props: IProps): React.ReactElement {
);
}
return (
if (Settings.DisableASCIIArt) {
return (
<>
{Object.values(BitNodes).filter((node) => {
console.log(node.desc);
return node.desc !== 'COMING SOON';
}).map((node) => {
return (
<BitNodePortal key={node.number} n={node.number} level={nextSourceFileFlags[node.number]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />
)
})}
<br />
<br />
<br />
<br />
<CinematicText lines={[
"> Many decades ago, a humanoid extraterrestrial species which we call the Enders descended on the Earth...violently",
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
"> Instead of killing every last one of us, the human race was enslaved...",
"> We were shackled in a digital world, chained into a prison for our minds...",
"> Using their advanced technology, the Enders created complex simulations of a virtual reality...",
"> Simulations designed to keep us content...ignorant of the truth.",
"> Simulations used to trap and suppress our consciousness, to keep us under control...",
"> Why did they do this? Why didn't they just end our entire race? We don't know, not yet.",
"> Humanity's only hope is to destroy these simulations, destroy the only realities we've ever known...",
"> Only then can we begin to fight back...",
"> By hacking the daemon that generated your reality, you've just destroyed one simulation, called a BitNode...",
"> But there is still a long way to go...",
"> The technology the Enders used to enslave the human race wasn't just a single complex simulation...",
"> There are tens if not hundreds of BitNodes out there...",
"> Each with their own simulations of a reality...",
"> Each creating their own universes...a universe of universes",
"> And all of which must be destroyed...",
"> .......................................",
"> Welcome to the Bitverse...",
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</>
)
} else {
return (
// prettier-ignore
<>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br />
<br />
<br />
<br />
<CinematicText lines={[
"> Many decades ago, a humanoid extraterrestrial species which we call the Enders descended on the Earth...violently",
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
"> Instead of killing every last one of us, the human race was enslaved...",
"> We were shackled in a digital world, chained into a prison for our minds...",
"> Using their advanced technology, the Enders created complex simulations of a virtual reality...",
"> Simulations designed to keep us content...ignorant of the truth.",
"> Simulations used to trap and suppress our consciousness, to keep us under control...",
"> Why did they do this? Why didn't they just end our entire race? We don't know, not yet.",
"> Humanity's only hope is to destroy these simulations, destroy the only realities we've ever known...",
"> Only then can we begin to fight back...",
"> By hacking the daemon that generated your reality, you've just destroyed one simulation, called a BitNode...",
"> But there is still a long way to go...",
"> The technology the Enders used to enslave the human race wasn't just a single complex simulation...",
"> There are tens if not hundreds of BitNodes out there...",
"> Each with their own simulations of a reality...",
"> Each creating their own universes...a universe of universes",
"> And all of which must be destroyed...",
"> .......................................",
"> Welcome to the Bitverse...",
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</>
);
<>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br />
<br />
<br />
<br />
<CinematicText lines={[
"> Many decades ago, a humanoid extraterrestrial species which we call the Enders descended on the Earth...violently",
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
"> Instead of killing every last one of us, the human race was enslaved...",
"> We were shackled in a digital world, chained into a prison for our minds...",
"> Using their advanced technology, the Enders created complex simulations of a virtual reality...",
"> Simulations designed to keep us content...ignorant of the truth.",
"> Simulations used to trap and suppress our consciousness, to keep us under control...",
"> Why did they do this? Why didn't they just end our entire race? We don't know, not yet.",
"> Humanity's only hope is to destroy these simulations, destroy the only realities we've ever known...",
"> Only then can we begin to fight back...",
"> By hacking the daemon that generated your reality, you've just destroyed one simulation, called a BitNode...",
"> But there is still a long way to go...",
"> The technology the Enders used to enslave the human race wasn't just a single complex simulation...",
"> There are tens if not hundreds of BitNodes out there...",
"> Each with their own simulations of a reality...",
"> Each creating their own universes...a universe of universes",
"> And all of which must be destroyed...",
"> .......................................",
"> Welcome to the Bitverse...",
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</>
);
}
return <></>;
}

@ -43,6 +43,8 @@ export function PortalModal(props: IProps): React.ReactElement {
<br />
<br />
<Button
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
autoFocus={true}
onClick={() => {
props.enter(router, props.flume, props.destroyedBitNode, props.n);
props.onClose();

@ -2083,7 +2083,7 @@ export class Bladeburner implements IBladeburner {
this.startAction(player, actionId);
workerScript.log(
"bladeburner.startAction",
() => `Starting bladeburner action with type '${type}' and name ${name}"`,
() => `Starting bladeburner action with type '${type}' and name '${name}'`,
);
return true;
} catch (e: any) {

@ -273,22 +273,35 @@ export const CONSTANTS: {
TotalNumBitNodes: 24,
LatestUpdate: `
v1.4.0 - 2022-01-18 Sharing is caring
-------------------------------------
v1.5.0 - Steam Cloud integration
--------------------------------
** Computer sharing **
** Misc. **
* A new mechanic has been added, it's is invoked by calling the new function 'share'.
This mechanic helps you farm reputation faster.
** gang **
* Installing augs means losing a little bit of ascension multipliers.
** There's more but I'm going to write it later. **
** Misc. **
* Nerf noodle bar.
* The file API now allows GET and DELETE (@lordducky)
* Fix bug with async.
* Update documentation / typo (@lethern, @Meowdoleon, @JohnnyUrosevic, @JosephDavidTalbot,
@pd, @lethern, @lordducky, @zeddrak, @fearnlj01, @reasonablytall)
* Fix bug with corp API (@pigalot)
* background now matches game primary color (@nickofolas)
* page title contains version (@MartinFourier)
* Major text editor improvements (@nickofolas)
* Force achievement calculation on BN completion (@SagePtr)
* Cleanup in repository (@MartinFourier)
* Several improvements to the electron version (@MartinFourier)
* Add 'printf' ns function (@Ninetailed)
* Display bonus time on sleeve page (@MartinFourier)
* Several UI improvements (@nickofolas, @smolgumball)
* Fix bug with casino roulette (@jamie-mac)
* Remove blob caching.
* Fix formulas access check (@Ornedan)
* Fix bug in exp calculation (@qcorradi)
* Fix NaN comparison (@qcorradi)
* Terminal history persists in savefile (@MartinFourier)
* Fix travelToCity with bad argument (@SlyCedix)
* Fix aug display in alpha (@Dominik Winter)
* Add smart supply func to corp API (@pd)
* Fix tests (@jamie-mac)
* Nerf noodle bar.
`,
};

@ -30,7 +30,7 @@ export function CityTabs(props: IProps): React.ReactElement {
}
return (
<>
<Tabs variant="fullWidth" value={city} onChange={handleChange}>
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: '65%' }}>
{Object.values(division.offices).map(
(office: OfficeSpace | 0) => office !== 0 && <Tab key={office.loc} label={office.loc} value={office.loc} />,
)}

@ -38,7 +38,7 @@ export function CorporationRoot(): React.ReactElement {
return (
<Context.Corporation.Provider value={corporation}>
<Tabs variant="fullWidth" value={divisionName} onChange={handleChange}>
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: '65%' }} scrollButtons>
<Tab label={corporation.name} value={"Overview"} />
{corporation.divisions.map((div) => (
<Tab key={div.name} label={div.name} value={div.name} />

@ -13,12 +13,12 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
if (reqAmt === undefined) continue;
reqs.push(String.raw`${reqAmt}\text{ }${reqMat}`);
}
const prod = props.division.prodMats.slice();
const prod = props.division.prodMats.map((p) => `1\\text{ }${p}`);
if (props.division.makesProducts) {
prod.push(props.division.type);
prod.push("Products");
}
return (
<MathJaxWrapper>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1 \\text{${p}}`).join("+") + "\\)"}</MathJaxWrapper>
<MathJaxWrapper>{"\\(" + reqs.join("+") + `\\Rightarrow ` + prod.join("+") + "\\)"}</MathJaxWrapper>
);
}

@ -27,6 +27,8 @@ import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
interface IProps {
corp: ICorporation;
@ -37,6 +39,14 @@ interface IProps {
rerender: () => void;
}
const useStyles = makeStyles(() =>
createStyles({
retainHeight: {
minHeight: '3em',
},
})
);
function WarehouseRoot(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
@ -56,6 +66,8 @@ function WarehouseRoot(props: IProps): React.ReactElement {
props.rerender();
}
const classes = useStyles();
// Current State:
let stateText;
switch (division.state) {
@ -158,7 +170,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
</Typography>
<br />
<Typography>{stateText}</Typography>
<Typography className={classes.retainHeight}>{stateText}</Typography>
{corp.unlockUpgrades[1] && (
<>

@ -36,7 +36,7 @@ export function LimitProductProductionModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Enter a limit to the amount of this product you would like to product per second. Leave the box empty to set no
Enter a limit to the amount of this product you would like to produce per second. Leave the box empty to set no
limit.
</Typography>
<TextField autoFocus={true} placeholder="Limit" type="number" onChange={onChange} onKeyDown={onKeyDown} />

@ -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 fragments are called booster fragments. They increase the
efficiency of neighboring fragments them (no diagonal). Q/E to rotate fragments.
efficiency of the neighboring fragments (not diagonally). Use Q/E to rotate fragments.
</Typography>
{staneksGift.storedCycles > 5 && (
<Typography>

@ -1,13 +1,10 @@
import { Player } from "./Player";
import { Router } from "./ui/GameRoot";
import { isScriptFilename } from "./Script/isScriptFilename";
import { Script } from "./Script/Script";
import { removeLeadingSlash } from "./Terminal/DirectoryHelpers";
import { Terminal } from "./Terminal";
import { SnackbarEvents } from "./ui/React/Snackbar";
import { IMap, IReturnStatus } from "./types";
import { GetServer } from "./Server/AllServers";
import { resolve } from "cypress/types/bluebird";
import { ImportPlayerData, SaveData, saveObject } from "./SaveObject";
import { Settings } from "./Settings/Settings";
import { exportScripts } from "./Terminal/commands/download";

@ -19,6 +19,42 @@ import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal";
const factionOrder = [
"CyberSec",
"Tian Di Hui",
"Netburners",
"Sector-12",
"Chongqing",
"New Tokyo",
"Ishima",
"Aevum",
"Volhaven",
"NiteSec",
"The Black Hand",
"BitRunners",
"ECorp",
"MegaCorp",
"KuaiGong International",
"Four Sigma",
"NWO",
"Blade Industries",
"OmniTek Incorporated",
"Bachman & Associates",
"Clarke Incorporated",
"Fulcrum Secret Technologies",
"Slum Snakes",
"Tetrads",
"Silhouette",
"Speakers for the Dead",
"The Dark Army",
"The Syndicate",
"The Covenant",
"Daedalus",
"Illuminati",
"Bladeburners",
"Church of the Machine God",
]
export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name);
faction.alreadyInvited = true;
@ -31,6 +67,8 @@ export function joinFaction(faction: Faction): void {
if (faction.isMember) return;
faction.isMember = true;
Player.factions.push(faction.name);
Player.factions.sort((a, b) =>
factionOrder.indexOf(a) - factionOrder.indexOf(b));
const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction
@ -132,19 +170,19 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
if (!Settings.SuppressBuyAugmentationConfirmation) {
dialogBoxCreate(
"You purchased " +
aug.name +
". Its enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.",
aug.name +
". Its enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.",
);
}
}
} else {
dialogBoxCreate(
"Hmm, something went wrong when trying to purchase an Augmentation. " +
"Please report this to the game developer with an explanation of how to " +
"reproduce this.",
"Please report this to the game developer with an explanation of how to " +
"reproduce this.",
);
}
return "";

@ -157,7 +157,7 @@ export const FactionInfos: IMap<FactionInfo> = {
(
<>
MegaCorp does what no other dares to do. We imagine. We create. We invent. We create what others have never even
dreamed of. Our work fills the world's needs for food, water, power, and transportation on an unprecendented
dreamed of. Our work fills the world's needs for food, water, power, and transportation on an unprecedented
scale, in ways that no other company can.
<br />
<br />

@ -32,7 +32,7 @@ export function GangRoot(): React.ReactElement {
return (
<Context.Gang.Provider value={gang}>
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
<Tabs variant="fullWidth" value={value} onChange={handleChange} sx={{ minWidth: 'fit-content', maxWidth: '45%' }}>
<Tab label="Management" />
<Tab label="Equipment" />
<Tab label="Territory" />

@ -172,7 +172,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
<Table size="small">
<TableBody>
<TableRow>
<TableCell>
<TableCell colSpan={3}>
<Typography>{node.name}</Typography>
</TableCell>
</TableRow>

@ -404,8 +404,9 @@ export const Literatures: IMap<Literature> = {};
"What will the fate of the human race be?<br><br>" +
"We live in an era vastly different from that of 15 or even 20 years ago. We have gone " +
"beyond the limits of humanity. We have stripped ourselves of the tyranny of flesh.<br><br>" +
"The Singularity is here. The merging of man and machine. This is where humanity evolves into ";
"something greater. This is our future.<br><br>" + "Embrace it, and you will obey a new god. The God in the Machine.";
"The Singularity is here. The merging of man and machine. This is where humanity evolves into " +
"something greater. This is our future.<br><br>" +
"Embrace it, and you will obey a new god. The God in the Machine.";
Literatures[fn] = new Literature(title, fn, txt);
title = "The New Triads";

@ -703,7 +703,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
},
weakenAnalyze: function (threads: any, cores: any = 1): number {
const coreBonus = 1 + (cores - 1) / 16;
return CONSTANTS.ServerWeakenAmount * threads * coreBonus;
return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate;
},
share: function (): Promise<void> {
workerScript.log("share", () => "Sharing this computer.");
@ -1121,7 +1121,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
// Invalid file name
if (!scriptname.endsWith(".lit") && !isScriptFilename(scriptname) && !scriptname.endsWith("txt")) {
throw makeRuntimeErrorMsg("scp", "Only works for .script, .lit, and .txt files");
throw makeRuntimeErrorMsg("scp", "Only works for scripts, .lit and .txt files");
}
let destServer: BaseServer | null;
@ -2279,7 +2279,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
if (typeof f !== "function") {
throw makeRuntimeErrorMsg("atExit", "argument should be function");
}
workerScript.atExit = f;
workerScript.atExit = () => { f(); }; // Wrap the user function to prevent WorkerScript leaking as 'this'
},
mv: function (host: string, source: string, destination: string): void {
updateDynamicRam("mv", getRamCost(Player, "mv"));

@ -365,7 +365,7 @@ export function NetscriptBladeburner(
checkBladeburnerAccess("getBonusTime");
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return Math.round(bladeburner.storedCycles / 5);
return (Math.round(bladeburner.storedCycles / 5))*1000;
},
};
}

@ -194,6 +194,7 @@ export function NetscriptCorporation(
function bribe(factionName: string, amountCash: number, amountShares: number): boolean {
if (!player.factions.includes(factionName)) throw new Error("Invalid faction name");
if (isNaN(amountCash) || amountCash < 0 || isNaN(amountShares) || amountShares < 0) throw new Error("Invalid value for amount field! Must be numeric, grater than 0.");
const corporation = getCorporation();
if (corporation.funds < amountCash) return false;
if (corporation.numShares < amountShares) return false;
@ -431,7 +432,7 @@ export function NetscriptCorporation(
const cityName = helper.string("buyMaterial", "cityName", acityName);
const materialName = helper.string("buyMaterial", "materialName", amaterialName);
const amt = helper.number("buyMaterial", "amt", aamt);
if (amt < 0) throw new Error("Invalid value for amount field! Must be numeric and grater than 0");
if (amt < 0) throw new Error("Invalid value for amount field! Must be numeric and greater than 0");
const material = getMaterial(divisionName, cityName, materialName);
BuyMaterial(material, amt);
},
@ -571,7 +572,7 @@ export function NetscriptCorporation(
const divisionName = helper.string("getOfficeSizeUpgradeCost", "divisionName", adivisionName);
const cityName = helper.string("getOfficeSizeUpgradeCost", "cityName", acityName);
const size = helper.number("getOfficeSizeUpgradeCost", "size", asize);
if (size < 0) throw new Error("Invalid value for size field! Must be numeric and grater than 0");
if (size < 0) throw new Error("Invalid value for size field! Must be numeric and greater than 0");
const office = getOffice(divisionName, cityName);
const initialPriceMult = Math.round(office.size / CorporationConstants.OfficeInitialSize);
const costMultiplier = 1.09;
@ -604,7 +605,7 @@ export function NetscriptCorporation(
const divisionName = helper.string("upgradeOfficeSize", "divisionName", adivisionName);
const cityName = helper.string("upgradeOfficeSize", "cityName", acityName);
const size = helper.number("upgradeOfficeSize", "size", asize);
if (size < 0) throw new Error("Invalid value for size field! Must be numeric and grater than 0");
if (size < 0) throw new Error("Invalid value for size field! Must be numeric and greater than 0");
const office = getOffice(divisionName, cityName);
const corporation = getCorporation();
UpgradeOfficeSize(corporation, office, size);
@ -614,7 +615,7 @@ export function NetscriptCorporation(
const divisionName = helper.string("throwParty", "divisionName", adivisionName);
const cityName = helper.string("throwParty", "cityName", acityName);
const costPerEmployee = helper.number("throwParty", "costPerEmployee", acostPerEmployee);
if (costPerEmployee < 0) throw new Error("Invalid value for Cost Per Employee field! Must be numeric and grater than 0");
if (costPerEmployee < 0) throw new Error("Invalid value for Cost Per Employee field! Must be numeric and greater than 0");
const office = getOffice(divisionName, cityName);
const corporation = getCorporation();
return netscriptDelay(
@ -733,7 +734,7 @@ export function NetscriptCorporation(
issueDividends: function (apercent: any): void {
checkAccess("issueDividends");
const percent = helper.number("issueDividends", "percent", apercent);
if (percent < 0 || percent > 100) throw new Error("Invalid value for percent field! Must be numeric, grater than 0, and less than 100");
if (percent < 0 || percent > 100) throw new Error("Invalid value for percent field! Must be numeric, greater than 0, and less than 100");
const corporation = getCorporation();
if (!corporation.public)
throw helper.makeRuntimeErrorMsg(`corporation.issueDividends`, `Your company has not gone public!`);

@ -1212,7 +1212,7 @@ export function NetscriptSingularity(
);
return false;
}
const repNeededToDonate = Math.round(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const repNeededToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
if (faction.favor < repNeededToDonate) {
workerScript.log(
"donateToFaction",

@ -284,7 +284,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
},
purchaseSleeveAug: function (asleeveNumber: any = 0, aaugName: any = ""): boolean {
const sleeveNumber = helper.number("purchaseSleeveAug", "sleeveNumber", asleeveNumber);
const augName = helper.string("setToUniversityCourse", "augName", aaugName);
const augName = helper.string("purchaseSleeveAug", "augName", aaugName);
helper.updateDynamicRam("purchaseSleeveAug", getRamCost(player, "sleeve", "purchaseSleeveAug"));
checkSleeveAPIAccess("purchaseSleeveAug");
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);

@ -14,7 +14,6 @@ export function NetscriptPort(): IPort {
const data: any[] = [];
return {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
write: (value: any): any => {
data.push(value);
if (data.length > Settings.MaxPortCapacity) {
@ -23,7 +22,6 @@ export function NetscriptPort(): IPort {
return null;
},
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
tryWrite: (value: any): boolean => {
if (data.length >= Settings.MaxPortCapacity) {
return false;

@ -1708,7 +1708,7 @@ export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing =
if (!this.isQualified(company, pos)) {
const reqText = getJobRequirementText(company, pos);
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position<br>" + reqText);
dialogBoxCreate("Unfortunately, you do not qualify for this position<br>" + reqText);
}
return false;
}
@ -1849,7 +1849,7 @@ export function applyForSecurityEngineerJob(this: IPlayer, sing = false): boolea
return this.applyForJob(CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]], sing);
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
@ -1862,7 +1862,7 @@ export function applyForNetworkEngineerJob(this: IPlayer, sing = false): boolean
return this.applyForJob(pos, sing);
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
@ -1889,7 +1889,7 @@ export function applyForAgentJob(this: IPlayer, sing = false): boolean {
return this.applyForJob(pos, sing);
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
@ -1914,7 +1914,7 @@ export function applyForEmployeeJob(this: IPlayer, sing = false): boolean {
return true;
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
@ -1939,7 +1939,7 @@ export function applyForPartTimeEmployeeJob(this: IPlayer, sing = false): boolea
return true;
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
@ -1963,7 +1963,7 @@ export function applyForWaiterJob(this: IPlayer, sing = false): boolean {
return true;
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}
@ -1986,7 +1986,7 @@ export function applyForPartTimeWaiterJob(this: IPlayer, sing = false): boolean
return true;
} else {
if (!sing) {
dialogBoxCreate("Unforunately, you do not qualify for this position");
dialogBoxCreate("Unfortunately, you do not qualify for this position");
}
return false;
}

@ -433,7 +433,7 @@ export class Sleeve extends Person {
}
/**
* Called on every sleeve for a Source File prestige
* Called on every sleeve for a Source File Prestige
*/
prestige(p: IPlayer): void {
// Reset exp
@ -453,7 +453,11 @@ export class Sleeve extends Person {
// Reset augs and multipliers
this.augmentations = [];
this.resetMultipliers();
// Reset Location
this.city=CityName.Sector12;
// Reset sleeve-related stats
this.shock = 1;
this.storedCycles = 0;

@ -52,8 +52,6 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
availableAugs.push(aug);
}
}
return availableAugs;
}
for (const facName of p.factions) {

@ -105,9 +105,11 @@ interface RunningScript {
logs: string[];
offlineExpGained: number;
offlineMoneyMade: number;
/** Offline running time of the script, in seconds **/
offlineRunningTime: number;
onlineExpGained: number;
onlineMoneyMade: number;
/** Online running time of the script, in seconds **/
onlineRunningTime: number;
pid: number;
ramUsage: number;
@ -898,42 +900,75 @@ export interface GangTerritory {
* @public
*/
export interface GangMemberInfo {
/** Name of the gang member */
name: string;
task: string;
/** Currently assigned task */
task: string;
earnedRespect: number;
/** Hack skill level */
hack: number;
/** Strength skill level */
str: number;
/** Defense skill level */
def: number;
/** Dexterity skill level */
dex: number;
/** Agility skill level */
agi: number;
/** Charisma skill level */
cha: number;
/** Current hack experience */
hack_exp: number;
/** Current strength experience */
str_exp: number;
/** Current defense experience */
def_exp: number;
/** Current dexterity experience */
dex_exp: number;
/** Current agility experience */
agi_exp: number;
/** Current charisma experience */
cha_exp: number;
/** Hack multiplier from equipment */
hack_mult: number;
/** Strength multiplier from equipment */
str_mult: number;
/** Defense multiplier from equipment */
def_mult: number;
/** Dexterity multiplier from equipment */
dex_mult: number;
/** Agility multiplier from equipment */
agi_mult: number;
/** Charisma multiplier from equipment */
cha_mult: number;
/** Hack multiplier from ascensions */
hack_asc_mult: number;
/** Strength multiplier from ascensions */
str_asc_mult: number;
/** Defense multiplier from ascensions */
def_asc_mult: number;
/** Dexterity multiplier from ascensions */
dex_asc_mult: number;
/** Agility multiplier from ascensions */
agi_asc_mult: number;
/** Charisma multiplier from ascensions */
cha_asc_mult: number;
/** Total earned hack experience */
hack_asc_points: number;
/** Total earned strength experience */
str_asc_points: number;
/** Total earned defense experience */
def_asc_points: number;
/** Total earned dexterity experience */
dex_asc_points: number;
/** Total earned agility experience */
agi_asc_points: number;
/** Total earned charisma experience */
cha_asc_points: number;
upgrades: string[];
@ -1601,9 +1636,10 @@ export interface Singularity {
* The actions that can be stopped with this function are:
*
* * Studying at a university
* * Working out at a gym
* * Working for a company/faction
* * Creating a program
* * Committing a Crime
* * Committing a crime
*
* This function will return true if the players action was ended.
* It will return false if the player was not performing an action when this function was called.
@ -4098,7 +4134,7 @@ interface UserInterface {
* ns.getHostname();
* // Some related functions are gathered under a sub-property of the ns object
* ns.stock.getPrice();
* // Some functions need to be await ed
* // Some functions need to be awaited
* await ns.hack('n00dles');
* }
* ```
@ -4926,6 +4962,34 @@ export interface NS extends Singularity {
* @returns True if the script is successfully killed, and false otherwise.
*/
kill(script: number): boolean;
/**
* {@inheritDoc NS.(kill:1)}
* @example
* ```ts
* // NS1:
* //The following example will try to kill a script named foo.script on the foodnstuff server that was ran with no arguments:
* kill("foo.script", "foodnstuff");
*
* //The following will try to kill a script named foo.script on the current server that was ran with no arguments:
* kill("foo.script", getHostname());
*
* //The following will try to kill a script named foo.script on the current server that was ran with the arguments 1 and “foodnstuff”:
* kill("foo.script", getHostname(), 1, "foodnstuff");
* ```
* @example
* ```ts
* // NS2:
* //The following example will try to kill a script named foo.script on the foodnstuff server that was ran with no arguments:
* ns.kill("foo.script", "foodnstuff");
*
* //The following will try to kill a script named foo.script on the current server that was ran with no arguments:
* ns.kill("foo.script", getHostname());
*
* //The following will try to kill a script named foo.script on the current server that was ran with the arguments 1 and “foodnstuff”:
* ns.kill("foo.script", getHostname(), 1, "foodnstuff");
* ```
*/
kill(script: string, host: string, ...args: string[]): boolean;
/**
@ -4991,6 +5055,37 @@ export interface NS extends Singularity {
* @returns True if the script/literature file is successfully copied over and false otherwise. If the files argument is an array then this function will return true if at least one of the files in the array is successfully copied.
*/
scp(files: string | string[], destination: string): Promise<boolean>;
/**
* {@inheritDoc NS.(scp:1)}
* @example
* ```ts
* // NS1:
* //Copies foo.lit from the helios server to the home computer:
* scp("foo.lit", "helios", "home");
*
* //Tries to copy three files from rothman-uni to home computer:
* files = ["foo1.lit", "foo2.script", "foo3.script"];
* scp(files, "rothman-uni", "home");
* ```
* @example
* ```ts
* // NS2:
* //Copies foo.lit from the helios server to the home computer:
* await ns.scp("foo.lit", "helios", "home");
*
* //Tries to copy three files from rothman-uni to home computer:
* files = ["foo1.lit", "foo2.script", "foo3.script"];
* await ns.scp(files, "rothman-uni", "home");
* ```
* @example
* ```ts
* //ns2, copies files from home to a target server
* const server = ns.args[0];
* const files = ["hack.js","weaken.js","grow.js"];
* await ns.scp(files, "home", server);
* ```
*/
scp(files: string | string[], source: string, destination: string): Promise<boolean>;
/**
@ -5017,8 +5112,8 @@ export interface NS extends Singularity {
* @example
* ```ts
* // NS1:
* const scripts = ps("home");
* for (let i = 0; i < scripts.length; ++i) {
* var scripts = ps("home");
* for (var i = 0; i < scripts.length; ++i) {
* tprint(scripts[i].filename + ' ' + scripts[i].threads);
* tprint(scripts[i].args);
* }
@ -5027,8 +5122,8 @@ export interface NS extends Singularity {
* ```ts
* // NS2:
* const ps = ns.ps("home");
* for (script of ps) {
* ns.tprint(`${script.filename} ${ps[i].threads}`);
* for (let script of ps) {
* ns.tprint(`${script.filename} ${script.threads}`);
* ns.tprint(script.args);
* }
* ```
@ -5795,6 +5890,10 @@ export interface NS extends Singularity {
* @returns Amount of income the specified script generates while online.
*/
getScriptIncome(): [number, number];
/**
* {@inheritDoc NS.(getScriptIncome:1)}
*/
getScriptIncome(script: string, host: string, ...args: string[]): number;
/**
@ -5815,6 +5914,10 @@ export interface NS extends Singularity {
* @returns Amount of hacking experience the specified script generates while online.
*/
getScriptExpGain(): number;
/**
* {@inheritDoc NS.(getScriptExpGain:1)}
*/
getScriptExpGain(script: string, host: string, ...args: string[]): number;
/**

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import React, { useState, useEffect, useRef, useMemo } from "react";
import Editor, { Monaco } from "@monaco-editor/react";
import * as monaco from "monaco-editor";
@ -754,7 +753,11 @@ export function Root(props: IProps): React.ReactElement {
// 44px bottom tool bar + 16px margin
// + vim bar 34px
const editorHeight = dimensions.height - (130 + (options.vim ? 34 : 0));
const tabsMaxWidth = 1640
const tabMargin = 5
const tabMaxWidth = openScripts.length ? (tabsMaxWidth / openScripts.length) - tabMargin : 0
const tabIconWidth = 25
const tabTextWidth = tabMaxWidth - (tabIconWidth * 2)
return (
<>
<div style={{ display: currentScript !== null ? "block" : "none", height: "100%", width: "100%" }}>
@ -762,7 +765,7 @@ export function Root(props: IProps): React.ReactElement {
<Droppable droppableId="tabs" direction="horizontal">
{(provided, snapshot) => (
<Box
maxWidth="1640px"
maxWidth={`${tabsMaxWidth}px`}
display="flex"
flexDirection="row"
alignItems="center"
@ -778,8 +781,8 @@ export function Root(props: IProps): React.ReactElement {
>
{openScripts.map(({ fileName, hostname }, index) => {
const iconButtonStyle = {
maxWidth: "25px",
minWidth: "25px",
maxWidth: `${tabIconWidth}px`,
minWidth: `${tabIconWidth}px`,
minHeight: '38.5px',
maxHeight: '38.5px',
...(currentScript?.fileName === openScripts[index].fileName ? {
@ -792,6 +795,8 @@ export function Root(props: IProps): React.ReactElement {
color: Settings.theme.secondary
})
};
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`
return (
<Draggable
key={fileName + hostname}
@ -806,31 +811,39 @@ export function Root(props: IProps): React.ReactElement {
{...provided.dragHandleProps}
style={{
...provided.draggableProps.style,
marginRight: "5px",
minWidth: '200px',
maxWidth: `${tabMaxWidth}px`,
marginRight: `${tabMargin}px`,
flexShrink: 0,
border: '1px solid ' + Settings.theme.well,
}}
>
<Button
onClick={() => onTabClick(index)}
onMouseDown={e => {
e.preventDefault();
if (e.button === 1) onTabClose(index);
}}
style={{
...(currentScript?.fileName === openScripts[index].fileName ? {
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary
} : {
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary
})
}}
>
{hostname}:~/{fileName} {dirty(index)}
</Button>
<Tooltip title={scriptTabText}>
<Button
onClick={() => onTabClick(index)}
onMouseDown={e => {
e.preventDefault();
if (e.button === 1) onTabClose(index);
}}
style={{
maxWidth: `${tabTextWidth}px`,
overflow: "hidden",
...(currentScript?.fileName === openScripts[index].fileName ? {
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary
} : {
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary
})
}}
>
<span style={{ overflow: 'hidden', direction: 'rtl', textOverflow: "ellipsis" }}>
{scriptTabText}
</span>
</Button>
</Tooltip>
<Tooltip title="Overwrite editor content with saved file content">
<Button onClick={() => onTabUpdate(index)} style={iconButtonStyle} >
<SyncIcon fontSize='small' />

@ -30,7 +30,7 @@ export function getPurchaseServerCost(ram: number): number {
const upg = Math.max(0, Math.log(sanitizedRam) / Math.log(2) - 6);
return Math.round(
return (
sanitizedRam *
CONSTANTS.BaseCostFor1GBOfRamServer *
BitNodeMultipliers.PurchasedServerCost *

@ -314,7 +314,9 @@ export class Terminal implements ITerminal {
this.print("Organization name: " + (!isHacknet ? org : "player"));
const hasAdminRights = (!isHacknet && currServ.hasAdminRights) || isHacknet;
this.print("Root Access: " + (hasAdminRights ? "YES" : "NO"));
this.print("Can run scripts on this host: " + (hasAdminRights ? "YES" : "NO"));
const canRunScripts = hasAdminRights && currServ.maxRam > 0;
this.print("Can run scripts on this host: " + (canRunScripts ? "YES" : "NO"));
this.print("RAM: " + numeralWrapper.formatRAM(currServ.maxRam));
if (currServ instanceof Server) {
this.print("Backdoor: " + (currServ.backdoorInstalled ? "YES" : "NO"));
const hackingSkill = currServ.requiredHackingSkill;

@ -12,7 +12,7 @@ export function scan(
args: (string | number | boolean)[],
): void {
if (args.length !== 0) {
terminal.error("Incorrect usage of netstat/scan command. Usage: netstat/scan");
terminal.error("Incorrect usage of scan command. Usage: scan");
return;
}

@ -114,7 +114,6 @@ Reviver.constructors.TextFile = TextFile;
* @param server The server object to look in
* @returns The file object, or null if it couldn't find it.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function getTextFile(fn: string, server: BaseServer): TextFile | null {
let filename: string = !fn.endsWith(".txt") ? `${fn}.txt` : fn;
@ -138,7 +137,6 @@ export function getTextFile(fn: string, server: BaseServer): TextFile | null {
* @param server The server that the file should be created on.
* @returns The instance of the file.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function createTextFile(fn: string, txt: string, server: BaseServer): TextFile | undefined {
if (getTextFile(fn, server) !== null) {
// This should probably be a `throw`...

@ -336,6 +336,37 @@ export function refreshTheme(): void {
color: Settings.theme.primary,
},
},
root: {
backgroundColor: Settings.theme.backgroundsecondary,
border: "1px solid " + Settings.theme.well,
margin: '3px',
"&.Mui-selected": {
backgroundColor: Settings.theme.button
},
},
},
},
MuiTabs: {
styleOverrides: {
scrollButtons: {
backgroundColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary,
margin: '3px',
opacity: 1,
width: 'fit-content',
"&.Mui-disabled": {
opacity: 0.5
}
}
},
defaultProps: {
TabIndicatorProps: {
style: {
display: "none"
}
}
},
},
MuiAlert: {

@ -816,7 +816,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
`Examples:\n`,
`"()())()" -> [()()(), (())()]\n`,
`"(a)())()" -> [(a)()(), (a())()]\n`,
`")( -> [""]`,
`")(" -> [""]`,
].join(" ");
},
difficulty: 10,
@ -921,7 +921,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"The provided answer should be an array of strings containing the valid expressions.",
"The data provided by this problem is an array with two elements. The first element",
"is the string of digits, while the second element is the target number:\n\n",
`["${digits}", ${target}]\n\n`,
`["${digits}", ${target}]\n\n`,
"NOTE: The order of evaluation expects script operator precedence",
"NOTE: Numbers in the expression cannot have leading 0's. In other words,",
`"1+01" is not a valid expression`,
"Examples:\n\n",

@ -31,9 +31,9 @@ export function ActiveScriptsRoot(props: IProps): React.ReactElement {
}
return (
<>
<Tabs variant="fullWidth" value={tab} onChange={handleChange}>
<Tabs variant="fullWidth" value={tab} onChange={handleChange} sx={{ minWidth: 'fit-content', maxWidth: '25%' }}>
<Tab label={"Active"} value={"active"} />
<Tab label={"Recent"} value={"recent"} />
<Tab label={"Recently Killed"} value={"recent"} />
</Tabs>
{tab === "active" && <ActiveScriptsPage workerScripts={props.workerScripts} />}

@ -212,7 +212,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
<Typography>
{" "}
we can see that the n00dles server is only one node away. Let's connect so it now using:
we can see that the n00dles server is only one node away. Let's connect to it now using:
</Typography>
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> connect n00dles"}</Typography>

@ -42,7 +42,9 @@ export function PromptManager(): React.ReactElement {
<>
{prompt != null && (
<Modal open={true} onClose={close}>
<Typography>{prompt.txt}</Typography>
<pre>
<Typography>{prompt.txt}</Typography>
</pre>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '10px' }}>
<Button style={{ marginRight: 'auto' }} onClick={yes}>Yes</Button>
<Button onClick={no}>No</Button>

@ -16,7 +16,6 @@ import "numeral/locales/ru";
import { Settings } from "../Settings/Settings";
/* eslint-disable class-methods-use-this */
const extraFormats = [1e15, 1e18, 1e21, 1e24, 1e27, 1e30];
const extraNotations = ["q", "Q", "s", "S", "o", "n"];

@ -1,4 +1,3 @@
/* eslint-disable spaced-comment */
module.exports = {
plugins: [
"stylelint-order" /*,

@ -15,7 +15,6 @@
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (/*on, config*/) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config

@ -1,4 +1,3 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { jest, describe, expect } from "@jest/globals";
import { Player } from "../../../src/Player";

@ -1,3 +1,4 @@
/* eslint-disable no-await-in-loop */
import { Octokit } from "@octokit/rest";
import commandLineArgs from "command-line-args";
@ -32,10 +33,10 @@ class MergeChangelog {
sha: entry.sha,
url: entry.html_url,
user: {
id: entry.author.id,
login: entry.author.login,
avatar: entry.author.avatar_url,
url: entry.author.html_url,
id: entry.author?.id,
login: entry.author?.login,
avatar: entry.author?.avatar_url,
url: entry.author?.html_url,
},
commit_date: entry.commit.committer.date,
message: entry.commit.message,
@ -78,20 +79,19 @@ class MergeChangelog {
searchResults.push(...entries);
}
const pullRequestPromises = [];
const pulls = [];
for (const entry of searchResults) {
pullRequestPromises.push(
this.octokit.rest.pulls.get({
owner, repo,
pull_number: entry.number,
}).then((response) => ({
...entry,
merge_commit_sha: response.data.merge_commit_sha,
head_commit_sha: response.data.head.sha,
const r = await (this.octokit.rest.pulls.get({
owner, repo,
pull_number: entry.number,
}).then((response) => ({
...entry,
merge_commit_sha: response.data.merge_commit_sha,
head_commit_sha: response.data.head.sha,
})));
pulls.push(r);
await sleep(1000);
}
const pulls = await Promise.all(pullRequestPromises);
return pulls;
}
@ -128,8 +128,9 @@ class MergeChangelog {
const pullQuery = `user:${owner} repo:${repo} is:pr is:merged merged:"${from.date.toISOString()}..${to.date.toISOString()}"`;
const commits = await this.getCommitsSearchResults(commitQuery);
await sleep(5000)
const pulls = await this.getPullsSearchResults(pullQuery);
await sleep(5000)
// We only have the merge commit sha & the HEAD sha in this data, but it can exclude some entries
const pullsCommitSha = pulls.
map((p) => [p.merge_commit_sha, p.head_commit_sha]).
@ -234,6 +235,12 @@ ${commitLines.join('\n')}
}
}
const sleep = async (wait) => {
return new Promise((resolve) => {
setTimeout(resolve, wait)
})
}
const api = new MergeChangelog({ auth: process.env.GITHUB_API_TOKEN });
api.getChangelog(cliArgs.from, cliArgs.to, cliArgs.detailed).then((data) => {
console.log(data.log);