VERSION: Update game version to 2.2 (#240)

Includes some bug fixes:
* Fix sleeve shock internal/display discrepancy (0-100 vs 0-100)
* Special error message if ns function called without a this
* Change whitespace to pre-wrap for dialog box.
* Fix bug where idle sleeves do not consume cycles but still recover shock from those cycles. Now they do not recover during idle.
* attempted to tag commit as v2.2.0
This commit is contained in:
Snarling 2022-12-01 16:07:46 -05:00 committed by GitHub
parent 6034e1c3fa
commit 5ff2cd5357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 441 additions and 505 deletions

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

@ -1,10 +1,10 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "2.1.0",
"version": "2.2.0",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie & Olivier Gagnon"
"name": "Daniel Xie, Olivier Gagnon, et al."
},
"bugs": {
"url": "https://github.com/bitburner-official/bitburner-src/issues"

@ -88,8 +88,8 @@ export const CONSTANTS: {
Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG
LatestUpdate: string;
} = {
VersionString: "2.1.0",
VersionNumber: 26,
VersionString: "2.2.0",
VersionNumber: 27,
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@ -232,186 +232,102 @@ export const CONSTANTS: {
Donations: 30,
LatestUpdate: `
v2.1.0 - 2022-09-23 Remote File API
-----------------------------------
v2.2.0 - 2022-12-01
Dev notes
* The most important change about this update is the introduction of the remote file api.
With this we also deprecate the HTTP file api and the visual studio extension. Those things
were made during the rush of Steam and aren't well thought out. This new process works with
both the web and Steam version of the game and every text editor. Moving forward we also
won't be doing much, if any, upgrades to the in-game editor. We think it's good enough for
now and if you need more we recommend you hook up your favorite external editor.
* Added functions to resize, move, and close tail windows
* Added a new Augmentation, Z.O.Ë., which allows Sleeves to benefit from Stanek.
BREAKING CHANGES:
* ns.codingcontract.attempt no longer returns a boolean, it returns empty string on failure or the reward string
on success, so comparing the result directly to true/false will no longer work. The result can still be used as
a condition directly.
* (NS2 only) ns functions use the this value from ns: if you move the function to its own variable off of ns, it
needs to be bound to ns. e.g.:
const tprint = ns.tprint.bind(ns);
* ns.formulas.work.classGains removed, replaced with ns.formulas.work.universityGains and ns.formulas.work.gymGains
* ns.sleeve.getSleeveStats and ns.sleeve.getSleeveInformation removed, ns.sleeve.getSleeve added and the returned
sleeve can be used with formulas API the same way the getPlayer return can be.
API
* Remove incorrectly placed 's' in ns.tFormat() (by @LJNeon)
* More ports (previously max 20, now practically unlimited) (by @Hoekstraa)
* Corp functions now return copy of constant arrays instead of the original (by @Mughur)
* All the player sub-objects need to be copied for 'getPlayer'. (by @MageKing17)
* add corp get<constant> functions, UI (by @Mughur)
* FIX #3860 destroyW0r1dD43m0n now properly gives achievements and FIX #3890 favor now properly syncs across pages and the Donate achievement is now given correctly (by @Aerophia)
CONTRIBUTIONS
* Modify PR template (by @Hoekstraa)
CCT
* inconsistent probability for generation between online and offline (by @quacksouls)
DOC
* Some typo fixes in Netscript functions (by @quacksouls)
* Fix #4033 Why use Coding Contract API (by @quacksouls)
* typo fix in description of Caesar cipher (by @quacksouls)
* FIX DOCS TYPO IN terminal.rst (by @BugiDev)
* Update bitburner.sleeve.settobladeburneraction.md (by @borisflagell)
CORPORATION
* FIX #3880, #3876, #3322 and #3138 Bunch of corporation fixes (by @Mughur)
* Gave investors some economics classes (by @Mughur)
* Limit shareholder priority on newly issued shares (by @Undeemiss)
UI
* FIX #2962 Add a setting to display middle time unit in Time Elapsed String (by @hydroflame)
* FIX #4106 Fix incorrect experience display in Crime UI. (by @SilverNexus)
* Bitnode stats now show if BB/Corporation are disabled (by @Kelenius)
* Removed three empty lines from BB status screen (by @Kelenius)
* Add missing space to BN7 description (by @hex7cd)
* Improvements to crime work UI (by @Kelenius)
* FIX #3975, #3882 Script Editor more responsive on resize, and fix dirty file indicator (by @Snarling)
API FIX
* getCrimeStats use bitnode multipliers in the output of crime stats (by @phyzical)
SLEEVES
* FIX #3819 Allow using the regeneration chamber with sleeves to heal them. (by @coderanger)
* FIX #4063 fix crash when player tries to assign more than 3 sleeves to Bladeburner contracts (by @Snarling)
* FIX #4051 Sleeves no longer crash when player quits company sleeve was working (by @Snarling)
API BACKUP
* add singularity function for exporting game save back (by @phyzical)
CORPORATION API
* FIX #3655 Expose exports from Material (by @Rasmoh)
SCRIPTS
* FIX #4081 Rerunning a script from tail window recalculates ram usage (by @Snarling)
* FIX #3962 The correct script will be closed even if the player modifies args (v2.0) (by @Snarling)
DOCUMENTATION
* Fixed Argument order for scp() (by @njalooo)
CORP API
* Fix up param order for limitProductProduction to match docs (by @phyzical)
NETSCRIPT
* FIX #2376 ns.exit now exits immediately (by @Snarling)
* FIX #4055 Fix dynamic ram check (by @Snarling)
* FIX #4037 ns1 wraps deeper layers correctly. (by @Snarling)
* FIX #3963 Prevent bladeburner.setActionLevel from setting invalid action levels (by @MPJ-K)
* Typo fixes in CodingContract, Hacknet, Singularity APIs (by @quacksouls)
* Fix a typo in doc of Singularity.travelToCity() (by @quacksouls)
* Update netscript definition file for scp, write, read, and flags (by @Snarling)
* Correct missing ! for boolean coercion in Corporation.createCorporation(). (by @Risenafis)
* Normalized Stock API logging (by @Snarling)
* fix #3992 allow null duration in toast ns function (by @RollerKnobster)
* Correct missing '!' for boolean coercion in 'singularity.workForCompany()'. (by @MageKing17)
* ns.scp and ns.write are now synchronous + fix exec race condition (by @Snarling)
* FIX #2931 atExit now allows synchronous ns functions (by @Snarling)
* Improve real life CPU and memory performance of scripts. (by @Snarling)
INFILTRATION
* Corrected ns formula for infiltration rewards (by @ezylot)
RFA
* NetscriptDefinitions retains export strings (by @Hoekstraa)
* Fix type of RFAMessages with non-String results (by @Hoekstraa)
* New Remote File API addition for transmitting files to the game (by @Hoekstraa)
DEVELOPMENT
* Development repo moved to https://github.com/bitburner-official/bitburner-src
* Dev version available on web at https://bitburner-official.github.io/bitburner-src/
* Development is active again for non-bugfix.
* A bunch of fixes and setup related to moving to a new repo (@hydroflame)
NETSCRIPT
* Faster API wrapping on script launch. (by @d0sboots & @Snarling)
* Expose more enums for player use under ns.enums (by @Snarling)
* tFormat: Fix display for negative time (by @Snarling)
CODING CONTRACT
* ns.codingcontract.attempt always returns a string (by @Snarling)
FORMULAS
* ns.formulas.work.classGains removed, replaced with ns.formulas.work.universityGains and
ns.formulas.work.gymGains (@Snarling)
* Add ns.formulas.work.companyGains function (by @AlexeyKozhemiakin)
PORTS
* added portHandle.nextWrite() (by @LJNeon)
* Make ns.writePort synchronous (by @Snarling)
SLEEVE
* FIX #4022, #4024, #4025, #3998 (by @Mughur)
* ns.sleeve.getSleeve added. getPlayer and getSleeve can both be used for formulas. (by @Snarling)
STOCK
* ns.stock.getOrganization added for getting org from stock symbol (by @SamuraiNinjaGuy)
DOCS, UI
* update docs a bit more, amending some BN and SF texts (by @Mughur)
SCRIPTS
* Fixed bug where zombie scripts could be created after a soft reset (by @Snarling)
GANG
* Added weight to GangMemberTask construction call (by @ezylot)
SCRIPT LOGS
* Add ctrl-a support for selecting all text in tail window (by @Snarling)
Coding Contracts
* Don't stringify answer if already a string (by @alainbryden)
CORPORATION
* Remove corp employees as objects (by @Kelenius)
* Happiness/Energy/Morale trend down even for productive corps (by @Snarling)
* Typo fixes in modals to sell materials and products (by @quacksouls)
* Reworked MP formula validation to prevent possible save corruption on invalid entry (by @Snarling)
* Internal reorganization of Industry data (by @Snarling)
* Added check to material buy amount (by @G4mingJon4s)
* Check there is room to make a new product before opening popup. (by @G4mingJon4s)
* Fix typos in research descriptions (by @quacksouls)
TERMINAL
* Fix ansi display bugs (by @Snarling)
SLEEVE
* Fixed inconsistencies in how sleeve work rewards are handled. (by @Snarling)
* Fix bug that prevented selecting some crimes from UI. (by @Snarling)
* Internally shock starts at 100 and lowers to 0. Previously this was backwards.
SCRIPT EDITOR
* Debounce updateRAM calls. (by @Snarling)
STOCKMARKET
* Fix broken initializer when manually buying WSE access (by @Snarling)
WORK
* Add singularity check for finishing company work (by @Snarling)
TERMINAL
* Connect command will connect to player owned servers from anywhere. (by @Snarling)
DOCS
* Correct documentation for 'run()' with 0 threads. (by @MageKing17)
* Some doc updates (by @Mughur)
UI
* Fix keyboard shortcuts for other keyboard layouts (by @d0sboots)
* Fixed spacing of text in Trade for reputation button after Infiltration (by @PyroGenesis)
* Fix spacing on ANSI background escape codes (by @Snarling)
* Fix several instances where newlines were not being displayed properly (by @quacksouls)
* SoftResetButton.tsx Tooltip changed to make more sense (by @rai68)
* GANG: Fix Gang UI to correctly report the bonus time multiplier as 25x (by @TheMas3212)
FILES
* FIX #3979 Allow characters & and ' in filenames (by @Snarling)
DOC
* Fix incorrect examples for grow (by @quacksouls)
* Updated limitMaterialProduction() and limitProductProduction() documentation to mention removing limits. (by @PyroGenesis)
* Add ns documentation for possible sleeve tasks (by @Snarling)
* Update documentation for workForFaction and workForCompany (by @quacksouls)
* Improve CCT documentation for HammingCodes (by @quacksouls)
* cleanup in doc of Netscript functions (by @quacksouls)
* Various other doc fixes (by @quacksouls)
* Update documentation for ns.args (by @Snarling)
* De-uglify ns.print examples (by @LJNeon)
CORP FIX
* dont take research points for something already researched via api (by @phyzical)
MISC
* Some error handling streamlining (by @Snarling)
* fix: check both ts and js source now (by @Tanimodori)
* chore: sync version in package-lock.json (by @Tanimodori)
* Better safety when loading game for multiple save corruption issues (by @Snarling)
* Nerf Noodle bar
FIX
* Prompt Add user friendly message to avoid throwing recovery screen for invalid choices (by @phyzical)
STATS
* Fix logic for increasing HP based on defense skill levels (by @mattgarretson)
TUTORIAL
* Fix #3965 Corrected tutorial text (by @mihilt)
TUTORIAL
* Fix Ram Text (by @jaculler)
CONTRACTS
* FIX #3755 change input handling for contract attempts (by @Snarling)
HOTFIX
* Fix infil definitions.d.ts (by @phyzical)
MISC
* crime gains, sleeve gang augs and faq (by @Mughur)
* FIX #3649 Preventing server starting security level from going above 100 (by @Shiiyu)
* Adds Shadows of Anarchy (by @Lagicrus)
* Added intormation about hacking managers to hacking algorithms page (by @Kelenius)
* Fix Jest CI Error (by @geggleto)
* multiple hasAugmentation checks didn't check if the augment was installed (by @Mughur)
* Fix for #2442 and #2795. (by @G4mingJon4s)
* Adds info regarding augments and focus (by @Lagicrus)
* Removed console.log line (by @dhosborne)
* Update some doc (by @hydroflame)
* Sleeve crime gain bitnode multiplier fix (by @Mughur)
* trying to fix int problems (by @hydroflame)
* Fix broken ns filesnames (by @hydroflame)
* new formula functions (by @hydroflame)
* v2.0.0 (by @hydroflame)
* test fixes/md updates (by @phyzical)
* Remove "based" from positive adjectives in infil (by @faangbait)
* minor fix in instance calculation (by @hydroflame)
* fix dynamic ram miscalc not triggering (by @hydroflame)
* Refactor game options into separate components (by @hydroflame)
* fix documentation for remote api (by @hydroflame)
* fix settings unfocusing on every key stroke (by @hydroflame)
* fix some stuff with the timestamp settings (by @hydroflame)
* fix some stuff with the timestamp settings (by @hydroflame)
* Fix unique key problem with ascii elements (by @hydroflame)
* Improve wrong arg user message and add ui.windowSize (by @hydroflame)
* fix stack trace missing in some errors (by @hydroflame)
* Fix scp and write in ns1 (by @hydroflame)
* Did some changes of the remote api and added documentation (by @hydroflame)
* Add dummy function to generate a mock server or player for formulas stuff (by @hydroflame)
* fix compile error (by @hydroflame)
* regen doc (by @hydroflame)
* rm console log (by @hydroflame)
* regen doc (by @hydroflame)
* Added more info about blood program, change some aug descriptions (by @hydroflame)
* use triple equal (by @hydroflame)
* Minor improvements to Netscript Port loading and unloading (by @hydroflame)
* Fix hostname generation being weird about dash 0 added (by @hydroflame)
* upgrade version number. (by @hydroflame)
* Nerf Noodle bar
`,
INFILTRATION
* Fix SlashGame scaling. (by @Snarling)`,
};

@ -13,13 +13,13 @@ import { Adjuster } from "./Adjuster";
export function Sleeves(): React.ReactElement {
function sleeveMaxAllShock(): void {
for (let i = 0; i < Player.sleeves.length; ++i) {
Player.sleeves[i].shock = 0;
Player.sleeves[i].shock = 100;
}
}
function sleeveClearAllShock(): void {
for (let i = 0; i < Player.sleeves.length; ++i) {
Player.sleeves[i].shock = 100;
Player.sleeves[i].shock = 0;
}
}

@ -46,6 +46,14 @@ export class StampedLayer {
const arrayPath = [...tree, key];
const functionPath = arrayPath.join(".");
function wrappedFunction(this: StampedLayer, ...args: unknown[]): unknown {
if (!this)
throw new Error(`
ns.${functionPath} called with no this value.
ns functions must be bound to ns if placed in a new
variable. e.g.
const ${key} = ns.${functionPath}.bind(ns);
${key}(${JSON.stringify(args).replace(/^\[|\]$/g, "")});\n\n`);
const ctx = { workerScript: this.#workerScript, function: key, functionPath };
helpers.checkEnvFlags(ctx);
helpers.updateDynamicRam(ctx, getRamCost(...tree, key));

@ -105,7 +105,7 @@ export function prestigeAugmentation(this: PlayerObject): void {
this.sleeves.push(new Sleeve());
}
this.sleeves.forEach((sleeve) => (sleeve.shock >= 100 ? sleeve.synchronize() : sleeve.shockRecovery()));
this.sleeves.forEach((sleeve) => (sleeve.shock <= 0 ? sleeve.synchronize() : sleeve.shockRecovery()));
this.lastUpdate = new Date().getTime();

@ -55,7 +55,7 @@ export class Sleeve extends Person implements SleevePerson {
*
* Reputation earned is also multiplied by shock%
*/
shock = 1;
shock = 100;
/** Stored number of game "loop" cycles */
storedCycles = 0;
@ -76,7 +76,7 @@ export class Sleeve extends Person implements SleevePerson {
findPurchasableAugs = sleeveMethods.findPurchasableAugs;
shockBonus(): number {
return this.shock / 100;
return (100 - this.shock) / 100;
}
syncBonus(): number {
@ -159,7 +159,7 @@ export class Sleeve extends Person implements SleevePerson {
this.city = CityName.Sector12;
// Reset sleeve-related stats
this.shock = 1;
this.shock = 100;
this.storedCycles = 0;
this.sync = Math.max(this.memory, 1);
}
@ -173,12 +173,9 @@ export class Sleeve extends Person implements SleevePerson {
// Only process once every second (5 cycles)
const CyclesPerSecond = 1000 / CONSTANTS.MilliPerCycle;
this.storedCycles += numCycles;
if (this.storedCycles < CyclesPerSecond) return;
let cyclesUsed = this.storedCycles;
cyclesUsed = Math.min(cyclesUsed, 15);
this.shock = Math.min(100, this.shock + 0.0001 * cyclesUsed);
if (!this.currentWork) return;
if (this.storedCycles < CyclesPerSecond || !this.currentWork) return;
const cyclesUsed = Math.min(this.storedCycles, 15);
this.shock = Math.max(0, this.shock - 0.0001 * cyclesUsed);
this.currentWork.process(this, cyclesUsed);
this.storedCycles -= cyclesUsed;
}
@ -457,7 +454,7 @@ export class Sleeve extends Person implements SleevePerson {
this.hp.current -= amt;
if (this.hp.current <= 0) {
this.shock = Math.max(0, this.shock - 0.5);
this.shock = Math.min(100, this.shock + 0.5);
this.hp.current = this.hp.max;
return true;
} else {

@ -11,8 +11,8 @@ export class SleeveRecoveryWork extends Work {
}
process(sleeve: Sleeve, cycles: number) {
sleeve.shock = Math.min(100, sleeve.shock + 0.0002 * cycles);
if (sleeve.shock >= 100) sleeve.stopWork();
sleeve.shock = Math.max(0, sleeve.shock - 0.0002 * cycles);
if (sleeve.shock <= 0) sleeve.stopWork();
}
APICopy() {

@ -169,12 +169,12 @@ export function SleeveElem(props: IProps): React.ReactElement {
</span>
</Tooltip>
<Tooltip
title={props.sleeve.shock < 100 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
title={props.sleeve.shock > 0 ? <Typography>Unlocked when sleeve has fully recovered</Typography> : ""}
>
<span>
<Button
onClick={() => setAugmentationsOpen(true)}
disabled={props.sleeve.shock < 100}
disabled={props.sleeve.shock > 0}
sx={{ width: "100%", height: "100%" }}
>
Manage Augmentations

@ -78,7 +78,7 @@ export function StatsElement(props: IProps): React.ReactElement {
<StatsRow
name="Shock"
color={Settings.theme.primary}
data={{ content: numeralWrapper.formatSleeveShock(100 - props.sleeve.shock) }}
data={{ content: numeralWrapper.formatSleeveShock(props.sleeve.shock) }}
/>
<StatsRow
name="Sync"

@ -245,7 +245,7 @@ const canDo: {
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
"Workout at Gym": (sleeve: Sleeve) => [CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
"Perform Bladeburner Actions": () => !!Player.bladeburner,
"Shock Recovery": (sleeve: Sleeve) => sleeve.shock < 100,
"Shock Recovery": (sleeve: Sleeve) => sleeve.shock > 0,
Synchronize: (sleeve: Sleeve) => sleeve.sync < 100,
};

@ -354,306 +354,309 @@ function evaluateVersionCompatibility(ver: string | number): void {
v1APIBreak();
ver = 1;
}
if (typeof ver === "number") {
if (ver < 2) {
AwardNFG(10);
Player.reapplyAllAugmentations(true);
Player.reapplyAllSourceFiles();
}
if (ver < 3) {
anyPlayer.money = parseFloat(anyPlayer.money);
if (anyPlayer.corporation) {
anyPlayer.corporation.funds = parseFloat(anyPlayer.corporation.funds);
anyPlayer.corporation.revenue = parseFloat(anyPlayer.corporation.revenue);
anyPlayer.corporation.expenses = parseFloat(anyPlayer.corporation.expenses);
if (typeof ver !== "number") return;
if (ver < 2) {
AwardNFG(10);
Player.reapplyAllAugmentations(true);
Player.reapplyAllSourceFiles();
}
if (ver < 3) {
anyPlayer.money = parseFloat(anyPlayer.money);
if (anyPlayer.corporation) {
anyPlayer.corporation.funds = parseFloat(anyPlayer.corporation.funds);
anyPlayer.corporation.revenue = parseFloat(anyPlayer.corporation.revenue);
anyPlayer.corporation.expenses = parseFloat(anyPlayer.corporation.expenses);
for (let i = 0; i < anyPlayer.corporation.divisions.length; ++i) {
const ind = anyPlayer.corporation.divisions[i];
ind.lastCycleRevenue = parseFloat(ind.lastCycleRevenue);
ind.lastCycleExpenses = parseFloat(ind.lastCycleExpenses);
ind.thisCycleRevenue = parseFloat(ind.thisCycleRevenue);
ind.thisCycleExpenses = parseFloat(ind.thisCycleExpenses);
}
for (let i = 0; i < anyPlayer.corporation.divisions.length; ++i) {
const ind = anyPlayer.corporation.divisions[i];
ind.lastCycleRevenue = parseFloat(ind.lastCycleRevenue);
ind.lastCycleExpenses = parseFloat(ind.lastCycleExpenses);
ind.thisCycleRevenue = parseFloat(ind.thisCycleRevenue);
ind.thisCycleExpenses = parseFloat(ind.thisCycleExpenses);
}
}
if (ver < 9) {
if (StockMarket.hasOwnProperty("Joes Guns")) {
const s = StockMarket["Joes Guns"];
delete StockMarket["Joes Guns"];
StockMarket[LocationName.Sector12JoesGuns] = s;
}
}
if (ver < 9) {
if (StockMarket.hasOwnProperty("Joes Guns")) {
const s = StockMarket["Joes Guns"];
delete StockMarket["Joes Guns"];
StockMarket[LocationName.Sector12JoesGuns] = s;
}
if (ver < 10) {
// Augmentation name was changed in 0.56.0 but sleeves aug list was missed.
if (anyPlayer.sleeves && anyPlayer.sleeves.length > 0) {
for (const sleeve of anyPlayer.sleeves) {
if (!sleeve.augmentations || sleeve.augmentations.length === 0) continue;
for (const augmentation of sleeve.augmentations) {
if (augmentation.name !== "Graphene BranchiBlades Upgrade") continue;
augmentation.name = "Graphene BrachiBlades Upgrade";
}
}
}
}
if (ver < 12) {
if (anyPlayer.resleeves !== undefined) {
delete anyPlayer.resleeves;
}
}
if (ver < 15) {
Settings.EditorTheme = { ...defaultMonacoTheme };
}
//Fix contract names
if (ver < 16) {
Factions[FactionNames.ShadowsOfAnarchy] = new Faction(FactionNames.ShadowsOfAnarchy);
//Iterate over all contracts on all servers
for (const server of GetAllServers()) {
for (const contract of server.contracts) {
//Rename old "HammingCodes: Integer to encoded Binary" contracts
//to "HammingCodes: Integer to Encoded Binary"
if (contract.type == "HammingCodes: Integer to encoded Binary") {
contract.type = "HammingCodes: Integer to Encoded Binary";
}
}
}
}
const v22PlayerBreak = () => {
// reset HP correctly to avoid crash
anyPlayer.hp = { current: 1, max: 1 };
}
if (ver < 10) {
// Augmentation name was changed in 0.56.0 but sleeves aug list was missed.
if (anyPlayer.sleeves && anyPlayer.sleeves.length > 0) {
for (const sleeve of anyPlayer.sleeves) {
sleeve.hp = { current: 1, max: 1 };
}
// transfer over old exp to new struct
anyPlayer.exp.hacking = anyPlayer.hacking_exp;
anyPlayer.exp.strength = anyPlayer.strength_exp;
anyPlayer.exp.defense = anyPlayer.defense_exp;
anyPlayer.exp.dexterity = anyPlayer.dexterity_exp;
anyPlayer.exp.agility = anyPlayer.agility_exp;
anyPlayer.exp.charisma = anyPlayer.charisma_exp;
anyPlayer.exp.intelligence = anyPlayer.intelligence_exp;
};
// Fix bugged NFG accumulation in owned augmentations
if (ver < 17) {
let ownedNFGs = [...Player.augmentations];
ownedNFGs = ownedNFGs.filter((aug) => aug.name === AugmentationNames.NeuroFluxGovernor);
const newNFG = new PlayerOwnedAugmentation(AugmentationNames.NeuroFluxGovernor);
newNFG.level = 0;
for (const nfg of ownedNFGs) {
newNFG.level += nfg.level;
}
Player.augmentations = [
...Player.augmentations.filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor),
newNFG,
];
v22PlayerBreak();
Player.reapplyAllAugmentations(true);
Player.reapplyAllSourceFiles();
}
if (ver < 20) {
// Create the darkweb for everyone but it won't be linked
const dw = GetServer(SpecialServers.DarkWeb);
if (!dw) {
const darkweb = safelyCreateUniqueServer({
ip: createUniqueRandomIp(),
hostname: SpecialServers.DarkWeb,
organizationName: "",
isConnectedTo: false,
adminRights: false,
purchasedByPlayer: false,
maxRam: 1,
});
AddToAllServers(darkweb);
if (!sleeve.augmentations || sleeve.augmentations.length === 0) continue;
for (const augmentation of sleeve.augmentations) {
if (augmentation.name !== "Graphene BranchiBlades Upgrade") continue;
augmentation.name = "Graphene BrachiBlades Upgrade";
}
}
}
if (ver < 21) {
// 2.0.0 work rework
AwardNFG(10);
const create = anyPlayer["createProgramName"];
if (create) Player.getHomeComputer().pushProgram(create);
const graft = anyPlayer["graftAugmentationName"];
if (graft) Player.augmentations.push({ name: graft, level: 1 });
}
if (ver < 12) {
if (anyPlayer.resleeves !== undefined) {
delete anyPlayer.resleeves;
}
if (ver < 22) {
v22PlayerBreak();
v2APIBreak();
}
if (ver < 15) {
Settings.EditorTheme = { ...defaultMonacoTheme };
}
//Fix contract names
if (ver < 16) {
Factions[FactionNames.ShadowsOfAnarchy] = new Faction(FactionNames.ShadowsOfAnarchy);
//Iterate over all contracts on all servers
for (const server of GetAllServers()) {
for (const contract of server.contracts) {
//Rename old "HammingCodes: Integer to encoded Binary" contracts
//to "HammingCodes: Integer to Encoded Binary"
if (contract.type == "HammingCodes: Integer to encoded Binary") {
contract.type = "HammingCodes: Integer to Encoded Binary";
}
}
}
if (ver < 23) {
anyPlayer.currentWork = null;
}
const v22PlayerBreak = () => {
// reset HP correctly to avoid crash
anyPlayer.hp = { current: 1, max: 1 };
for (const sleeve of anyPlayer.sleeves) {
sleeve.hp = { current: 1, max: 1 };
}
if (ver < 24) {
Player.getHomeComputer().scripts.forEach((s) => s.filename.endsWith(".ns") && (s.filename += ".js"));
// transfer over old exp to new struct
anyPlayer.exp.hacking = anyPlayer.hacking_exp;
anyPlayer.exp.strength = anyPlayer.strength_exp;
anyPlayer.exp.defense = anyPlayer.defense_exp;
anyPlayer.exp.dexterity = anyPlayer.dexterity_exp;
anyPlayer.exp.agility = anyPlayer.agility_exp;
anyPlayer.exp.charisma = anyPlayer.charisma_exp;
anyPlayer.exp.intelligence = anyPlayer.intelligence_exp;
};
// Fix bugged NFG accumulation in owned augmentations
if (ver < 17) {
let ownedNFGs = [...Player.augmentations];
ownedNFGs = ownedNFGs.filter((aug) => aug.name === AugmentationNames.NeuroFluxGovernor);
const newNFG = new PlayerOwnedAugmentation(AugmentationNames.NeuroFluxGovernor);
newNFG.level = 0;
for (const nfg of ownedNFGs) {
newNFG.level += nfg.level;
}
if (ver < 25) {
const removePlayerFields = [
"hacking_chance_mult",
"hacking_speed_mult",
"hacking_money_mult",
"hacking_grow_mult",
"hacking_mult",
"strength_mult",
"defense_mult",
"dexterity_mult",
"agility_mult",
"charisma_mult",
"hacking_exp_mult",
"strength_exp_mult",
"defense_exp_mult",
"dexterity_exp_mult",
"agility_exp_mult",
"charisma_exp_mult",
"company_rep_mult",
"faction_rep_mult",
"crime_money_mult",
"crime_success_mult",
"work_money_mult",
"hacknet_node_money_mult",
"hacknet_node_purchase_cost_mult",
"hacknet_node_ram_cost_mult",
"hacknet_node_core_cost_mult",
"hacknet_node_level_cost_mult",
"bladeburner_max_stamina_mult",
"bladeburner_stamina_gain_mult",
"bladeburner_analysis_mult",
"bladeburner_success_chance_mult",
"hacking_exp",
"strength_exp",
"defense_exp",
"dexterity_exp",
"agility_exp",
"charisma_exp",
"intelligence_exp",
"companyName",
"isWorking",
"workType",
"workCostMult",
"workExpMult",
"currentWorkFactionName",
"currentWorkFactionDescription",
"workHackExpGainRate",
"workStrExpGainRate",
"workDefExpGainRate",
"workDexExpGainRate",
"workAgiExpGainRate",
"workChaExpGainRate",
"workRepGainRate",
"workMoneyGainRate",
"workMoneyLossRate",
"workHackExpGained",
"workStrExpGained",
"workDefExpGained",
"workDexExpGained",
"workAgiExpGained",
"workChaExpGained",
"workRepGained",
"workMoneyGained",
"createProgramName",
"createProgramReqLvl",
"graftAugmentationName",
"timeWorkedGraftAugmentation",
"className",
"crimeType",
"timeWorked",
"timeWorkedCreateProgram",
"timeNeededToCompleteWork",
"factionWorkType",
"committingCrimeThruSingFn",
"singFnCrimeWorkerScript",
"hacking",
"max_hp",
"strength",
"defense",
"dexterity",
"agility",
"charisma",
"intelligence",
];
const removeSleeveFields = [
"gymStatType",
"bbAction",
"bbContract",
"hacking",
"strength",
"defense",
"dexterity",
"agility",
"charisma",
"intelligence",
"max_hp",
"hacking_exp",
"strength_exp",
"defense_exp",
"dexterity_exp",
"agility_exp",
"charisma_exp",
"intelligence_exp",
"hacking_mult",
"strength_mult",
"defense_mult",
"dexterity_mult",
"agility_mult",
"charisma_mult",
"hacking_exp_mult",
"strength_exp_mult",
"defense_exp_mult",
"dexterity_exp_mult",
"agility_exp_mult",
"charisma_exp_mult",
"hacking_chance_mult",
"hacking_speed_mult",
"hacking_money_mult",
"hacking_grow_mult",
"company_rep_mult",
"faction_rep_mult",
"crime_money_mult",
"crime_success_mult",
"work_money_mult",
"hacknet_node_money_mult",
"hacknet_node_purchase_cost_mult",
"hacknet_node_ram_cost_mult",
"hacknet_node_core_cost_mult",
"hacknet_node_level_cost_mult",
"bladeburner_max_stamina_mult",
"bladeburner_stamina_gain_mult",
"bladeburner_analysis_mult",
"bladeburner_success_chance_mult",
"className",
"crimeType",
"currentTask",
"currentTaskLocation",
"currentTaskMaxTime",
"currentTaskTime",
"earningsForSleeves",
"earningsForPlayer",
"earningsForTask",
"factionWorkType",
"gainRatesForTask",
"logs",
];
let intExp = Number(anyPlayer.intelligence_exp);
Player.augmentations = [
...Player.augmentations.filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor),
newNFG,
];
v22PlayerBreak();
Player.reapplyAllAugmentations(true);
Player.reapplyAllSourceFiles();
}
if (ver < 20) {
// Create the darkweb for everyone but it won't be linked
const dw = GetServer(SpecialServers.DarkWeb);
if (!dw) {
const darkweb = safelyCreateUniqueServer({
ip: createUniqueRandomIp(),
hostname: SpecialServers.DarkWeb,
organizationName: "",
isConnectedTo: false,
adminRights: false,
purchasedByPlayer: false,
maxRam: 1,
});
AddToAllServers(darkweb);
}
}
if (ver < 21) {
// 2.0.0 work rework
AwardNFG(10);
const create = anyPlayer["createProgramName"];
if (create) Player.getHomeComputer().pushProgram(create);
const graft = anyPlayer["graftAugmentationName"];
if (graft) Player.augmentations.push({ name: graft, level: 1 });
}
if (ver < 22) {
v22PlayerBreak();
v2APIBreak();
}
if (ver < 23) {
anyPlayer.currentWork = null;
}
if (ver < 24) {
Player.getHomeComputer().scripts.forEach((s) => s.filename.endsWith(".ns") && (s.filename += ".js"));
}
if (ver < 25) {
const removePlayerFields = [
"hacking_chance_mult",
"hacking_speed_mult",
"hacking_money_mult",
"hacking_grow_mult",
"hacking_mult",
"strength_mult",
"defense_mult",
"dexterity_mult",
"agility_mult",
"charisma_mult",
"hacking_exp_mult",
"strength_exp_mult",
"defense_exp_mult",
"dexterity_exp_mult",
"agility_exp_mult",
"charisma_exp_mult",
"company_rep_mult",
"faction_rep_mult",
"crime_money_mult",
"crime_success_mult",
"work_money_mult",
"hacknet_node_money_mult",
"hacknet_node_purchase_cost_mult",
"hacknet_node_ram_cost_mult",
"hacknet_node_core_cost_mult",
"hacknet_node_level_cost_mult",
"bladeburner_max_stamina_mult",
"bladeburner_stamina_gain_mult",
"bladeburner_analysis_mult",
"bladeburner_success_chance_mult",
"hacking_exp",
"strength_exp",
"defense_exp",
"dexterity_exp",
"agility_exp",
"charisma_exp",
"intelligence_exp",
"companyName",
"isWorking",
"workType",
"workCostMult",
"workExpMult",
"currentWorkFactionName",
"currentWorkFactionDescription",
"workHackExpGainRate",
"workStrExpGainRate",
"workDefExpGainRate",
"workDexExpGainRate",
"workAgiExpGainRate",
"workChaExpGainRate",
"workRepGainRate",
"workMoneyGainRate",
"workMoneyLossRate",
"workHackExpGained",
"workStrExpGained",
"workDefExpGained",
"workDexExpGained",
"workAgiExpGained",
"workChaExpGained",
"workRepGained",
"workMoneyGained",
"createProgramName",
"createProgramReqLvl",
"graftAugmentationName",
"timeWorkedGraftAugmentation",
"className",
"crimeType",
"timeWorked",
"timeWorkedCreateProgram",
"timeNeededToCompleteWork",
"factionWorkType",
"committingCrimeThruSingFn",
"singFnCrimeWorkerScript",
"hacking",
"max_hp",
"strength",
"defense",
"dexterity",
"agility",
"charisma",
"intelligence",
];
const removeSleeveFields = [
"gymStatType",
"bbAction",
"bbContract",
"hacking",
"strength",
"defense",
"dexterity",
"agility",
"charisma",
"intelligence",
"max_hp",
"hacking_exp",
"strength_exp",
"defense_exp",
"dexterity_exp",
"agility_exp",
"charisma_exp",
"intelligence_exp",
"hacking_mult",
"strength_mult",
"defense_mult",
"dexterity_mult",
"agility_mult",
"charisma_mult",
"hacking_exp_mult",
"strength_exp_mult",
"defense_exp_mult",
"dexterity_exp_mult",
"agility_exp_mult",
"charisma_exp_mult",
"hacking_chance_mult",
"hacking_speed_mult",
"hacking_money_mult",
"hacking_grow_mult",
"company_rep_mult",
"faction_rep_mult",
"crime_money_mult",
"crime_success_mult",
"work_money_mult",
"hacknet_node_money_mult",
"hacknet_node_purchase_cost_mult",
"hacknet_node_ram_cost_mult",
"hacknet_node_core_cost_mult",
"hacknet_node_level_cost_mult",
"bladeburner_max_stamina_mult",
"bladeburner_stamina_gain_mult",
"bladeburner_analysis_mult",
"bladeburner_success_chance_mult",
"className",
"crimeType",
"currentTask",
"currentTaskLocation",
"currentTaskMaxTime",
"currentTaskTime",
"earningsForSleeves",
"earningsForPlayer",
"earningsForTask",
"factionWorkType",
"gainRatesForTask",
"logs",
];
let intExp = Number(anyPlayer.intelligence_exp);
if (isNaN(intExp)) intExp = 0;
anyPlayer.exp.intelligence += intExp;
for (const field of removePlayerFields) {
delete anyPlayer[field];
}
for (const sleeve of anyPlayer.sleeves) {
const anySleeve = sleeve as any;
let intExp = Number(anySleeve.intelligence_exp);
if (isNaN(intExp)) intExp = 0;
anyPlayer.exp.intelligence += intExp;
for (const field of removePlayerFields) {
delete anyPlayer[field];
}
for (const sleeve of anyPlayer.sleeves) {
const anySleeve = sleeve as any;
let intExp = Number(anySleeve.intelligence_exp);
if (isNaN(intExp)) intExp = 0;
anySleeve.exp.intelligence += intExp;
for (const field of removeSleeveFields) {
delete sleeve[field];
}
anySleeve.exp.intelligence += intExp;
for (const field of removeSleeveFields) {
delete sleeve[field];
}
}
}
if (ver < 27) {
// Prior to v2.2.0, sleeve shock was 0 to 100 internally but displayed as 100 to 0. This unifies them as 100 to 0.
for (const sleeve of Player.sleeves) sleeve.shock = 100 - sleeve.shock;
}
}
function loadGame(saveString: string): boolean {
@ -730,7 +733,7 @@ function loadGame(saveString: string): boolean {
try {
const ver = JSON.parse(saveObj.VersionSave, Reviver);
evaluateVersionCompatibility(ver);
if (window.location.href.toLowerCase().includes("bitburner-beta")) {
if (window.location.href.toLowerCase().includes("bitburner-src")) {
// Beta branch, always show changes
createBetaUpdateText();
} else if (ver !== CONSTANTS.VersionNumber) {

@ -10,7 +10,9 @@ export function dialogBoxCreate(txt: string | JSX.Element, html = false): void {
) : html ? (
<div dangerouslySetInnerHTML={{ __html: txt }}></div>
) : (
<Typography component="span">{txt}</Typography>
<Typography component="span" style={{ whiteSpace: "pre-wrap" }}>
{txt}
</Typography>
),
);
}

@ -2,12 +2,12 @@
import { Octokit } from "@octokit/rest";
import commandLineArgs from "command-line-args";
const owner = "danielyxie";
const repo = "bitburner";
const owner = "bitburner-official";
const repo = "bitburner-src";
const cliArgs = commandLineArgs([
{ name: "from", alias: "f", type: String },
{ name: "to", alias: "t", type: String },
{ name: "to", alias: "t", type: String, defaultValue: undefined },
]);
class MergeChangelog {
@ -75,18 +75,22 @@ class MergeChangelog {
const pulls = [];
for (const entry of searchResults) {
const r = await this.octokit.rest.pulls
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);
.then((response) =>
pulls.push({
...entry,
merge_commit_sha: response.data.merge_commit_sha,
head_commit_sha: response.data.head.sha,
}),
)
.catch((e) => {
console.warn(`Encountered error retrieving pull: ${e}`);
});
await sleep(1000);
}
return pulls;
@ -234,11 +238,17 @@ const sleep = async (wait) => {
});
};
const token = process.env.GITHUB_API_TOKEN;
if (!token) {
console.log("You need to set the env var GITHUB_API_TOKEN.");
process.exit(1);
}
const api = new MergeChangelog({ auth: process.env.GITHUB_API_TOKEN });
if (!cliArgs.from || !cliArgs.to) {
console.error("USAGE: node index.js --from hash --to hash");
if (!cliArgs.from) {
console.error("USAGE: node index.js --from hash [--to hash]");
process.exit();
}
cliArgs.to ??= await api.getLastCommitByBranch("dev");
api.getChangelog(cliArgs.from, cliArgs.to).then((data) => {
console.log(data.log);
});