mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-09 17:23:53 +01:00
fix a bunch of small v2 related bugs
This commit is contained in:
parent
5d2b81053d
commit
faa3e212f3
@ -30,6 +30,7 @@ secrets that you've been searching for.
|
||||
Tools & Resources <toolsandresources>
|
||||
Changelog <changelog>
|
||||
v1.0.0 script migration guide <v1.0.0_migration.rst>
|
||||
v2.0.0 script migration guide <v2.0.0_migration.rst>
|
||||
404 <404.rst>
|
||||
Donate <https://paypal.me/danielyxie>
|
||||
|
||||
|
93
doc/source/v2.0.0_migration.rst
Normal file
93
doc/source/v2.0.0_migration.rst
Normal file
@ -0,0 +1,93 @@
|
||||
v2.0.0 Migration Guide
|
||||
======================
|
||||
|
||||
In v2.0.0 a few more API have been broken.
|
||||
|
||||
Working
|
||||
-------
|
||||
|
||||
Working has been rebuilt from the grounds up. The motivation for that change is that all
|
||||
different types of work all required different cached variables on the main Player object.
|
||||
This caused a lot of bugs and crashes. It's been reworked in such a way as to prevent bugs
|
||||
and make it nearly trivial to add new kinds of work.
|
||||
All work type give their reward immediately. No need to stop work to bank rewards like reputation.
|
||||
Faction and Company work no longer have a time limit.
|
||||
Company work no longer reduces rep gain by half for quitting early.
|
||||
Company faction require 400k rep to join (from 200k)
|
||||
Backdooring company server reduces faction requirement to 300k.
|
||||
All types of work generally no longer keep track of cumulative gains like exp and reputation since it's applied instantly.
|
||||
|
||||
commitCrime
|
||||
-----------
|
||||
|
||||
crime now loops, meaning after finishing one shoplift you start the next one with no input. While the signature
|
||||
has not changed its behavior has. It also has a new 'focus' parameters.
|
||||
|
||||
getPlayer
|
||||
---------
|
||||
|
||||
The following work-related fields are not longer included:
|
||||
|
||||
* workChaExpGained
|
||||
* currentWorkFactionName
|
||||
* workDexExpGained
|
||||
* workHackExpGained
|
||||
* createProgramReqLvl
|
||||
* workStrExpGained
|
||||
* companyName
|
||||
* crimeType
|
||||
* workRepGained
|
||||
* workChaExpGainRate
|
||||
* workType
|
||||
* workStrExpGainRate
|
||||
* isWorking
|
||||
* workRepGainRate
|
||||
* workDefExpGained
|
||||
* currentWorkFactionDescription
|
||||
* workHackExpGainRate
|
||||
* workAgiExpGainRate
|
||||
* workDexExpGainRate
|
||||
* workMoneyGained
|
||||
* workMoneyLossRate
|
||||
* workMoneyGainRate
|
||||
* createProgramName
|
||||
* workDefExpGainRate
|
||||
* workAgiExpGained
|
||||
* className
|
||||
|
||||
The reason for that is that these fields are all, in one way or another, included in the new work field 'currentWork'.
|
||||
Some of these values are also irrelevant.
|
||||
'currentWork' will be one of many different kind of value. For example when creating a program it will have a programName field.
|
||||
One field that all kinds of work have in common is 'type' which denotes the current kind of work.
|
||||
|
||||
All fields ending in _mult have been moved to the 'mults' struct.
|
||||
For example: getPlayer().hacking_skill_mult is now getPlayer().mults.hacking_skill
|
||||
|
||||
workForCompany
|
||||
--------------
|
||||
|
||||
The argument 'companyName' is now not-optional.
|
||||
|
||||
|
||||
getScriptIncome & getScriptExpGain
|
||||
----------------------------------
|
||||
|
||||
Those 2 functions used to have a call where no arguments would return the total for all scripts. This caused weird signature.
|
||||
If you want to get the total income/exp for all scripts used the new getTotalScriptIncome / getTotalScriptExpGain instead.
|
||||
|
||||
scp
|
||||
---
|
||||
|
||||
scp has it's 2 last argument reversed, the signature is now scp(files, destination, optional_source)
|
||||
|
||||
Singularity
|
||||
-----------
|
||||
|
||||
A while ago top level singularity function were deprecated in favor of the singularity namespace.
|
||||
This means calls like 'ns.connect' need to be changed to 'ns.singularity.connect'
|
||||
|
||||
|
||||
stock.buy and stock.sell
|
||||
------------------------
|
||||
These 2 functions were renamed to stock.buyStock and stock.sellStock because 'buy' and 'sell'
|
||||
are very common tokens that would trick the ram calculation.
|
10
src/Alias.ts
10
src/Alias.ts
@ -36,16 +36,18 @@ export function printAliases(): void {
|
||||
|
||||
// Returns true if successful, false otherwise
|
||||
export function parseAliasDeclaration(dec: string, global = false): boolean {
|
||||
const re = /^([\w|!%,@-]+)=(("(.+)")|('(.+)'))$/;
|
||||
console.log(dec);
|
||||
const re = /^([\w|!%,@-]+)=(.+)$/;
|
||||
const matches = dec.match(re);
|
||||
if (matches == null || matches.length != 7) {
|
||||
console.log(matches);
|
||||
if (matches == null || matches.length != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (global) {
|
||||
addGlobalAlias(matches[1], matches[4] || matches[6]);
|
||||
addGlobalAlias(matches[1], matches[2]);
|
||||
} else {
|
||||
addAlias(matches[1], matches[4] || matches[6]);
|
||||
addAlias(matches[1], matches[2]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -107,12 +107,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
step={25}
|
||||
min={0}
|
||||
max={500}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of lines a script's logs can hold. Setting this too high can cause the game to use a
|
||||
lot of memory if you have many scripts running.
|
||||
</>
|
||||
}
|
||||
tooltip={<>The maximum number of recently killed scripts the game will keep.</>}
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Netscript log size"
|
||||
|
@ -84,7 +84,6 @@ import {
|
||||
Infiltration as IInfiltration,
|
||||
RunningScript as IRunningScript,
|
||||
RecentScript as IRecentScript,
|
||||
SourceFileLvl,
|
||||
BasicHGWOptions,
|
||||
ProcessInfo,
|
||||
HackingMultipliers,
|
||||
@ -2470,6 +2469,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
inBladeburner: Player.inBladeburner(),
|
||||
hasCorporation: Player.hasCorporation(),
|
||||
entropy: Player.entropy,
|
||||
currentWork: Player.currentWork,
|
||||
};
|
||||
Object.assign(data.jobs, Player.jobs);
|
||||
return data;
|
||||
|
@ -11,7 +11,6 @@ import { LocationName } from "../../../Locations/data/LocationNames";
|
||||
import { Locations } from "../../../Locations/Locations";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums";
|
||||
import { Settings } from "../../../Settings/Settings";
|
||||
import { IMap } from "../../../types";
|
||||
import { use } from "../../../ui/Context";
|
||||
import { ConfirmationModal } from "../../../ui/React/ConfirmationModal";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
@ -20,7 +19,15 @@ import { IPlayer } from "../../IPlayer";
|
||||
import { GraftableAugmentation } from "../GraftableAugmentation";
|
||||
import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers";
|
||||
|
||||
export const GraftableAugmentations: IMap<GraftableAugmentation> = {};
|
||||
export const GraftableAugmentations = (): Record<string, GraftableAugmentation> => {
|
||||
const gAugs: Record<string, GraftableAugmentation> = {};
|
||||
for (const aug of Object.values(StaticAugmentations)) {
|
||||
const name = aug.name;
|
||||
const graftableAug = new GraftableAugmentation(aug);
|
||||
gAugs[name] = graftableAug;
|
||||
}
|
||||
return gAugs;
|
||||
};
|
||||
|
||||
const canGraft = (player: IPlayer, aug: GraftableAugmentation): boolean => {
|
||||
if (player.money < aug.cost) {
|
||||
@ -56,11 +63,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
|
||||
for (const aug of Object.values(StaticAugmentations)) {
|
||||
const name = aug.name;
|
||||
const graftableAug = new GraftableAugmentation(aug);
|
||||
GraftableAugmentations[name] = graftableAug;
|
||||
}
|
||||
const graftableAugmentations = useState(GraftableAugmentations())[0];
|
||||
|
||||
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs(player)[0]);
|
||||
const [graftOpen, setGraftOpen] = useState(false);
|
||||
@ -75,7 +78,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
const augs = getGraftingAvailableAugs(player);
|
||||
switch (Settings.PurchaseAugmentationsOrder) {
|
||||
case PurchaseAugmentationsOrderSetting.Cost:
|
||||
return augs.sort((a, b) => GraftableAugmentations[a].cost - GraftableAugmentations[b].cost);
|
||||
return augs.sort((a, b) => graftableAugmentations[a].cost - graftableAugmentations[b].cost);
|
||||
default:
|
||||
return augs;
|
||||
}
|
||||
@ -126,7 +129,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||
<Typography
|
||||
sx={{
|
||||
color: canGraft(player, GraftableAugmentations[k])
|
||||
color: canGraft(player, graftableAugmentations[k])
|
||||
? Settings.theme.primary
|
||||
: Settings.theme.disabled,
|
||||
}}
|
||||
@ -143,11 +146,11 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<Button
|
||||
onClick={() => setGraftOpen(true)}
|
||||
sx={{ width: "100%" }}
|
||||
disabled={!canGraft(player, GraftableAugmentations[selectedAug])}
|
||||
disabled={!canGraft(player, graftableAugmentations[selectedAug])}
|
||||
>
|
||||
Graft Augmentation (
|
||||
<Typography>
|
||||
<Money money={GraftableAugmentations[selectedAug].cost} player={player} />
|
||||
<Money money={graftableAugmentations[selectedAug].cost} player={player} />
|
||||
</Typography>
|
||||
)
|
||||
</Button>
|
||||
@ -183,7 +186,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<Typography color={Settings.theme.info}>
|
||||
<b>Time to Graft:</b>{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
|
||||
calculateGraftingTimeWithBonus(player, graftableAugmentations[selectedAug]),
|
||||
)}
|
||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||
</Typography>
|
||||
|
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
2
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -4471,7 +4471,7 @@ export interface NS {
|
||||
readonly infiltration: Infiltration;
|
||||
/**
|
||||
* Namespace for corporation functions.
|
||||
* RAM cost: 0 GB
|
||||
* RAM cost: 1022.4 GB
|
||||
*/
|
||||
readonly corporation: Corporation;
|
||||
|
||||
|
@ -51,7 +51,7 @@ export function buyStock(
|
||||
}
|
||||
if (stock == null || isNaN(shares)) {
|
||||
if (workerScript) {
|
||||
workerScript.log("stock.buy", () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
|
||||
workerScript.log("stock.buyStock", () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
|
||||
} else if (opts.suppressDialog !== true) {
|
||||
dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer");
|
||||
}
|
||||
@ -67,7 +67,7 @@ export function buyStock(
|
||||
if (Player.money < totalPrice) {
|
||||
if (workerScript) {
|
||||
workerScript.log(
|
||||
"stock.buy",
|
||||
"stock.buyStock",
|
||||
() =>
|
||||
`You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`,
|
||||
);
|
||||
@ -86,7 +86,7 @@ export function buyStock(
|
||||
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
|
||||
if (workerScript) {
|
||||
workerScript.log(
|
||||
"stock.buy",
|
||||
"stock.buyStock",
|
||||
() =>
|
||||
`Purchasing '${shares + stock.playerShares + stock.playerShortShares}' shares would exceed ${
|
||||
stock.symbol
|
||||
@ -119,7 +119,7 @@ export function buyStock(
|
||||
} for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(
|
||||
CONSTANTS.StockMarketCommission,
|
||||
)} in commission fees.`;
|
||||
workerScript.log("stock.buy", () => resultTxt);
|
||||
workerScript.log("stock.buyStock", () => resultTxt);
|
||||
} else if (opts.suppressDialog !== true) {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
@ -149,7 +149,7 @@ export function sellStock(
|
||||
// Sanitize/Validate arguments
|
||||
if (stock == null || shares < 0 || isNaN(shares)) {
|
||||
if (workerScript) {
|
||||
workerScript.log("stock.sell", () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
|
||||
workerScript.log("stock.sellStock", () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
|
||||
} else if (opts.suppressDialog !== true) {
|
||||
dialogBoxCreate(
|
||||
"Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer",
|
||||
@ -195,7 +195,7 @@ export function sellStock(
|
||||
const resultTxt =
|
||||
`Sold ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
|
||||
`After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`;
|
||||
workerScript.log("stock.sell", () => resultTxt);
|
||||
workerScript.log("stock.sellStock", () => resultTxt);
|
||||
} else if (opts.suppressDialog !== true) {
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
|
@ -7,6 +7,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { CrimeType } from "../utils/WorkType";
|
||||
import { Work, WorkType } from "./Work";
|
||||
import { newWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
|
||||
|
||||
interface CrimeWorkParams {
|
||||
crimeType: CrimeType;
|
||||
@ -42,14 +43,22 @@ export class CrimeWork extends Work {
|
||||
return false;
|
||||
}
|
||||
|
||||
earnings(): WorkStats {
|
||||
const crime = this.getCrime();
|
||||
return newWorkStats({
|
||||
money: crime.money,
|
||||
hackExp: crime.hacking_exp * 2,
|
||||
strExp: crime.strength_exp * 2,
|
||||
defExp: crime.defense_exp * 2,
|
||||
dexExp: crime.dexterity_exp * 2,
|
||||
agiExp: crime.agility_exp * 2,
|
||||
chaExp: crime.charisma_exp * 2,
|
||||
intExp: crime.intelligence_exp * 2,
|
||||
});
|
||||
}
|
||||
|
||||
commit(player: IPlayer): void {
|
||||
let crime = null;
|
||||
for (const i of Object.keys(Crimes)) {
|
||||
if (Crimes[i].type == this.crimeType) {
|
||||
crime = Crimes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
const crime = this.getCrime();
|
||||
if (crime == null) {
|
||||
dialogBoxCreate(
|
||||
`ERR: Unrecognized crime type (${this.crimeType}). This is probably a bug please contact the developer`,
|
||||
@ -59,33 +68,23 @@ export class CrimeWork extends Work {
|
||||
const focusPenalty = player.focusPenalty();
|
||||
// exp times 2 because were trying to maintain the same numbers as before the conversion
|
||||
// Technically the definition of Crimes should have the success numbers and failure should divide by 4
|
||||
let hackExp = crime.hacking_exp * 2;
|
||||
let StrExp = crime.strength_exp * 2;
|
||||
let DefExp = crime.defense_exp * 2;
|
||||
let DexExp = crime.dexterity_exp * 2;
|
||||
let AgiExp = crime.agility_exp * 2;
|
||||
let ChaExp = crime.charisma_exp * 2;
|
||||
let gains = scaleWorkStats(this.earnings(), focusPenalty);
|
||||
let karma = crime.karma;
|
||||
const success = determineCrimeSuccess(player, crime.type);
|
||||
if (success) {
|
||||
player.gainMoney(crime.money * focusPenalty, "crime");
|
||||
player.gainMoney(gains.money, "crime");
|
||||
player.numPeopleKilled += crime.kills;
|
||||
player.gainIntelligenceExp(crime.intelligence_exp * focusPenalty);
|
||||
player.gainIntelligenceExp(gains.intExp);
|
||||
} else {
|
||||
hackExp /= 4;
|
||||
StrExp /= 4;
|
||||
DefExp /= 4;
|
||||
DexExp /= 4;
|
||||
AgiExp /= 4;
|
||||
ChaExp /= 4;
|
||||
gains = scaleWorkStats(gains, 0.25);
|
||||
karma /= 4;
|
||||
}
|
||||
player.gainHackingExp(hackExp * focusPenalty);
|
||||
player.gainStrengthExp(StrExp * focusPenalty);
|
||||
player.gainDefenseExp(DefExp * focusPenalty);
|
||||
player.gainDexterityExp(DexExp * focusPenalty);
|
||||
player.gainAgilityExp(AgiExp * focusPenalty);
|
||||
player.gainCharismaExp(ChaExp * focusPenalty);
|
||||
player.gainHackingExp(gains.hackExp);
|
||||
player.gainStrengthExp(gains.strExp);
|
||||
player.gainDefenseExp(gains.defExp);
|
||||
player.gainDexterityExp(gains.dexExp);
|
||||
player.gainAgilityExp(gains.agiExp);
|
||||
player.gainCharismaExp(gains.chaExp);
|
||||
player.karma -= karma * focusPenalty;
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ export class GraftingWork extends Work {
|
||||
super(WorkType.GRAFTING, params?.singularity ?? true);
|
||||
this.unitCompleted = 0;
|
||||
this.augmentation = params?.augmentation ?? AugmentationNames.Targeting1;
|
||||
|
||||
if (params?.player) params.player.loseMoney(GraftableAugmentations[this.augmentation].cost, "augmentations");
|
||||
const gAugs = GraftableAugmentations();
|
||||
if (params?.player) params.player.loseMoney(gAugs[this.augmentation].cost, "augmentations");
|
||||
}
|
||||
|
||||
unitNeeded(): number {
|
||||
|
@ -48,7 +48,7 @@ interface IWorkInfo {
|
||||
stopTooltip?: string | React.ReactElement;
|
||||
}
|
||||
|
||||
export function ExpRows(rate: WorkStats): React.ReactElement[] {
|
||||
function ExpRows(rate: WorkStats): React.ReactElement[] {
|
||||
return [
|
||||
rate.hackExp > 0 ? (
|
||||
<StatsRow
|
||||
@ -119,6 +119,77 @@ export function ExpRows(rate: WorkStats): React.ReactElement[] {
|
||||
];
|
||||
}
|
||||
|
||||
function CrimeExpRows(rate: WorkStats): React.ReactElement[] {
|
||||
return [
|
||||
rate.hackExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Hacking Exp"
|
||||
color={Settings.theme.hack}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.hackExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
rate.strExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Strength Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.strExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
rate.defExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Defense Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.defExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
rate.dexExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Dexterity Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.dexExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
rate.agiExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Agility Exp"
|
||||
color={Settings.theme.combat}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.agiExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
rate.chaExp > 0 ? (
|
||||
<StatsRow
|
||||
name="Charisma Exp"
|
||||
color={Settings.theme.cha}
|
||||
data={{
|
||||
content: `${numeralWrapper.formatExp(rate.chaExp * CYCLES_PER_SEC)}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
export function WorkInProgressRoot(): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
@ -149,7 +220,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
if (isCrimeWork(player.currentWork)) {
|
||||
const crime = player.currentWork.getCrime();
|
||||
const completion = (player.currentWork.unitCompleted / crime.time) * 100;
|
||||
|
||||
const gains = player.currentWork.earnings();
|
||||
workInfo = {
|
||||
buttons: {
|
||||
cancel: () => {
|
||||
@ -163,6 +234,15 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
},
|
||||
title: `You are attempting to ${crime.type}`,
|
||||
|
||||
gains: [
|
||||
<Typography>Gains (on success)</Typography>,
|
||||
<StatsRow name="Money:" color={Settings.theme.money}>
|
||||
<Typography>
|
||||
<Money money={gains.money} />
|
||||
</Typography>
|
||||
</StatsRow>,
|
||||
...CrimeExpRows(gains),
|
||||
],
|
||||
progress: {
|
||||
remaining: crime.time - player.currentWork.unitCompleted,
|
||||
percentage: completion,
|
||||
|
Loading…
Reference in New Issue
Block a user