mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-03-07 11:04:36 +01:00
2
.github/PULL_REQUEST_TEMPLATE
vendored
2
.github/PULL_REQUEST_TEMPLATE
vendored
@ -1,5 +1,7 @@
|
||||
# DELETE THIS AFTER READING
|
||||
|
||||
# READ CONTRIBUTING.md
|
||||
|
||||
# PR title
|
||||
|
||||
Formatted as such:
|
||||
|
4
dist/main.bundle.js
vendored
4
dist/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main.bundle.js.map
vendored
2
dist/main.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
42
dist/vendor.bundle.js
vendored
42
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
11
doc/NEW_BN_GUIDELINE.md
Normal file
11
doc/NEW_BN_GUIDELINE.md
Normal file
@ -0,0 +1,11 @@
|
||||
Promote:
|
||||
|
||||
- New mechanic
|
||||
- Coding problems based on NP problems. This makes solution that are easy to implement inefficient and solutions that are hard to implement efficent. (eg. Stanek)
|
||||
- inter-mechanic synergy
|
||||
- Simplicity (eg. Stanek, Hashnet. bad example: Corp)
|
||||
|
||||
Avoid:
|
||||
|
||||
- Failure conditions, it's very frustrating to revert several days worth of progress.
|
||||
- Making existing mechanic harder. This makes it hard to port the content to other BNs.
|
1
doc/POTENTIAL_BN_1.md
Normal file
1
doc/POTENTIAL_BN_1.md
Normal file
@ -0,0 +1 @@
|
||||
Sleeves meet Screeps (That's all I got)
|
3
doc/POTENTIAL_BN_2.md
Normal file
3
doc/POTENTIAL_BN_2.md
Normal file
@ -0,0 +1,3 @@
|
||||
A game of risk from the point of view of a politician.
|
||||
|
||||
You allocate resources on a world map, trying to win elections.
|
@ -336,12 +336,12 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | |
|
||||
| | | You are given an LZ-encoded string. Decode it and output the original string. |
|
||||
| | | |
|
||||
| | | Example: decoding '5aaabc340533bca' chunk-by-chunk |
|
||||
| | | 5aaabc -> aaabc |
|
||||
| | | 5aaabc34 -> aaabcaab |
|
||||
| | | 5aaabc340 -> aaabcaab |
|
||||
| | | 5aaabc34053 -> aaabcaabaabaa |
|
||||
| | | 5aaabc340533bca -> aaabcaabaabaabca |
|
||||
| | | Example: decoding '5aaabb450723abb' chunk-by-chunk |
|
||||
| | | 5aaabb -> aaabb |
|
||||
| | | 5aaabb45 -> aaabbaaab |
|
||||
| | | 5aaabb450 -> aaabbaaab |
|
||||
| | | 5aaabb45072 -> aaabbaaababababa |
|
||||
| | | 5aaabb450723abb -> aaabbaaababababaabb |
|
||||
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Compression III: LZ Compression | | Lempel-Ziv (LZ) compression is a data compression technique which encodes data using |
|
||||
| | | references to earlier parts of the data. In this variant of LZ, data is encoded in two |
|
||||
@ -361,12 +361,12 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | possible output length. |
|
||||
| | | |
|
||||
| | | Examples (some have other possible encodings of minimal length): |
|
||||
| | | abracadabra -> 7abracad47 |
|
||||
| | | mississippi -> 4miss433ppi |
|
||||
| | | aAAaAAaAaAA -> 3aAA53035 |
|
||||
| | | 2718281828 -> 627182844 |
|
||||
| | | abcdefghijk -> 9abcdefghi02jk |
|
||||
| | | aaaaaaaaaaa -> 1a911a |
|
||||
| | | aaaaaaaaaaaa -> 1a912aa |
|
||||
| | | aaaaaaaaaaaaa -> 1a91031 |
|
||||
| | | abracadabra -> 7abracad47 |
|
||||
| | | mississippi -> 4miss433ppi |
|
||||
| | | aAAaAAaAaAA -> 3aAA53035 |
|
||||
| | | 2718281828 -> 627182844 |
|
||||
| | | abcdefghijk -> 9abcdefghi02jk |
|
||||
| | | aaaaaaaaaaaa -> 3aaa91 |
|
||||
| | | aaaaaaaaaaaaa -> 1a91031 |
|
||||
| | | aaaaaaaaaaaaaa -> 1a91041 |
|
||||
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
||||
|
@ -1 +0,0 @@
|
||||
I want the wiki here https://bitburner.fandom.com/wiki/Bitburner_Wiki taken down please.
|
1556
package-lock.json
generated
1556
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"version": "1.6.4",
|
||||
"version": "1.7.0",
|
||||
"main": "electron-main.js",
|
||||
"author": {
|
||||
"name": "Daniel Xie & Olivier Gagnon"
|
||||
@ -57,6 +57,7 @@
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/escodegen": "^0.0.7",
|
||||
"@types/file-saver": "^2.0.3",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/numeral": "^2.0.2",
|
||||
|
7
src/@types/global.d.ts
vendored
7
src/@types/global.d.ts
vendored
@ -1,8 +1,13 @@
|
||||
// Defined by webpack on startup or compilation
|
||||
declare let __COMMIT_HASH__: string;
|
||||
declare const __COMMIT_HASH__: string;
|
||||
|
||||
// When using file-loader, we'll get a path to the resource
|
||||
declare module "*.png" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
// Achievements communicated back to Electron shell for Steam.
|
||||
declare interface Document {
|
||||
achievements: string[];
|
||||
}
|
||||
|
@ -334,7 +334,7 @@
|
||||
},
|
||||
"BLADEBURNER_UNSPENT_100000": {
|
||||
"ID": "BLADEBURNER_UNSPENT_100000",
|
||||
"Name": "You should really spent those.",
|
||||
"Name": "You should really spend those.",
|
||||
"Description": "Have 100 000 unspent bladeburner skill points."
|
||||
},
|
||||
"4S": {
|
||||
|
@ -24,6 +24,7 @@ import { IMap } from "../types";
|
||||
import * as data from "./AchievementData.json";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
|
||||
import { ClassType } from "../utils/WorkType";
|
||||
|
||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||
@ -391,12 +392,9 @@ export const achievements: IMap<Achievement> = {
|
||||
...achievementData["WORKOUT"],
|
||||
Icon: "WORKOUT",
|
||||
Condition: () =>
|
||||
[
|
||||
CONSTANTS.ClassGymStrength,
|
||||
CONSTANTS.ClassGymDefense,
|
||||
CONSTANTS.ClassGymDexterity,
|
||||
CONSTANTS.ClassGymAgility,
|
||||
].includes(Player.className),
|
||||
[ClassType.GymStrength, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymAgility].includes(
|
||||
Player.className,
|
||||
),
|
||||
},
|
||||
TOR: {
|
||||
...achievementData["TOR"],
|
||||
@ -799,5 +797,5 @@ export function calculateAchievements(): void {
|
||||
// Write all player's achievements to document for Steam/Electron
|
||||
// This could be replaced by "availableAchievements"
|
||||
// if we don't want to grant the save game achievements to steam but only currently available
|
||||
(document as any).achievements = [...Player.achievements.map((a) => a.ID)];
|
||||
document.achievements = [...Player.achievements.map((a) => a.ID)];
|
||||
}
|
||||
|
@ -9,6 +9,18 @@ import { Money } from "../ui/React/Money";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { AugmentationNames } from "./data/AugmentationNames";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { StaticAugmentations } from "./StaticAugmentations";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { getBaseAugmentationPriceMultiplier, getGenericAugmentationPriceMultiplier } from "./AugmentationHelpers";
|
||||
import { initSoAAugmentations } from "./data/AugmentationCreator";
|
||||
|
||||
export interface AugmentationCosts {
|
||||
moneyCost: number;
|
||||
repCost: number;
|
||||
}
|
||||
|
||||
export interface IConstructorParams {
|
||||
info: string | JSX.Element;
|
||||
@ -410,10 +422,10 @@ function generateStatsDescription(mults: IMap<number>, programs?: string[], star
|
||||
}
|
||||
|
||||
export class Augmentation {
|
||||
// How much money this costs to buy
|
||||
// How much money this costs to buy before multipliers
|
||||
baseCost = 0;
|
||||
|
||||
// How much faction reputation is required to unlock this
|
||||
// How much faction reputation is required to unlock this before multipliers
|
||||
baseRepRequirement = 0;
|
||||
|
||||
// Description of what this Aug is and what it does
|
||||
@ -425,9 +437,6 @@ export class Augmentation {
|
||||
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
|
||||
isSpecial = false;
|
||||
|
||||
// Augmentation level - for repeatable Augs like NeuroFlux Governor
|
||||
level = 0;
|
||||
|
||||
// Name of Augmentation
|
||||
name = "";
|
||||
|
||||
@ -438,12 +447,6 @@ export class Augmentation {
|
||||
// The Player/Person classes
|
||||
mults: IMap<number> = {};
|
||||
|
||||
// Initial cost. Doesn't change when you purchase multiple Augmentation
|
||||
startingCost = 0;
|
||||
|
||||
// Initial rep requirement. Doesn't change when you purchase multiple Augmentation
|
||||
startingRepRequirement = 0;
|
||||
|
||||
// Factions that offer this aug.
|
||||
factions: string[] = [];
|
||||
|
||||
@ -461,17 +464,15 @@ export class Augmentation {
|
||||
this.prereqs = params.prereqs ? params.prereqs : [];
|
||||
|
||||
this.baseRepRequirement = params.repCost;
|
||||
Object.freeze(this.baseRepRequirement);
|
||||
this.baseCost = params.moneyCost;
|
||||
this.startingCost = this.baseCost;
|
||||
this.startingRepRequirement = this.baseRepRequirement;
|
||||
Object.freeze(this.baseCost);
|
||||
this.factions = params.factions;
|
||||
|
||||
if (params.isSpecial) {
|
||||
this.isSpecial = true;
|
||||
}
|
||||
|
||||
this.level = 0;
|
||||
|
||||
// Set multipliers
|
||||
if (params.hacking_mult) {
|
||||
this.mults.hacking_mult = params.hacking_mult;
|
||||
@ -600,6 +601,62 @@ export class Augmentation {
|
||||
}
|
||||
}
|
||||
|
||||
getCost(player: IPlayer): AugmentationCosts {
|
||||
const augmentationReference = StaticAugmentations[this.name];
|
||||
let moneyCost = augmentationReference.baseCost;
|
||||
let repCost = augmentationReference.baseRepRequirement;
|
||||
|
||||
if (augmentationReference.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
let nextLevel = this.getLevel(player);
|
||||
--nextLevel;
|
||||
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
|
||||
repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||
moneyCost = augmentationReference.baseCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
|
||||
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
|
||||
moneyCost *= getBaseAugmentationPriceMultiplier();
|
||||
}
|
||||
} else if (augmentationReference.factions.includes(FactionNames.ShadowsOfAnarchy)) {
|
||||
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
|
||||
const soaMultiplier = Math.pow(
|
||||
CONSTANTS.SoACostMult,
|
||||
soaAugmentationNames.filter((augmentationName) => player.hasAugmentation(augmentationName)).length,
|
||||
);
|
||||
moneyCost = augmentationReference.baseCost * soaMultiplier;
|
||||
if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) {
|
||||
repCost = augmentationReference.baseRepRequirement * soaMultiplier;
|
||||
}
|
||||
} else {
|
||||
moneyCost =
|
||||
augmentationReference.baseCost *
|
||||
getGenericAugmentationPriceMultiplier() *
|
||||
BitNodeMultipliers.AugmentationMoneyCost;
|
||||
repCost = augmentationReference.baseRepRequirement * BitNodeMultipliers.AugmentationRepCost;
|
||||
}
|
||||
return { moneyCost, repCost };
|
||||
}
|
||||
|
||||
getLevel(player: IPlayer): number {
|
||||
// Get current Neuroflux level based on Player's augmentations
|
||||
if (this.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
let currLevel = 0;
|
||||
for (let i = 0; i < player.augmentations.length; ++i) {
|
||||
if (player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||
currLevel = player.augmentations[i].level;
|
||||
}
|
||||
}
|
||||
|
||||
// Account for purchased but uninstalled Augmentations
|
||||
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
|
||||
if (player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||
++currLevel;
|
||||
}
|
||||
}
|
||||
return currLevel + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Adds this Augmentation to all Factions
|
||||
addToAllFactions(): void {
|
||||
for (const fac of Object.keys(Factions)) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Augmentation } from "./Augmentation";
|
||||
import { Augmentations } from "./Augmentations";
|
||||
import { StaticAugmentations } from "./StaticAugmentations";
|
||||
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "./data/AugmentationNames";
|
||||
|
||||
@ -20,30 +20,11 @@ import {
|
||||
initNeuroFluxGovernor,
|
||||
initUnstableCircadianModulator,
|
||||
} from "./data/AugmentationCreator";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Router } from "../ui/GameRoot";
|
||||
|
||||
export function AddToAugmentations(aug: Augmentation): void {
|
||||
export function AddToStaticAugmentations(aug: Augmentation): void {
|
||||
const name = aug.name;
|
||||
Augmentations[name] = aug;
|
||||
}
|
||||
|
||||
export function getNextNeuroFluxLevel(): number {
|
||||
// Get current Neuroflux level based on Player's augmentations
|
||||
let currLevel = 0;
|
||||
for (let i = 0; i < Player.augmentations.length; ++i) {
|
||||
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||
currLevel = Player.augmentations[i].level;
|
||||
}
|
||||
}
|
||||
|
||||
// Account for purchased but uninstalled Augmentations
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||
++currLevel;
|
||||
}
|
||||
}
|
||||
return currLevel + 1;
|
||||
StaticAugmentations[name] = aug;
|
||||
}
|
||||
|
||||
function createAugmentations(): void {
|
||||
@ -67,105 +48,54 @@ function resetFactionAugmentations(): void {
|
||||
|
||||
function initAugmentations(): void {
|
||||
resetFactionAugmentations();
|
||||
clearObject(Augmentations);
|
||||
clearObject(StaticAugmentations);
|
||||
createAugmentations();
|
||||
updateAugmentationCosts();
|
||||
Player.reapplyAllAugmentations();
|
||||
}
|
||||
|
||||
function getBaseAugmentationPriceMultiplier(): number {
|
||||
export function getBaseAugmentationPriceMultiplier(): number {
|
||||
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
|
||||
}
|
||||
export function getGenericAugmentationPriceMultiplier(): number {
|
||||
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);
|
||||
}
|
||||
|
||||
function updateNeuroFluxGovernorCosts(neuroFluxGovernorAugmentation: Augmentation): void {
|
||||
let nextLevel = getNextNeuroFluxLevel();
|
||||
--nextLevel;
|
||||
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
|
||||
neuroFluxGovernorAugmentation.baseRepRequirement =
|
||||
neuroFluxGovernorAugmentation.startingRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||
neuroFluxGovernorAugmentation.baseCost =
|
||||
neuroFluxGovernorAugmentation.startingCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
neuroFluxGovernorAugmentation.baseCost *= getBaseAugmentationPriceMultiplier();
|
||||
}
|
||||
}
|
||||
|
||||
function updateSoACosts(soaAugmentation: Augmentation): void {
|
||||
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
|
||||
const soaAugCount = soaAugmentationNames.filter((augmentationName) =>
|
||||
Player.hasAugmentation(augmentationName),
|
||||
).length;
|
||||
soaAugmentation.baseCost = soaAugmentation.startingCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount);
|
||||
if (soaAugmentationNames.find((augmentationName) => augmentationName === soaAugmentation.name)) {
|
||||
soaAugmentation.baseRepRequirement =
|
||||
soaAugmentation.startingRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateAugmentationCosts(): void {
|
||||
for (const name of Object.keys(Augmentations)) {
|
||||
if (Augmentations.hasOwnProperty(name)) {
|
||||
const augmentationToUpdate = Augmentations[name];
|
||||
if (augmentationToUpdate.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
updateNeuroFluxGovernorCosts(augmentationToUpdate);
|
||||
} else if (augmentationToUpdate.factions.includes(FactionNames.ShadowsOfAnarchy)) {
|
||||
updateSoACosts(augmentationToUpdate);
|
||||
} else {
|
||||
augmentationToUpdate.baseCost =
|
||||
augmentationToUpdate.startingCost *
|
||||
getGenericAugmentationPriceMultiplier() *
|
||||
BitNodeMultipliers.AugmentationMoneyCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Resets an Augmentation during (re-initizliation)
|
||||
function resetAugmentation(aug: Augmentation): void {
|
||||
aug.addToFactions(aug.factions);
|
||||
const name = aug.name;
|
||||
if (augmentationExists(name)) {
|
||||
delete Augmentations[name];
|
||||
delete StaticAugmentations[name];
|
||||
}
|
||||
AddToAugmentations(aug);
|
||||
AddToStaticAugmentations(aug);
|
||||
}
|
||||
|
||||
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
|
||||
const augObj = Augmentations[aug.name];
|
||||
const staticAugmentation = StaticAugmentations[aug.name];
|
||||
|
||||
// Apply multipliers
|
||||
for (const mult of Object.keys(augObj.mults)) {
|
||||
const v = Player.getMult(mult) * augObj.mults[mult];
|
||||
for (const mult of Object.keys(staticAugmentation.mults)) {
|
||||
const v = Player.getMult(mult) * staticAugmentation.mults[mult];
|
||||
Player.setMult(mult, v);
|
||||
}
|
||||
|
||||
// Special logic for NeuroFlux Governor
|
||||
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
if (!reapply) {
|
||||
Augmentations[aug.name].level = aug.level;
|
||||
for (let i = 0; i < Player.augmentations.length; ++i) {
|
||||
if (Player.augmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||
Player.augmentations[i].level = aug.level;
|
||||
return;
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special logic for Congruity Implant
|
||||
if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
|
||||
Player.entropy = 0;
|
||||
Player.applyEntropy(Player.entropy);
|
||||
}
|
||||
|
||||
// Special logic for NeuroFlux Governor
|
||||
const ownedNfg = Player.augmentations.find((pAug) => pAug.name === AugmentationNames.NeuroFluxGovernor);
|
||||
if (aug.name === AugmentationNames.NeuroFluxGovernor && !reapply && ownedNfg) {
|
||||
ownedNfg.level = aug.level;
|
||||
return;
|
||||
}
|
||||
|
||||
// Push onto Player's Augmentation list
|
||||
if (!reapply) {
|
||||
const ownedAug = new PlayerOwnedAugmentation(aug.name);
|
||||
|
||||
Player.augmentations.push(ownedAug);
|
||||
}
|
||||
}
|
||||
@ -185,7 +115,7 @@ function installAugmentations(force?: boolean): boolean {
|
||||
}
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
const ownedAug = Player.queuedAugmentations[i];
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
const aug = StaticAugmentations[ownedAug.name];
|
||||
if (aug == null) {
|
||||
console.error(`Invalid augmentation: ${ownedAug.name}`);
|
||||
continue;
|
||||
@ -215,7 +145,7 @@ function installAugmentations(force?: boolean): boolean {
|
||||
}
|
||||
|
||||
function augmentationExists(name: string): boolean {
|
||||
return Augmentations.hasOwnProperty(name);
|
||||
return StaticAugmentations.hasOwnProperty(name);
|
||||
}
|
||||
|
||||
export function isRepeatableAug(aug: Augmentation): boolean {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Augmentation } from "./Augmentation";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const Augmentations: IMap<Augmentation> = {};
|
||||
export const StaticAugmentations: IMap<Augmentation> = {};
|
@ -109,6 +109,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
rewards, reduced damage taken, etc.
|
||||
</>
|
||||
),
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -121,6 +122,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
stats: (
|
||||
<>This augmentation makes the Slash minigame easier by showing you via an indictor when the slash in coming.</>
|
||||
),
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -129,6 +131,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1e6,
|
||||
info: "A connective brain implant to SASHA that focuses in pattern recognition and predictive templating.",
|
||||
stats: <>This augmentation makes the Bracket minigame easier by removing all '[' ']'.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -137,6 +140,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1e6,
|
||||
info: "Opto-occipito implant to process visual signal before brain interpretation.",
|
||||
stats: <>This augmentation makes the Backwards minigame easier by flipping the words.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -147,6 +151,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
"Pheromone extruder injected in the thoracodorsal nerve. Emits pleasing scent guaranteed to " +
|
||||
"make conversational partners more agreeable.",
|
||||
stats: <>This augmentation makes the Bribe minigame easier by indicating the incorrect paths.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -155,6 +160,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1e6,
|
||||
info: "Penta-dynamo-neurovascular-valve inserted in the carpal ligament, enhances dexterity.",
|
||||
stats: <>This augmentation makes the Cheat Code minigame easier by allowing the opposite character.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -163,6 +169,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1e6,
|
||||
info: "Transtinatium VVD reticulator used in optico-sterbing recognition.",
|
||||
stats: <>This augmentation makes the Symbol matching minigame easier by indicating the correct choice.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -176,6 +183,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
position.
|
||||
</>
|
||||
),
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -184,6 +192,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1e6,
|
||||
info: "Neodynic retention fjengeln spoofer using -φ karmions, net positive effect on implantees delta wave.",
|
||||
stats: <>This augmentation makes the Wire Cutting minigame easier by indicating the incorrect wires.</>,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.ShadowsOfAnarchy],
|
||||
}),
|
||||
];
|
||||
@ -242,7 +251,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 1.15e8,
|
||||
repCost: 2.75e4,
|
||||
info: "The latest version of the 'Augmented Targeting' implant adds the ability to lock-on and track threats.",
|
||||
prereqs: [AugmentationNames.Targeting2],
|
||||
prereqs: [AugmentationNames.Targeting2, AugmentationNames.Targeting1],
|
||||
dexterity_mult: 1.3,
|
||||
factions: [
|
||||
FactionNames.TheDarkArmy,
|
||||
@ -339,7 +348,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
info:
|
||||
"The latest version of the 'Combat Rib' augmentation releases advanced anabolic steroids that " +
|
||||
"improve muscle mass and physical performance while being safe and free of side effects.",
|
||||
prereqs: [AugmentationNames.CombatRib2],
|
||||
prereqs: [AugmentationNames.CombatRib2, AugmentationNames.CombatRib1],
|
||||
strength_mult: 1.18,
|
||||
defense_mult: 1.18,
|
||||
factions: [
|
||||
@ -673,7 +682,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"This upgraded firmware allows the Embedded Netburner Module to control information on " +
|
||||
"a network by re-routing traffic, spoofing IP addresses, and altering the data inside network " +
|
||||
"packets.",
|
||||
prereqs: [AugmentationNames.ENMCore],
|
||||
prereqs: [AugmentationNames.ENMCore, AugmentationNames.ENM],
|
||||
hacking_speed_mult: 1.05,
|
||||
hacking_money_mult: 1.3,
|
||||
hacking_chance_mult: 1.05,
|
||||
@ -698,7 +707,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"The Core V3 library is an implant that upgrades the firmware of the Embedded Netburner Module. " +
|
||||
"This upgraded firmware allows the Embedded Netburner Module to seamlessly inject code into " +
|
||||
"any device on a network.",
|
||||
prereqs: [AugmentationNames.ENMCoreV2],
|
||||
prereqs: [AugmentationNames.ENMCoreV2, AugmentationNames.ENMCore, AugmentationNames.ENM],
|
||||
hacking_speed_mult: 1.05,
|
||||
hacking_money_mult: 1.4,
|
||||
hacking_chance_mult: 1.1,
|
||||
@ -826,7 +835,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG2],
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG2, AugmentationNames.CranialSignalProcessorsG1],
|
||||
hacking_speed_mult: 1.02,
|
||||
hacking_money_mult: 1.15,
|
||||
hacking_mult: 1.09,
|
||||
@ -841,7 +850,11 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG3],
|
||||
prereqs: [
|
||||
AugmentationNames.CranialSignalProcessorsG3,
|
||||
AugmentationNames.CranialSignalProcessorsG2,
|
||||
AugmentationNames.CranialSignalProcessorsG1,
|
||||
],
|
||||
hacking_speed_mult: 1.02,
|
||||
hacking_money_mult: 1.2,
|
||||
hacking_grow_mult: 1.25,
|
||||
@ -856,7 +869,12 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
"are a set of specialized microprocessors that are attached to " +
|
||||
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
|
||||
"so that the brain doesn't have to.",
|
||||
prereqs: [AugmentationNames.CranialSignalProcessorsG4],
|
||||
prereqs: [
|
||||
AugmentationNames.CranialSignalProcessorsG4,
|
||||
AugmentationNames.CranialSignalProcessorsG3,
|
||||
AugmentationNames.CranialSignalProcessorsG2,
|
||||
AugmentationNames.CranialSignalProcessorsG1,
|
||||
],
|
||||
hacking_mult: 1.3,
|
||||
hacking_money_mult: 1.25,
|
||||
hacking_grow_mult: 1.75,
|
||||
@ -1254,6 +1272,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
moneyCost: 0,
|
||||
info: "It's time to leave the cave.",
|
||||
stats: null,
|
||||
isSpecial: true,
|
||||
factions: [FactionNames.Daedalus],
|
||||
}),
|
||||
new Augmentation({
|
||||
@ -1952,7 +1971,7 @@ export const initChurchOfTheMachineGodAugmentations = (): Augmentation[] => [
|
||||
"You will become greater than the sum of our parts. As One. Embrace your gift " +
|
||||
"fully and wholly free of it's accursed toll. Serenity brings tranquility the form " +
|
||||
"of no longer suffering a stat penalty. ",
|
||||
prereqs: [AugmentationNames.StaneksGift2],
|
||||
prereqs: [AugmentationNames.StaneksGift2, AugmentationNames.StaneksGift1],
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 1 / 0.95,
|
||||
hacking_speed_mult: 1 / 0.95,
|
||||
@ -2003,6 +2022,7 @@ export function initNeuroFluxGovernor(): Augmentation {
|
||||
multiplicatively.
|
||||
</>
|
||||
),
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 1.01 + donationBonus,
|
||||
hacking_speed_mult: 1.01 + donationBonus,
|
||||
hacking_money_mult: 1.01 + donationBonus,
|
||||
|
@ -114,17 +114,6 @@ export enum AugmentationNames {
|
||||
StaneksGift2 = "Stanek's Gift - Awakening",
|
||||
StaneksGift3 = "Stanek's Gift - Serenity",
|
||||
|
||||
/*
|
||||
MightOfAres = "Might of Ares", // slash
|
||||
WisdomOfAthena = "Wisdom of Athena", // bracket
|
||||
TrickeryOfHermes = "Trickery of Hermes", // cheatcode
|
||||
BeautyOfAphrodite = "Beauty of Aphrodite", // bribe
|
||||
ChaosOfDionysus = "Chaos of Dionysus", // reverse
|
||||
FloodOfPoseidon = "Flood of Poseidon", // hex
|
||||
HuntOfArtemis = "Hunt of Artemis", // mine
|
||||
KnowledgeOfApollo = "Knowledge of Apollo", // wire
|
||||
*/
|
||||
|
||||
// Infiltrators MiniGames
|
||||
MightOfAres = "SoA - Might of Ares", // slash
|
||||
WisdomOfAthena = "SoA - Wisdom of Athena", // bracket
|
||||
@ -135,10 +124,4 @@ export enum AugmentationNames {
|
||||
HuntOfArtemis = "SoA - Hunt of Artemis", // mine
|
||||
KnowledgeOfApollo = "SoA - Knowledge of Apollo", // wire
|
||||
WKSharmonizer = "SoA - phyzical WKS harmonizer",
|
||||
|
||||
//Wasteland Augs
|
||||
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
||||
//PepBoyForceField Generates plasma force fields
|
||||
//PepBoyBlasts Generate high density plasma concussive blasts
|
||||
//PepBoyDataStorage STore more data on pep boy,
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { StaticAugmentations } from "../StaticAugmentations";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { Info } from "@mui/icons-material";
|
||||
@ -39,7 +39,9 @@ const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
|
||||
<Typography variant="h5" color={Settings.theme.info}>
|
||||
NeuroFlux Governor - Level {level}
|
||||
</Typography>
|
||||
<Typography color={Settings.theme.info}>{Augmentations[AugmentationNames.NeuroFluxGovernor].stats}</Typography>
|
||||
<Typography color={Settings.theme.info}>
|
||||
{StaticAugmentations[AugmentationNames.NeuroFluxGovernor].stats}
|
||||
</Typography>
|
||||
</Paper>
|
||||
) : (
|
||||
<></>
|
||||
|
@ -13,7 +13,7 @@ import React, { useState } from "react";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { StaticAugmentations } from "../StaticAugmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
|
||||
export function InstalledAugmentations(): React.ReactElement {
|
||||
@ -77,7 +77,7 @@ export function InstalledAugmentations(): React.ReactElement {
|
||||
</Typography>
|
||||
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||
{(() => {
|
||||
const aug = Augmentations[selectedAug.name];
|
||||
const aug = StaticAugmentations[selectedAug.name];
|
||||
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
|
@ -8,7 +8,7 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { StaticAugmentations } from "../StaticAugmentations";
|
||||
|
||||
interface IAugmentedStats {
|
||||
[index: string]: number;
|
||||
@ -17,7 +17,7 @@ interface IAugmentedStats {
|
||||
function calculateAugmentedStats(): IAugmentedStats {
|
||||
const augP: IAugmentedStats = {};
|
||||
for (const aug of Player.queuedAugmentations) {
|
||||
const augObj = Augmentations[aug.name];
|
||||
const augObj = StaticAugmentations[aug.name];
|
||||
for (const mult of Object.keys(augObj.mults)) {
|
||||
const v = augP[mult] ? augP[mult] : 1;
|
||||
augP[mult] = v * augObj.mults[mult];
|
||||
@ -45,13 +45,13 @@ function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactEle
|
||||
);
|
||||
}
|
||||
|
||||
type MultiplierListItemData = [
|
||||
multiplier: string,
|
||||
currentValue: number,
|
||||
augmentedValue: number,
|
||||
bitNodeMultiplier: number,
|
||||
color: string,
|
||||
];
|
||||
interface MultiplierListItemData {
|
||||
mult: string;
|
||||
current: number;
|
||||
augmented: number;
|
||||
bnMult?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
interface IMultiplierListProps {
|
||||
rows: MultiplierListItemData[];
|
||||
@ -60,23 +60,23 @@ interface IMultiplierListProps {
|
||||
function MultiplierList(props: IMultiplierListProps): React.ReactElement {
|
||||
const listItems = props.rows
|
||||
.map((data) => {
|
||||
const [multiplier, currentValue, augmentedValue, bitNodeMultiplier, color] = data;
|
||||
const { mult, current, augmented, bnMult = 1, color = Settings.theme.primary } = data;
|
||||
|
||||
if (!isNaN(augmentedValue)) {
|
||||
if (!isNaN(augmented)) {
|
||||
return (
|
||||
<ListItem key={multiplier} disableGutters sx={{ py: 0 }}>
|
||||
<ListItem key={mult} disableGutters sx={{ py: 0 }}>
|
||||
<ListItemText
|
||||
sx={{ my: 0.1 }}
|
||||
primary={
|
||||
<Typography color={color}>
|
||||
<b>{multiplier}</b>
|
||||
<b>{mult}</b>
|
||||
</Typography>
|
||||
}
|
||||
secondary={
|
||||
<span style={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
<BitNodeModifiedStats base={currentValue} mult={bitNodeMultiplier} color={color} />
|
||||
<BitNodeModifiedStats base={current} mult={bnMult} color={color} />
|
||||
<DoubleArrow fontSize="small" color="success" sx={{ mb: 0.5, mx: 1 }} />
|
||||
<BitNodeModifiedStats base={augmentedValue} mult={bitNodeMultiplier} color={Settings.theme.success} />
|
||||
<BitNodeModifiedStats base={augmented} mult={bnMult} color={Settings.theme.success} />
|
||||
</span>
|
||||
}
|
||||
disableTypography
|
||||
@ -94,177 +94,205 @@ function MultiplierList(props: IMultiplierListProps): React.ReactElement {
|
||||
export function PlayerMultipliers(): React.ReactElement {
|
||||
const mults = calculateAugmentedStats();
|
||||
|
||||
// Column data is a bit janky, so it's set up here to allow for
|
||||
// easier logic in setting up the layout
|
||||
const leftColData: MultiplierListItemData[] = [
|
||||
...[
|
||||
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1],
|
||||
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1],
|
||||
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1],
|
||||
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1],
|
||||
[
|
||||
"Hacking Level ",
|
||||
Player.hacking_mult,
|
||||
Player.hacking_mult * mults.hacking_mult,
|
||||
BitNodeMultipliers.HackingLevelMultiplier,
|
||||
],
|
||||
[
|
||||
"Hacking Experience ",
|
||||
Player.hacking_exp_mult,
|
||||
Player.hacking_exp_mult * mults.hacking_exp_mult,
|
||||
BitNodeMultipliers.HackExpGain,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.hack])),
|
||||
{
|
||||
mult: "Hacking Chance",
|
||||
current: Player.hacking_chance_mult,
|
||||
augmented: Player.hacking_chance_mult * mults.hacking_chance_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacking Speed",
|
||||
current: Player.hacking_speed_mult,
|
||||
augmented: Player.hacking_speed_mult * mults.hacking_speed_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacking Money",
|
||||
current: Player.hacking_money_mult,
|
||||
augmented: Player.hacking_money_mult * mults.hacking_money_mult,
|
||||
bnMult: BitNodeMultipliers.ScriptHackMoney,
|
||||
},
|
||||
{
|
||||
mult: "Hacking Growth",
|
||||
current: Player.hacking_grow_mult,
|
||||
augmented: Player.hacking_grow_mult * mults.hacking_grow_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacking Level",
|
||||
current: Player.hacking_mult,
|
||||
augmented: Player.hacking_mult * mults.hacking_mult,
|
||||
bnMult: BitNodeMultipliers.HackingLevelMultiplier,
|
||||
},
|
||||
{
|
||||
mult: "Hacking Experience",
|
||||
current: Player.hacking_exp_mult,
|
||||
augmented: Player.hacking_exp_mult * mults.hacking_exp_mult,
|
||||
bnMult: BitNodeMultipliers.HackExpGain,
|
||||
},
|
||||
].map((data: MultiplierListItemData) =>
|
||||
Object.defineProperty(data, "color", {
|
||||
value: Settings.theme.hack,
|
||||
}),
|
||||
),
|
||||
...[
|
||||
[
|
||||
"Strength Level ",
|
||||
Player.strength_mult,
|
||||
Player.strength_mult * mults.strength_mult,
|
||||
BitNodeMultipliers.StrengthLevelMultiplier,
|
||||
],
|
||||
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1],
|
||||
[
|
||||
"Defense Level ",
|
||||
Player.defense_mult,
|
||||
Player.defense_mult * mults.defense_mult,
|
||||
BitNodeMultipliers.DefenseLevelMultiplier,
|
||||
],
|
||||
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1],
|
||||
[
|
||||
"Dexterity Level ",
|
||||
Player.dexterity_mult,
|
||||
Player.dexterity_mult * mults.dexterity_mult,
|
||||
BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
],
|
||||
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult, 1],
|
||||
[
|
||||
"Agility Level ",
|
||||
Player.agility_mult,
|
||||
Player.agility_mult * mults.agility_mult,
|
||||
BitNodeMultipliers.AgilityLevelMultiplier,
|
||||
],
|
||||
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.combat])),
|
||||
[
|
||||
"Charisma Level ",
|
||||
Player.charisma_mult,
|
||||
Player.charisma_mult * mults.charisma_mult,
|
||||
BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
Settings.theme.cha,
|
||||
],
|
||||
[
|
||||
"Charisma Experience ",
|
||||
Player.charisma_exp_mult,
|
||||
Player.charisma_exp_mult * mults.charisma_exp_mult,
|
||||
1,
|
||||
Settings.theme.cha,
|
||||
],
|
||||
{
|
||||
mult: "Strength Level",
|
||||
current: Player.strength_mult,
|
||||
augmented: Player.strength_mult * mults.strength_mult,
|
||||
bnMult: BitNodeMultipliers.StrengthLevelMultiplier,
|
||||
},
|
||||
{
|
||||
mult: "Strength Experience",
|
||||
current: Player.strength_exp_mult,
|
||||
augmented: Player.strength_exp_mult * mults.strength_exp_mult,
|
||||
},
|
||||
{
|
||||
mult: "Defense Level",
|
||||
current: Player.defense_mult,
|
||||
augmented: Player.defense_mult * mults.defense_mult,
|
||||
bnMult: BitNodeMultipliers.DefenseLevelMultiplier,
|
||||
},
|
||||
{
|
||||
mult: "Defense Experience",
|
||||
current: Player.defense_exp_mult,
|
||||
augmented: Player.defense_exp_mult * mults.defense_exp_mult,
|
||||
},
|
||||
{
|
||||
mult: "Dexterity Level",
|
||||
current: Player.dexterity_mult,
|
||||
augmented: Player.dexterity_mult * mults.dexterity_mult,
|
||||
bnMult: BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
},
|
||||
{
|
||||
mult: "Dexterity Experience",
|
||||
current: Player.dexterity_exp_mult,
|
||||
augmented: Player.dexterity_exp_mult * mults.dexterity_exp_mult,
|
||||
},
|
||||
{
|
||||
mult: "Agility Level",
|
||||
current: Player.agility_mult,
|
||||
augmented: Player.agility_mult * mults.agility_mult,
|
||||
bnMult: BitNodeMultipliers.AgilityLevelMultiplier,
|
||||
},
|
||||
{
|
||||
mult: "Agility Experience",
|
||||
current: Player.agility_exp_mult,
|
||||
augmented: Player.agility_exp_mult * mults.agility_exp_mult,
|
||||
},
|
||||
].map((data: MultiplierListItemData) =>
|
||||
Object.defineProperty(data, "color", {
|
||||
value: Settings.theme.combat,
|
||||
}),
|
||||
),
|
||||
{
|
||||
mult: "Charisma Level",
|
||||
current: Player.charisma_mult,
|
||||
augmented: Player.charisma_mult * mults.charisma_mult,
|
||||
bnMult: BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
color: Settings.theme.cha,
|
||||
},
|
||||
{
|
||||
mult: "Charisma Experience",
|
||||
current: Player.charisma_exp_mult,
|
||||
augmented: Player.charisma_exp_mult * mults.charisma_exp_mult,
|
||||
color: Settings.theme.cha,
|
||||
},
|
||||
];
|
||||
const rightColData: MultiplierListItemData[] = [
|
||||
...[
|
||||
[
|
||||
"Hacknet Node production ",
|
||||
Player.hacknet_node_money_mult,
|
||||
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
||||
BitNodeMultipliers.HacknetNodeMoney,
|
||||
],
|
||||
[
|
||||
"Hacknet Node purchase cost ",
|
||||
Player.hacknet_node_purchase_cost_mult,
|
||||
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node RAM upgrade cost ",
|
||||
Player.hacknet_node_ram_cost_mult,
|
||||
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node Core purchase cost ",
|
||||
Player.hacknet_node_core_cost_mult,
|
||||
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node level upgrade cost ",
|
||||
Player.hacknet_node_level_cost_mult,
|
||||
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
||||
1,
|
||||
],
|
||||
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1],
|
||||
[
|
||||
"Faction reputation gain ",
|
||||
Player.faction_rep_mult,
|
||||
Player.faction_rep_mult * mults.faction_rep_mult,
|
||||
BitNodeMultipliers.FactionWorkRepGain,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||
[
|
||||
"Salary ",
|
||||
Player.work_money_mult,
|
||||
Player.work_money_mult * mults.work_money_mult,
|
||||
BitNodeMultipliers.CompanyWorkMoney,
|
||||
Settings.theme.money,
|
||||
],
|
||||
[
|
||||
"Crime success ",
|
||||
Player.crime_success_mult,
|
||||
Player.crime_success_mult * mults.crime_success_mult,
|
||||
1,
|
||||
Settings.theme.combat,
|
||||
],
|
||||
[
|
||||
"Crime money ",
|
||||
Player.crime_money_mult,
|
||||
Player.crime_money_mult * mults.crime_money_mult,
|
||||
BitNodeMultipliers.CrimeMoney,
|
||||
Settings.theme.money,
|
||||
],
|
||||
{
|
||||
mult: "Hacknet Node Production",
|
||||
current: Player.hacknet_node_money_mult,
|
||||
augmented: Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
||||
bnMult: BitNodeMultipliers.HacknetNodeMoney,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Purchase Cost",
|
||||
current: Player.hacknet_node_purchase_cost_mult,
|
||||
augmented: Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node RAM Upgrade Cost",
|
||||
current: Player.hacknet_node_ram_cost_mult,
|
||||
augmented: Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Core Purchase Cost",
|
||||
current: Player.hacknet_node_core_cost_mult,
|
||||
augmented: Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Level Upgrade Cost",
|
||||
current: Player.hacknet_node_level_cost_mult,
|
||||
augmented: Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
||||
},
|
||||
{
|
||||
mult: "Company Reputation Gain",
|
||||
current: Player.company_rep_mult,
|
||||
augmented: Player.company_rep_mult * mults.company_rep_mult,
|
||||
},
|
||||
{
|
||||
mult: "Faction Reputation Gain",
|
||||
current: Player.faction_rep_mult,
|
||||
augmented: Player.faction_rep_mult * mults.faction_rep_mult,
|
||||
bnMult: BitNodeMultipliers.FactionWorkRepGain,
|
||||
},
|
||||
{
|
||||
mult: "Salary",
|
||||
current: Player.work_money_mult,
|
||||
augmented: Player.work_money_mult * mults.work_money_mult,
|
||||
bnMult: BitNodeMultipliers.CompanyWorkMoney,
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
{
|
||||
mult: "Crime Success Chance",
|
||||
current: Player.crime_success_mult,
|
||||
augmented: Player.crime_success_mult * mults.crime_success_mult,
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
{
|
||||
mult: "Crime Money",
|
||||
current: Player.crime_money_mult,
|
||||
augmented: Player.crime_money_mult * mults.crime_money_mult,
|
||||
bnMult: BitNodeMultipliers.CrimeMoney,
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
];
|
||||
|
||||
if (Player.canAccessBladeburner()) {
|
||||
rightColData.push(
|
||||
...[
|
||||
[
|
||||
"Bladeburner Success Chance",
|
||||
Player.bladeburner_success_chance_mult,
|
||||
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Max Stamina",
|
||||
Player.bladeburner_max_stamina_mult,
|
||||
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Stamina Gain",
|
||||
Player.bladeburner_stamina_gain_mult,
|
||||
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Field Analysis",
|
||||
Player.bladeburner_analysis_mult,
|
||||
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
||||
1,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||
{
|
||||
mult: "Bladeburner Success Chance",
|
||||
current: Player.bladeburner_success_chance_mult,
|
||||
augmented: Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
||||
},
|
||||
{
|
||||
mult: "Bladeburner Max Stamina",
|
||||
current: Player.bladeburner_max_stamina_mult,
|
||||
augmented: Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
||||
},
|
||||
{
|
||||
mult: "Bladeburner Stamina Gain",
|
||||
current: Player.bladeburner_stamina_gain_mult,
|
||||
augmented: Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
||||
},
|
||||
{
|
||||
mult: "Bladeburner Field Analysis",
|
||||
current: Player.bladeburner_analysis_mult,
|
||||
augmented: Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const hasLeftImprovements = +!!(leftColData.filter((item) => item[2] !== 0).length > 0),
|
||||
hasRightImprovements = +!!(rightColData.filter((item) => item[2] !== 0).length > 0);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1,
|
||||
maxHeight: 400,
|
||||
overflowY: "scroll",
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${hasLeftImprovements + hasRightImprovements}, 1fr)`,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexWrap: "wrap",
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<MultiplierList rows={leftColData} />
|
||||
|
265
src/Augmentation/ui/PurchasableAugmentations.tsx
Normal file
265
src/Augmentation/ui/PurchasableAugmentations.tsx
Normal file
@ -0,0 +1,265 @@
|
||||
/**
|
||||
* React component for displaying a single augmentation for purchase through
|
||||
* the faction UI
|
||||
*/
|
||||
import { CheckBox, CheckBoxOutlineBlank, CheckCircle, Info, NewReleases, Report } from "@mui/icons-material";
|
||||
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Augmentation } from "../Augmentation";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
||||
import { StaticAugmentations } from "../StaticAugmentations";
|
||||
|
||||
interface IPreReqsProps {
|
||||
player: IPlayer;
|
||||
aug: Augmentation;
|
||||
}
|
||||
|
||||
const PreReqs = (props: IPreReqsProps): React.ReactElement => {
|
||||
const ownedPreReqs = props.aug.prereqs.filter((aug) => props.player.hasAugmentation(aug));
|
||||
const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation has the following pre-requisite(s):
|
||||
</Typography>
|
||||
{props.aug.prereqs.map((preAug) => (
|
||||
<Requirement
|
||||
fulfilled={props.player.hasAugmentation(preAug)}
|
||||
value={preAug}
|
||||
color={Settings.theme.money}
|
||||
key={preAug}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
color: hasPreReqs ? Settings.theme.successlight : Settings.theme.error,
|
||||
}}
|
||||
>
|
||||
{hasPreReqs ? (
|
||||
<>
|
||||
<CheckCircle fontSize="small" sx={{ mr: 1 }} />
|
||||
Pre-requisites Owned
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Report fontSize="small" sx={{ mr: 1 }} />
|
||||
Missing {props.aug.prereqs.length - ownedPreReqs.length} pre-requisite(s)
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IExclusiveProps {
|
||||
player: IPlayer;
|
||||
aug: Augmentation;
|
||||
}
|
||||
|
||||
const Exclusive = (props: IExclusiveProps): React.ReactElement => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation can only be acquired from the following source(s):
|
||||
</Typography>
|
||||
<ul>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
<li>
|
||||
<b>{props.aug.factions[0]}</b> faction
|
||||
</li>
|
||||
{props.player.canAccessGang() && !props.aug.isSpecial && (
|
||||
<li>
|
||||
Certain <b>gangs</b>
|
||||
</li>
|
||||
)}
|
||||
{props.player.canAccessGrafting() &&
|
||||
!props.aug.isSpecial &&
|
||||
props.aug.name !== AugmentationNames.TheRedPill && (
|
||||
<li>
|
||||
<b>Grafting</b>
|
||||
</li>
|
||||
)}
|
||||
</Typography>
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<NewReleases sx={{ ml: 1, color: Settings.theme.money, transform: "rotate(180deg)" }} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IReqProps {
|
||||
value: string;
|
||||
color: string;
|
||||
fulfilled: boolean;
|
||||
}
|
||||
|
||||
const Requirement = (props: IReqProps): React.ReactElement => {
|
||||
return (
|
||||
<Typography sx={{ display: "flex", alignItems: "center", color: props.color }}>
|
||||
{props.fulfilled ? <CheckBox sx={{ mr: 1 }} /> : <CheckBoxOutlineBlank sx={{ mr: 1 }} />}
|
||||
{props.value}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
interface IPurchasableAugsProps {
|
||||
augNames: string[];
|
||||
ownedAugNames: string[];
|
||||
player: IPlayer;
|
||||
|
||||
canPurchase: (player: IPlayer, aug: Augmentation) => boolean;
|
||||
purchaseAugmentation: (player: IPlayer, aug: Augmentation, showModal: (open: boolean) => void) => void;
|
||||
|
||||
rep?: number;
|
||||
sleeveAugs?: boolean;
|
||||
faction?: Faction;
|
||||
}
|
||||
|
||||
export const PurchasableAugmentations = (props: IPurchasableAugsProps): React.ReactElement => {
|
||||
return (
|
||||
<Container
|
||||
maxWidth="lg"
|
||||
disableGutters
|
||||
sx={{ mx: 0, display: "grid", gridTemplateColumns: "repeat(1, 1fr)", gap: 1 }}
|
||||
>
|
||||
{props.augNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={false} />
|
||||
))}
|
||||
{props.ownedAugNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={true} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
interface IPurchasableAugProps {
|
||||
parent: IPurchasableAugsProps;
|
||||
augName: string;
|
||||
owned: boolean;
|
||||
}
|
||||
|
||||
export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const aug = StaticAugmentations[props.augName];
|
||||
const augCosts = aug.getCost(props.parent.player);
|
||||
const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost;
|
||||
const repCost = augCosts.repCost;
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const description = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "minmax(0, 4fr) 1fr",
|
||||
gap: 1,
|
||||
opacity: props.owned ? 0.75 : 1,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
props.parent.purchaseAugmentation(props.parent.player, aug, (open): void => {
|
||||
setOpen(open);
|
||||
})
|
||||
}
|
||||
disabled={!props.parent.canPurchase(props.parent.player, aug) || props.owned}
|
||||
sx={{ width: "48px", height: "48px", float: "left", clear: "none", mr: 1 }}
|
||||
>
|
||||
{props.owned ? "Owned" : "Buy"}
|
||||
</Button>
|
||||
|
||||
<Box sx={{ maxWidth: props.owned ? "100%" : "85%" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography variant="h5">
|
||||
{props.augName}
|
||||
{props.augName === AugmentationNames.NeuroFluxGovernor &&
|
||||
` - Level ${aug.getLevel(props.parent.player)}`}
|
||||
</Typography>
|
||||
<Typography>{description}</Typography>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Info sx={{ mr: 1 }} color="info" />
|
||||
</Tooltip>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
color: props.owned ? Settings.theme.disabled : Settings.theme.primary,
|
||||
}}
|
||||
>
|
||||
{aug.name}
|
||||
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel(props.parent.player)}`}
|
||||
</Typography>
|
||||
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
|
||||
<Exclusive player={props.parent.player} aug={aug} />
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs player={props.parent.player} aug={aug} />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{props.owned || (
|
||||
<Box sx={{ display: "grid", alignItems: "center", justifyItems: "left" }}>
|
||||
<Requirement
|
||||
fulfilled={cost === 0 || props.parent.player.money > cost}
|
||||
value={numeralWrapper.formatMoney(cost)}
|
||||
color={Settings.theme.money}
|
||||
/>
|
||||
{props.parent.rep !== undefined && (
|
||||
<Requirement
|
||||
fulfilled={props.parent.rep >= repCost}
|
||||
value={`${numeralWrapper.formatReputation(repCost)} rep`}
|
||||
color={Settings.theme.rep}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{Settings.SuppressBuyAugmentationConfirmation || (
|
||||
<PurchaseAugmentationModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
faction={props.parent.faction}
|
||||
aug={aug}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</Paper>
|
||||
);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Faction } from "../Faction";
|
||||
import { purchaseAugmentation } from "../FactionHelpers";
|
||||
import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
|
||||
import { Augmentation } from "../Augmentation";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { purchaseAugmentation } from "../../Faction/FactionHelpers";
|
||||
import { isRepeatableAug } from "../AugmentationHelpers";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { use } from "../../ui/Context";
|
||||
@ -13,21 +13,23 @@ import Button from "@mui/material/Button";
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
faction: Faction;
|
||||
aug: Augmentation;
|
||||
rerender: () => void;
|
||||
faction?: Faction;
|
||||
aug?: Augmentation;
|
||||
}
|
||||
|
||||
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||
if (typeof props.aug === "undefined" || typeof props.faction === "undefined") {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const player = use.Player();
|
||||
|
||||
function buy(): void {
|
||||
if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
|
||||
if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
purchaseAugmentation(props.aug, props.faction);
|
||||
props.rerender();
|
||||
purchaseAugmentation(props.aug as Augmentation, props.faction as Faction);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
@ -42,7 +44,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
Would you like to purchase the {props.aug.name} Augmentation for
|
||||
<Money money={props.aug.baseCost} />?
|
||||
<Money money={props.aug.getCost(player).moneyCost} />?
|
||||
<br />
|
||||
<br />
|
||||
</Typography>
|
@ -5,7 +5,7 @@
|
||||
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import * as React from "react";
|
||||
import { Player } from "../../Player";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { StaticAugmentations } from "../StaticAugmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
|
||||
export function PurchasedAugmentations(): React.ReactElement {
|
||||
@ -23,7 +23,7 @@ export function PurchasedAugmentations(): React.ReactElement {
|
||||
let displayName = ownedAug.name;
|
||||
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
const aug = StaticAugmentations[ownedAug.name];
|
||||
|
||||
let level = null;
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
|
@ -488,7 +488,7 @@ export const defaultMultipliers: IBitNodeMultipliers = {
|
||||
FourSigmaMarketDataApiCost: 1,
|
||||
|
||||
CorporationValuation: 1,
|
||||
CorporationSoftCap: 1,
|
||||
CorporationSoftcap: 1,
|
||||
|
||||
BladeburnerRank: 1,
|
||||
BladeburnerSkillCost: 1,
|
||||
@ -504,6 +504,8 @@ export const defaultMultipliers: IBitNodeMultipliers = {
|
||||
WorldDaemonDifficulty: 1,
|
||||
};
|
||||
|
||||
Object.freeze(defaultMultipliers);
|
||||
|
||||
export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultipliers {
|
||||
const mults = Object.assign({}, defaultMultipliers);
|
||||
switch (n) {
|
||||
@ -523,7 +525,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: 2,
|
||||
StaneksGiftExtraSize: -6,
|
||||
PurchasedServerSoftcap: 1.3,
|
||||
CorporationSoftCap: 0.9,
|
||||
CorporationSoftcap: 0.9,
|
||||
WorldDaemonDifficulty: 5,
|
||||
});
|
||||
}
|
||||
@ -609,7 +611,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: 0.5,
|
||||
StaneksGiftExtraSize: 2,
|
||||
GangSoftcap: 0.7,
|
||||
CorporationSoftCap: 0.9,
|
||||
CorporationSoftcap: 0.9,
|
||||
WorldDaemonDifficulty: 2,
|
||||
GangUniqueAugs: 0.2,
|
||||
});
|
||||
@ -637,7 +639,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: 0.9,
|
||||
StaneksGiftExtraSize: -1,
|
||||
GangSoftcap: 0.7,
|
||||
CorporationSoftCap: 0.9,
|
||||
CorporationSoftcap: 0.9,
|
||||
WorldDaemonDifficulty: 2,
|
||||
GangUniqueAugs: 0.2,
|
||||
});
|
||||
@ -657,7 +659,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftExtraSize: -99,
|
||||
PurchasedServerSoftcap: 4,
|
||||
GangSoftcap: 0,
|
||||
CorporationSoftCap: 0,
|
||||
CorporationSoftcap: 0,
|
||||
GangUniqueAugs: 0,
|
||||
});
|
||||
}
|
||||
@ -685,7 +687,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: 0.5,
|
||||
StaneksGiftExtraSize: 2,
|
||||
GangSoftcap: 0.8,
|
||||
CorporationSoftCap: 0.7,
|
||||
CorporationSoftcap: 0.7,
|
||||
WorldDaemonDifficulty: 2,
|
||||
GangUniqueAugs: 0.25,
|
||||
});
|
||||
@ -717,7 +719,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftExtraSize: -3,
|
||||
PurchasedServerSoftcap: 1.1,
|
||||
GangSoftcap: 0.9,
|
||||
CorporationSoftCap: 0.9,
|
||||
CorporationSoftcap: 0.9,
|
||||
WorldDaemonDifficulty: 2,
|
||||
GangUniqueAugs: 0.25,
|
||||
});
|
||||
@ -741,7 +743,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
FourSigmaMarketDataCost: 4,
|
||||
FourSigmaMarketDataApiCost: 4,
|
||||
PurchasedServerSoftcap: 2,
|
||||
CorporationSoftCap: 0.9,
|
||||
CorporationSoftcap: 0.9,
|
||||
WorldDaemonDifficulty: 1.5,
|
||||
GangUniqueAugs: 0.75,
|
||||
});
|
||||
@ -809,7 +811,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: inc,
|
||||
StaneksGiftExtraSize: inc,
|
||||
GangSoftcap: 0.8,
|
||||
CorporationSoftCap: 0.8,
|
||||
CorporationSoftcap: 0.8,
|
||||
WorldDaemonDifficulty: inc,
|
||||
|
||||
GangUniqueAugs: dec,
|
||||
@ -854,7 +856,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
|
||||
StaneksGiftPowerMultiplier: 2,
|
||||
StaneksGiftExtraSize: 1,
|
||||
GangSoftcap: 0.3,
|
||||
CorporationSoftCap: 0.3,
|
||||
CorporationSoftcap: 0.3,
|
||||
WorldDaemonDifficulty: 3,
|
||||
GangUniqueAugs: 0.1,
|
||||
});
|
||||
|
@ -242,7 +242,7 @@ export interface IBitNodeMultipliers {
|
||||
/**
|
||||
* Influences corporation dividends.
|
||||
*/
|
||||
CorporationSoftCap: number;
|
||||
CorporationSoftcap: number;
|
||||
|
||||
// Index signature
|
||||
[key: string]: number;
|
||||
@ -252,4 +252,4 @@ export interface IBitNodeMultipliers {
|
||||
* The multipliers that are influenced by current Bitnode progression.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const BitNodeMultipliers = defaultMultipliers;
|
||||
export const BitNodeMultipliers = Object.assign({}, defaultMultipliers);
|
||||
|
@ -1,554 +1,335 @@
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
import { Box, Collapse, ListItemButton, ListItemText, Paper, Typography } from "@mui/material";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import { Box, Collapse, ListItemButton, ListItemText, Paper, Table, TableBody, Typography } from "@mui/material";
|
||||
import { uniqueId } from "lodash";
|
||||
import React from "react";
|
||||
import { SpecialServers } from "../../Server/data/SpecialServers";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../../ui/Context";
|
||||
import { StatsRow } from "../../ui/React/StatsRow";
|
||||
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
|
||||
import { IBitNodeMultipliers } from "../BitNodeMultipliers";
|
||||
import { SpecialServers } from "../../Server/data/SpecialServers";
|
||||
|
||||
interface IProps {
|
||||
n: number;
|
||||
level?: number;
|
||||
}
|
||||
|
||||
export function BitnodeMultiplierDescription({ n }: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactElement {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const mults = getBitNodeMultipliers(n, player.sourceFileLvl(n));
|
||||
if (n === 1) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Box component={Paper}>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText primary={<Typography>Bitnode multipliers:</Typography>} />
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Box mx={2}>
|
||||
<Collapse in={open}>
|
||||
<GeneralMults n={n} mults={mults} />
|
||||
<FactionMults n={n} mults={mults} />
|
||||
<AugmentationMults n={n} mults={mults} />
|
||||
<StockMults n={n} mults={mults} />
|
||||
<SkillMults n={n} mults={mults} />
|
||||
<HackingMults n={n} mults={mults} />
|
||||
<PurchasedServersMults n={n} mults={mults} />
|
||||
<CrimeMults n={n} mults={mults} />
|
||||
<InfiltrationMults n={n} mults={mults} />
|
||||
<CompanyMults n={n} mults={mults} />
|
||||
<GangMults n={n} mults={mults} />
|
||||
<CorporationMults n={n} mults={mults} />
|
||||
<BladeburnerMults n={n} mults={mults} />
|
||||
<StanekMults n={n} mults={mults} />
|
||||
<br />
|
||||
</Collapse>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
<Box component={Paper} sx={{ mt: 1, p: 1 }}>
|
||||
<ListItemButton disableGutters onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText primary={<Typography variant="h6">Bitnode Multipliers</Typography>} />
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Collapse in={open}>
|
||||
<BitNodeMultipliersDisplay n={n} level={level} />
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElement => {
|
||||
const player = use.Player();
|
||||
// If a level argument has been provided, use that as the multiplier level
|
||||
// If not, then we have to assume that we want the next level up from the
|
||||
// current node's source file, so we get the min of that, the SF's max level,
|
||||
// or if it's BN12, ∞
|
||||
const maxSfLevel = n === 12 ? Infinity : 3;
|
||||
const mults = getBitNodeMultipliers(n, level ?? Math.min(player.sourceFileLvl(n) + 1, maxSfLevel));
|
||||
|
||||
return (
|
||||
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
|
||||
<GeneralMults n={n} mults={mults} />
|
||||
<SkillMults n={n} mults={mults} />
|
||||
<FactionMults n={n} mults={mults} />
|
||||
<AugmentationMults n={n} mults={mults} />
|
||||
<HackingMults n={n} mults={mults} />
|
||||
<PurchasedServersMults n={n} mults={mults} />
|
||||
<StockMults n={n} mults={mults} />
|
||||
<CrimeMults n={n} mults={mults} />
|
||||
<InfiltrationMults n={n} mults={mults} />
|
||||
<CompanyMults n={n} mults={mults} />
|
||||
<GangMults n={n} mults={mults} />
|
||||
<CorporationMults n={n} mults={mults} />
|
||||
<BladeburnerMults n={n} mults={mults} />
|
||||
<StanekMults n={n} mults={mults} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
interface IBNMultRows {
|
||||
[mult: string]: {
|
||||
name: string;
|
||||
content?: string;
|
||||
color?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface IBNMultTableProps {
|
||||
sectionName: string;
|
||||
rowData: IBNMultRows;
|
||||
mults: IBitNodeMultipliers;
|
||||
}
|
||||
|
||||
const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
|
||||
const rowsArray = Object.entries(props.rowData)
|
||||
.filter(([key, _value]) => props.mults[key] !== defaultMultipliers[key])
|
||||
.map(([key, value]) => (
|
||||
<StatsRow
|
||||
key={uniqueId()}
|
||||
name={value.name}
|
||||
data={{ content: value.content ?? `${(props.mults[key] * 100).toFixed(3)}%` }}
|
||||
color={value.color ?? Settings.theme.primary}
|
||||
/>
|
||||
));
|
||||
|
||||
return rowsArray.length > 0 ? (
|
||||
<span style={{ display: "inline-block", width: "100%", marginBottom: "16px" }}>
|
||||
<Typography variant="h6">{props.sectionName}</Typography>
|
||||
<Table>
|
||||
<TableBody>{rowsArray}</TableBody>
|
||||
</Table>
|
||||
</span>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
interface IMultsProps {
|
||||
n: number;
|
||||
mults: IBitNodeMultipliers;
|
||||
}
|
||||
|
||||
function GeneralMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.ClassGymExpGain === defaultMultipliers.ClassGymExpGain &&
|
||||
mults.CodingContractMoney === defaultMultipliers.CodingContractMoney &&
|
||||
mults.DaedalusAugsRequirement === defaultMultipliers.DaedalusAugsRequirement &&
|
||||
mults.WorldDaemonDifficulty === defaultMultipliers.WorldDaemonDifficulty &&
|
||||
mults.HacknetNodeMoney === defaultMultipliers.HacknetNodeMoney
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>General:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.WorldDaemonDifficulty !== defaultMultipliers.WorldDaemonDifficulty ? (
|
||||
<Typography>
|
||||
{SpecialServers.WorldDaemon} difficulty: x{mults.WorldDaemonDifficulty.toFixed(3)}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.DaedalusAugsRequirement !== defaultMultipliers.DaedalusAugsRequirement ? (
|
||||
<Typography>Daedalus aug req.: {mults.DaedalusAugsRequirement}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.HacknetNodeMoney !== defaultMultipliers.HacknetNodeMoney ? (
|
||||
<Typography>Hacknet production: x{mults.HacknetNodeMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.CodingContractMoney !== defaultMultipliers.CodingContractMoney ? (
|
||||
<Typography>Coding contract reward: x{mults.CodingContractMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ClassGymExpGain !== defaultMultipliers.ClassGymExpGain ? (
|
||||
<Typography>Class/Gym exp: x{mults.ClassGymExpGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
WorldDaemonDifficulty: { name: `${SpecialServers.WorldDaemon} Difficulty` },
|
||||
DaedalusAugsRequirement: {
|
||||
name: "Daedalus Augs Requirement",
|
||||
content: String(mults.DaedalusAugsRequirement),
|
||||
},
|
||||
HacknetNodeMoney: { name: "Hacknet Production" },
|
||||
CodingContractMoney: { name: "Coding Contract Reward" },
|
||||
ClassGymExpGain: { name: "Class/Gym Exp" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="General" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function AugmentationMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.AugmentationMoneyCost === defaultMultipliers.AugmentationMoneyCost &&
|
||||
mults.AugmentationRepCost === defaultMultipliers.AugmentationRepCost
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Augmentations:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.AugmentationMoneyCost !== defaultMultipliers.AugmentationMoneyCost ? (
|
||||
<Typography>Cost: x{mults.AugmentationMoneyCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.AugmentationRepCost !== defaultMultipliers.AugmentationRepCost ? (
|
||||
<Typography>Reputation: x{mults.AugmentationRepCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
AugmentationMoneyCost: { name: "Money Cost" },
|
||||
AugmentationRepCost: {
|
||||
name: "Reputation Cost",
|
||||
color: Settings.theme.rep,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Augmentations" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function CompanyMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.CompanyWorkExpGain === defaultMultipliers.CompanyWorkExpGain &&
|
||||
mults.CompanyWorkMoney === defaultMultipliers.CompanyWorkMoney
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Company:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.CompanyWorkMoney !== defaultMultipliers.CompanyWorkMoney ? (
|
||||
<Typography>Money: x{mults.CompanyWorkMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.CompanyWorkExpGain !== defaultMultipliers.CompanyWorkExpGain ? (
|
||||
<Typography>Exp: x{mults.CompanyWorkExpGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
CompanyWorkMoney: {
|
||||
name: "Work Money",
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
CompanyWorkExpGain: { name: "Work Exp" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Company" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function StockMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.FourSigmaMarketDataApiCost === defaultMultipliers.FourSigmaMarketDataApiCost &&
|
||||
mults.FourSigmaMarketDataCost === defaultMultipliers.FourSigmaMarketDataCost
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Stock market:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.FourSigmaMarketDataCost !== defaultMultipliers.FourSigmaMarketDataCost ? (
|
||||
<Typography>Market data cost: x{mults.FourSigmaMarketDataCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.FourSigmaMarketDataApiCost !== defaultMultipliers.FourSigmaMarketDataApiCost ? (
|
||||
<Typography>Market data API cost: x{mults.FourSigmaMarketDataApiCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
FourSigmaMarketDataCost: { name: "Market Data Cost" },
|
||||
FourSigmaMarketDataApiCost: { name: "Market Data API Cost" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Stock Market" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function FactionMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.FactionPassiveRepGain === defaultMultipliers.FactionPassiveRepGain &&
|
||||
mults.FactionWorkExpGain === defaultMultipliers.FactionWorkExpGain &&
|
||||
mults.FactionWorkRepGain === defaultMultipliers.FactionWorkRepGain &&
|
||||
mults.RepToDonateToFaction === defaultMultipliers.RepToDonateToFaction
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Faction:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.RepToDonateToFaction !== defaultMultipliers.RepToDonateToFaction ? (
|
||||
<Typography>Favor to donate: x{mults.RepToDonateToFaction.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.FactionWorkRepGain !== defaultMultipliers.FactionWorkRepGain ? (
|
||||
<Typography>Work rep: x{mults.FactionWorkRepGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.FactionWorkExpGain !== defaultMultipliers.FactionWorkExpGain ? (
|
||||
<Typography>Work exp: x{mults.FactionWorkExpGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.FactionPassiveRepGain !== defaultMultipliers.FactionPassiveRepGain ? (
|
||||
<Typography>Passive rep: x{mults.FactionPassiveRepGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
RepToDonateToFaction: { name: "Favor to Donate" },
|
||||
FactionWorkRepGain: {
|
||||
name: "Work Reputation",
|
||||
color: Settings.theme.rep,
|
||||
},
|
||||
FactionWorkExpGain: { name: "Work Exp" },
|
||||
FactionPassiveRepGain: {
|
||||
name: "Passive Rep",
|
||||
color: Settings.theme.rep,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Faction" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function CrimeMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (mults.CrimeExpGain === defaultMultipliers.CrimeExpGain && mults.CrimeMoney === defaultMultipliers.CrimeMoney)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Crime:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.CrimeExpGain !== defaultMultipliers.CrimeExpGain ? (
|
||||
<Typography>Exp: x{mults.CrimeExpGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.CrimeMoney !== defaultMultipliers.CrimeMoney ? (
|
||||
<Typography>Money: x{mults.CrimeMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
CrimeExpGain: {
|
||||
name: "Crime Exp",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
CrimeMoney: {
|
||||
name: "Crime Money",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Crime" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function SkillMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.HackingLevelMultiplier === defaultMultipliers.HackingLevelMultiplier &&
|
||||
mults.AgilityLevelMultiplier === defaultMultipliers.AgilityLevelMultiplier &&
|
||||
mults.DefenseLevelMultiplier === defaultMultipliers.DefenseLevelMultiplier &&
|
||||
mults.DexterityLevelMultiplier === defaultMultipliers.DexterityLevelMultiplier &&
|
||||
mults.StrengthLevelMultiplier === defaultMultipliers.StrengthLevelMultiplier &&
|
||||
mults.CharismaLevelMultiplier === defaultMultipliers.CharismaLevelMultiplier
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Skills:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.HackingLevelMultiplier !== defaultMultipliers.HackingLevelMultiplier ? (
|
||||
<Typography>Hacking: x{mults.HackingLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.AgilityLevelMultiplier !== defaultMultipliers.AgilityLevelMultiplier ? (
|
||||
<Typography>Agility: x{mults.AgilityLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.DefenseLevelMultiplier !== defaultMultipliers.DefenseLevelMultiplier ? (
|
||||
<Typography>Defense: x{mults.DefenseLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.DexterityLevelMultiplier !== defaultMultipliers.DexterityLevelMultiplier ? (
|
||||
<Typography>Dexterity: x{mults.DexterityLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.StrengthLevelMultiplier !== defaultMultipliers.StrengthLevelMultiplier ? (
|
||||
<Typography>Strength: x{mults.StrengthLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.CharismaLevelMultiplier !== defaultMultipliers.CharismaLevelMultiplier ? (
|
||||
<Typography>Charisma: x{mults.CharismaLevelMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
HackingLevelMultiplier: {
|
||||
name: "Hacking Level",
|
||||
color: Settings.theme.hack,
|
||||
},
|
||||
StrengthLevelMultiplier: {
|
||||
name: "Strength Level",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
DefenseLevelMultiplier: {
|
||||
name: "Defense Level",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
DexterityLevelMultiplier: {
|
||||
name: "Dexterity Level",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
AgilityLevelMultiplier: {
|
||||
name: "Agility Level",
|
||||
color: Settings.theme.combat,
|
||||
},
|
||||
CharismaLevelMultiplier: {
|
||||
name: "Charisma Level",
|
||||
color: Settings.theme.cha,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Skills" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function HackingMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.ServerGrowthRate === defaultMultipliers.ServerGrowthRate &&
|
||||
mults.ServerMaxMoney === defaultMultipliers.ServerMaxMoney &&
|
||||
mults.ServerStartingMoney === defaultMultipliers.ServerStartingMoney &&
|
||||
mults.ServerStartingSecurity === defaultMultipliers.ServerStartingSecurity &&
|
||||
mults.ServerWeakenRate === defaultMultipliers.ServerWeakenRate &&
|
||||
mults.ManualHackMoney === defaultMultipliers.ManualHackMoney &&
|
||||
mults.ScriptHackMoney === defaultMultipliers.ScriptHackMoney &&
|
||||
mults.ScriptHackMoneyGain === defaultMultipliers.ScriptHackMoneyGain &&
|
||||
mults.HackExpGain === defaultMultipliers.HackExpGain
|
||||
)
|
||||
return <></>;
|
||||
const rows: IBNMultRows = {
|
||||
HackExpGain: {
|
||||
name: "Hacking Exp",
|
||||
color: Settings.theme.hack,
|
||||
},
|
||||
ServerGrowthRate: { name: "Server Growth Rate" },
|
||||
ServerMaxMoney: { name: "Server Max Money" },
|
||||
ServerStartingMoney: { name: "Server Starting Money" },
|
||||
ServerStartingSecurity: { name: "Server Starting Security" },
|
||||
ServerWeakenRate: { name: "Server Weaken Rate" },
|
||||
ManualHackMoney: {
|
||||
name: "Manual Hack Money",
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
ScriptHackMoney: {
|
||||
name: "Script Hack Money",
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
ScriptHackMoneyGain: {
|
||||
name: "Money Gained From Hack",
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Hacking:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.HackExpGain !== defaultMultipliers.HackExpGain ? (
|
||||
<Typography>Exp: x{mults.HackExpGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ServerGrowthRate !== defaultMultipliers.ServerGrowthRate ? (
|
||||
<Typography>Growth rate: x{mults.ServerGrowthRate.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ServerMaxMoney !== defaultMultipliers.ServerMaxMoney ? (
|
||||
<Typography>Max money: x{mults.ServerMaxMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ServerStartingMoney !== defaultMultipliers.ServerStartingMoney ? (
|
||||
<Typography>Starting money: x{mults.ServerStartingMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ServerStartingSecurity !== defaultMultipliers.ServerStartingSecurity ? (
|
||||
<Typography>Starting security: x{mults.ServerStartingSecurity.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ServerWeakenRate !== defaultMultipliers.ServerWeakenRate ? (
|
||||
<Typography>Weaken rate: x{mults.ServerWeakenRate.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ManualHackMoney !== defaultMultipliers.ManualHackMoney ? (
|
||||
<Typography>Manual hack money: x{mults.ManualHackMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ScriptHackMoney !== defaultMultipliers.ScriptHackMoney ? (
|
||||
<Typography>Hack money stolen: x{mults.ScriptHackMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.ScriptHackMoneyGain !== defaultMultipliers.ScriptHackMoneyGain ? (
|
||||
<Typography>Money gained from hack: x{mults.ScriptHackMoneyGain.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
return <BNMultTable sectionName="Hacking" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function PurchasedServersMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.PurchasedServerCost === defaultMultipliers.PurchasedServerCost &&
|
||||
mults.PurchasedServerSoftcap === defaultMultipliers.PurchasedServerSoftcap &&
|
||||
mults.PurchasedServerLimit === defaultMultipliers.PurchasedServerLimit &&
|
||||
mults.PurchasedServerMaxRam === defaultMultipliers.PurchasedServerMaxRam &&
|
||||
mults.HomeComputerRamCost === defaultMultipliers.HomeComputerRamCost
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Purchased servers:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.PurchasedServerCost !== defaultMultipliers.PurchasedServerCost ? (
|
||||
<Typography>Base cost: {mults.PurchasedServerCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.PurchasedServerSoftcap !== defaultMultipliers.PurchasedServerSoftcap ? (
|
||||
<Typography>Softcap cost: {mults.PurchasedServerSoftcap.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.PurchasedServerLimit !== defaultMultipliers.PurchasedServerLimit ? (
|
||||
<Typography>Limit: x{mults.PurchasedServerLimit.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.PurchasedServerMaxRam !== defaultMultipliers.PurchasedServerMaxRam ? (
|
||||
<Typography>Max ram: x{mults.PurchasedServerMaxRam.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.HomeComputerRamCost !== defaultMultipliers.HomeComputerRamCost ? (
|
||||
<Typography>Home ram cost: x{mults.HomeComputerRamCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
PurchasedServerCost: {
|
||||
name: "Base Cost",
|
||||
content: mults.PurchasedServerCost.toFixed(3),
|
||||
},
|
||||
PurchasedServerSoftcap: {
|
||||
name: "Softcap Cost",
|
||||
content: mults.PurchasedServerSoftcap.toFixed(3),
|
||||
},
|
||||
PurchasedServerLimit: { name: "Server Limit" },
|
||||
PurchasedServerMaxRam: { name: "Max RAM" },
|
||||
HomeComputerRamCost: { name: "Home RAM Cost" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Purchased Servers" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function InfiltrationMults({ mults }: IMultsProps): React.ReactElement {
|
||||
// is it empty check
|
||||
if (
|
||||
mults.InfiltrationMoney === defaultMultipliers.InfiltrationMoney &&
|
||||
mults.InfiltrationRep === defaultMultipliers.InfiltrationRep
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Infiltration:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.InfiltrationMoney !== defaultMultipliers.InfiltrationMoney ? (
|
||||
<Typography>Money: {mults.InfiltrationMoney.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.InfiltrationRep !== defaultMultipliers.InfiltrationRep ? (
|
||||
<Typography>Reputation: x{mults.InfiltrationRep.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
InfiltrationMoney: {
|
||||
name: "Infiltration Money",
|
||||
color: Settings.theme.money,
|
||||
},
|
||||
InfiltrationRep: {
|
||||
name: "Infiltration Reputation",
|
||||
color: Settings.theme.rep,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Infiltration" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function BladeburnerMults({ n, mults }: IMultsProps): React.ReactElement {
|
||||
function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
// access check
|
||||
if (n !== 6 && n !== 7 && player.sourceFileLvl(6) === 0) return <></>;
|
||||
//default mults check
|
||||
if (mults.BladeburnerRank === 1 && mults.BladeburnerSkillCost === 1) return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Bladeburner:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.BladeburnerRank !== 1 ? <Typography>Rank gain: x{mults.BladeburnerRank.toFixed(3)}</Typography> : <></>}
|
||||
{mults.BladeburnerSkillCost !== 1 ? (
|
||||
<Typography>Skill cost: x{mults.BladeburnerSkillCost.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
if (!player.canAccessBladeburner()) return <></>;
|
||||
|
||||
const rows: IBNMultRows = {
|
||||
BladeburnerRank: { name: "Rank Gain" },
|
||||
BladeburnerSkillCost: { name: "Skill Cost" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Bladeburner" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function StanekMults({ n, mults }: IMultsProps): React.ReactElement {
|
||||
function StanekMults({ mults }: IMultsProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
// access check
|
||||
if (n !== 13 && player.sourceFileLvl(13) === 0) return <></>;
|
||||
//default mults check
|
||||
if (
|
||||
mults.StaneksGiftExtraSize === defaultMultipliers.StaneksGiftExtraSize &&
|
||||
mults.StaneksGiftPowerMultiplier === defaultMultipliers.StaneksGiftPowerMultiplier
|
||||
)
|
||||
return <></>;
|
||||
if (!player.canAccessCotMG()) return <></>;
|
||||
|
||||
const s = mults.StaneksGiftExtraSize;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Stanek's Gift:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.StaneksGiftPowerMultiplier !== defaultMultipliers.StaneksGiftPowerMultiplier ? (
|
||||
<Typography>Gift power: x{mults.StaneksGiftPowerMultiplier.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{s !== defaultMultipliers.StaneksGiftExtraSize ? (
|
||||
<Typography>Base size modifier: {s > defaultMultipliers.StaneksGiftExtraSize ? `+${s}` : s}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const extraSize = mults.StaneksGiftExtraSize.toFixed(3);
|
||||
const rows: IBNMultRows = {
|
||||
StnakesGiftPowerMultiplier: { name: "Gift Power" },
|
||||
StaneksGiftExtraSize: {
|
||||
name: "Base Size Modifier",
|
||||
content: `${mults.StaneksGiftExtraSize > defaultMultipliers.StaneksGiftExtraSize ? `+${extraSize}` : extraSize}`,
|
||||
},
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Stanek's Gift" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function GangMults({ n, mults }: IMultsProps): React.ReactElement {
|
||||
function GangMults({ mults }: IMultsProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
// access check
|
||||
if (n !== 2 && player.sourceFileLvl(2) === 0) return <></>;
|
||||
// is it empty check
|
||||
if (
|
||||
mults.GangSoftcap === defaultMultipliers.GangSoftcap &&
|
||||
mults.GangUniqueAugs === defaultMultipliers.GangUniqueAugs
|
||||
)
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Gang:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.GangSoftcap !== defaultMultipliers.GangSoftcap ? (
|
||||
<Typography>Softcap: {mults.GangSoftcap.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.GangUniqueAugs !== defaultMultipliers.GangUniqueAugs ? (
|
||||
<Typography>Unique augs: x{mults.GangUniqueAugs.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
if (player.bitNodeN !== 2 && player.sourceFileLvl(2) <= 0) return <></>;
|
||||
|
||||
const rows: IBNMultRows = {
|
||||
GangSoftcap: {
|
||||
name: "Gang Softcap",
|
||||
content: mults.GangSoftcap.toFixed(3),
|
||||
},
|
||||
GangUniqueAugs: { name: "Unique Augmentations" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Gang" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
||||
function CorporationMults({ n, mults }: IMultsProps): React.ReactElement {
|
||||
function CorporationMults({ mults }: IMultsProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
// access check
|
||||
if (n !== 3 && player.sourceFileLvl(3) === 0) return <></>;
|
||||
// is it empty check
|
||||
if (
|
||||
mults.CorporationSoftCap === defaultMultipliers.CorporationSoftCap &&
|
||||
mults.CorporationValuation === defaultMultipliers.CorporationValuation
|
||||
)
|
||||
return <></>;
|
||||
if (!player.canAccessCorporation()) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant={"h5"}>Corporation:</Typography>
|
||||
<Box mx={1}>
|
||||
{mults.CorporationSoftCap !== defaultMultipliers.CorporationSoftCap ? (
|
||||
<Typography>Softcap: {mults.CorporationSoftCap.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{mults.CorporationValuation !== defaultMultipliers.CorporationValuation ? (
|
||||
<Typography>Valuation: x{mults.CorporationValuation.toFixed(3)}</Typography>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
const rows: IBNMultRows = {
|
||||
CorporationSoftcap: {
|
||||
name: "Corporation Softcap",
|
||||
content: mults.CorporationSoftcap.toFixed(3),
|
||||
},
|
||||
CorporationValuation: { name: "Valuation" },
|
||||
};
|
||||
|
||||
return <BNMultTable sectionName="Corporation" rowData={rows} mults={mults} />;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export function PortalModal(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
<Typography>{bitNode.info}</Typography>
|
||||
<BitnodeMultiplierDescription n={props.n} />
|
||||
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
|
@ -697,7 +697,7 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
// Set variables
|
||||
if (args.length === 4) {
|
||||
const variable = args[1];
|
||||
const variable = args[1].toLowerCase(); // allows Action Type to be with or without capitalisation.
|
||||
const val = args[2];
|
||||
|
||||
let highLow = false; // True for high, false for low
|
||||
@ -1919,7 +1919,7 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
|
||||
// If the Player starts doing some other actions, set action to idle and alert
|
||||
if (player.hasAugmentation(AugmentationNames.BladesSimulacrum) === false && player.isWorking) {
|
||||
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.isWorking) {
|
||||
if (this.action.type !== ActionTypes["Idle"]) {
|
||||
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
||||
if (this.automateEnabled) {
|
||||
|
245
src/Constants.ts
245
src/Constants.ts
@ -63,26 +63,6 @@ export const CONSTANTS: {
|
||||
GameCyclesPerQuarterHour: number;
|
||||
MillisecondsPerFiveMinutes: number;
|
||||
GameCyclesPerFiveMinutes: number;
|
||||
FactionWorkHacking: string;
|
||||
FactionWorkField: string;
|
||||
FactionWorkSecurity: string;
|
||||
WorkTypeCompany: string;
|
||||
WorkTypeCompanyPartTime: string;
|
||||
WorkTypeFaction: string;
|
||||
WorkTypeCreateProgram: string;
|
||||
WorkTypeStudyClass: string;
|
||||
WorkTypeCrime: string;
|
||||
WorkTypeGraftAugmentation: string;
|
||||
ClassStudyComputerScience: string;
|
||||
ClassDataStructures: string;
|
||||
ClassNetworks: string;
|
||||
ClassAlgorithms: string;
|
||||
ClassManagement: string;
|
||||
ClassLeadership: string;
|
||||
ClassGymStrength: string;
|
||||
ClassGymDefense: string;
|
||||
ClassGymDexterity: string;
|
||||
ClassGymAgility: string;
|
||||
ClassDataStructuresBaseCost: number;
|
||||
ClassNetworksBaseCost: number;
|
||||
ClassAlgorithmsBaseCost: number;
|
||||
@ -95,18 +75,6 @@ export const CONSTANTS: {
|
||||
ClassAlgorithmsBaseExp: number;
|
||||
ClassManagementBaseExp: number;
|
||||
ClassLeadershipBaseExp: number;
|
||||
CrimeShoplift: string;
|
||||
CrimeRobStore: string;
|
||||
CrimeMug: string;
|
||||
CrimeLarceny: string;
|
||||
CrimeDrugs: string;
|
||||
CrimeBondForgery: string;
|
||||
CrimeTraffickArms: string;
|
||||
CrimeHomicide: string;
|
||||
CrimeGrandTheftAuto: string;
|
||||
CrimeKidnap: string;
|
||||
CrimeAssassination: string;
|
||||
CrimeHeist: string;
|
||||
CodingContractBaseFactionRepGain: number;
|
||||
CodingContractBaseCompanyRepGain: number;
|
||||
CodingContractBaseMoneyGain: number;
|
||||
@ -116,11 +84,12 @@ export const CONSTANTS: {
|
||||
SoARepMult: number;
|
||||
EntropyEffect: number;
|
||||
TotalNumBitNodes: number;
|
||||
InfiniteLoopLimit: number;
|
||||
Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG
|
||||
LatestUpdate: string;
|
||||
} = {
|
||||
VersionString: "1.6.4",
|
||||
VersionNumber: 15,
|
||||
VersionString: "1.7.0",
|
||||
VersionNumber: 18,
|
||||
|
||||
// Speed (in ms) at which the main loop is updated
|
||||
_idleSpeed: 200,
|
||||
@ -223,28 +192,6 @@ export const CONSTANTS: {
|
||||
|
||||
// Player Work & Action
|
||||
BaseFocusBonus: 0.8,
|
||||
FactionWorkHacking: "Faction Hacking Work",
|
||||
FactionWorkField: "Faction Field Work",
|
||||
FactionWorkSecurity: "Faction Security Work",
|
||||
|
||||
WorkTypeCompany: "Working for Company",
|
||||
WorkTypeCompanyPartTime: "Working for Company part-time",
|
||||
WorkTypeFaction: "Working for Faction",
|
||||
WorkTypeCreateProgram: "Working on Create a Program",
|
||||
WorkTypeStudyClass: "Studying or Taking a class at university",
|
||||
WorkTypeCrime: "Committing a crime",
|
||||
WorkTypeGraftAugmentation: "Grafting an Augmentation",
|
||||
|
||||
ClassStudyComputerScience: "studying Computer Science",
|
||||
ClassDataStructures: "taking a Data Structures course",
|
||||
ClassNetworks: "taking a Networks course",
|
||||
ClassAlgorithms: "taking an Algorithms course",
|
||||
ClassManagement: "taking a Management course",
|
||||
ClassLeadership: "taking a Leadership course",
|
||||
ClassGymStrength: "training your strength at a gym",
|
||||
ClassGymDefense: "training your defense at a gym",
|
||||
ClassGymDexterity: "training your dexterity at a gym",
|
||||
ClassGymAgility: "training your agility at a gym",
|
||||
|
||||
ClassDataStructuresBaseCost: 40,
|
||||
ClassNetworksBaseCost: 80,
|
||||
@ -260,19 +207,6 @@ export const CONSTANTS: {
|
||||
ClassManagementBaseExp: 2,
|
||||
ClassLeadershipBaseExp: 4,
|
||||
|
||||
CrimeShoplift: "shoplift",
|
||||
CrimeRobStore: "rob a store",
|
||||
CrimeMug: "mug someone",
|
||||
CrimeLarceny: "commit larceny",
|
||||
CrimeDrugs: "deal drugs",
|
||||
CrimeBondForgery: "forge corporate bonds",
|
||||
CrimeTraffickArms: "traffick illegal arms",
|
||||
CrimeHomicide: "commit homicide",
|
||||
CrimeGrandTheftAuto: "commit grand theft auto",
|
||||
CrimeKidnap: "kidnap someone for ransom",
|
||||
CrimeAssassination: "assassinate a high-profile target",
|
||||
CrimeHeist: "pull off the ultimate heist",
|
||||
|
||||
// Coding Contract
|
||||
// TODO: Move this into Coding contract implementation?
|
||||
CodingContractBaseFactionRepGain: 2500,
|
||||
@ -293,22 +227,173 @@ export const CONSTANTS: {
|
||||
// BitNode/Source-File related stuff
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
Donations: 6,
|
||||
InfiniteLoopLimit: 1000,
|
||||
|
||||
Donations: 7,
|
||||
|
||||
LatestUpdate: `
|
||||
v1.6.3 - 2022-04-01 Few stanek fixes
|
||||
----------------------------
|
||||
## [draft] v1.7.0 - 2022-04-13 to 2022-05-20
|
||||
|
||||
Stanek Gift
|
||||
#### Information
|
||||
|
||||
* Has a minimum size of 2x3
|
||||
* Active Fragment property 'avgCharge' renamed to 'highestCharge'
|
||||
* Formula for fragment effect updated to make 561% more sense.
|
||||
Now you can charge to your heart content.
|
||||
* Logs for the 'chargeFragment' function updated.
|
||||
Modifications included between **2022-04-13** and **2022-05-20** 'b5e4d70' to '0fbe4a1').
|
||||
|
||||
Misc.
|
||||
_[See Pull Requests on GitHub](https://github.com/search?q=user%3Adanielyxie%20repo%3Abitburner%20is%3Apr%20is%3Amerged%20merged%3A%222022-04-13T16%3A32%3A26.000Z..2022-05-20T06%3A08%3A51.000Z%22)_
|
||||
|
||||
#### Merged Pull Requests
|
||||
|
||||
- [Feature] Monaco Theme Editor (by @nickofolas) #[3438](https://github.com/danielyxie/bitburner/pull/3438)
|
||||
- [Fix] Dummy Stanek grid width (by @nickofolas) #[3442](https://github.com/danielyxie/bitburner/pull/3442)
|
||||
- [Fix] Theme browser assets not loading (by @nickofolas) #[3446](https://github.com/danielyxie/bitburner/pull/3446)
|
||||
- Accept valid JSON arrays in coding contracts (by @Savlik) #[3247](https://github.com/danielyxie/bitburner/pull/3247)
|
||||
- another dark theme? (by @hydroflame) #[3450](https://github.com/danielyxie/bitburner/pull/3450)
|
||||
- API: Add repFromDonation() to the Formula API (by @Hoekstraa) #[3461](https://github.com/danielyxie/bitburner/pull/3461)
|
||||
- API: Add safeguard to ns.killall(), preventing killing itself by default (by @Hoekstraa) #[3607](https://github.com/danielyxie/bitburner/pull/3607)
|
||||
- API: FIX #2993 sleeve.travel with invalid city names (by @TheMas3212) #[3458](https://github.com/danielyxie/bitburner/pull/3458)
|
||||
- API: Fix inconsistent return value in 'ns.grafting.getAugmentationGraftTime' (by @nickofolas) #[3539](https://github.com/danielyxie/bitburner/pull/3539)
|
||||
- API: Fix leak of real Employee object in hireEmployee (by @TheMas3212) #[3483](https://github.com/danielyxie/bitburner/pull/3483)
|
||||
- API: replace a number of references to workerscript.log with \_ctx.log (by @TheMas3212) #[3470](https://github.com/danielyxie/bitburner/pull/3470)
|
||||
- API: Terminal screen can now be cleared from within scripts with ns.ui.clearTerminal() (by @Hoekstraa) #[3618](https://github.com/danielyxie/bitburner/pull/3618)
|
||||
- AUGMENTATIONS: Fix 'isSpecial' filter in helper (Removes NeuroFlux, Stanek's Gift, etc from gangs) (by @nickofolas) #[3565](https://github.com/danielyxie/bitburner/pull/3565)
|
||||
- AUGMENTATIONS: Fix Augmentation rep req not being properly influenced by BitNode multipliers (by @nickofolas) #[3652](https://github.com/danielyxie/bitburner/pull/3652)
|
||||
- AUGMENTATIONS: Fix NeuroFlux being applied improperly and migrate broken saves (by @nickofolas) #[3613](https://github.com/danielyxie/bitburner/pull/3613)
|
||||
- AUGMENTATIONS: Fix reputation check for faction augs (by @nickofolas) #[3609](https://github.com/danielyxie/bitburner/pull/3609)
|
||||
- AUGMENTATIONS: Tweak a couple small UI elements (by @nickofolas) #[3614](https://github.com/danielyxie/bitburner/pull/3614)
|
||||
- basic doc no longer hacker themed (by @hydroflame) #[3449](https://github.com/danielyxie/bitburner/pull/3449)
|
||||
- BITNODE: FIX #3546 BitVerse now shows proper BN level when accessed via flume (by @nickofolas) #[3550](https://github.com/danielyxie/bitburner/pull/3550)
|
||||
- BLADEBURNER: fixes #3648 : Automate console command capitalisation inconsistent (by @Vic1970) #[3647](https://github.com/danielyxie/bitburner/pull/3647)
|
||||
- BLADEBURNER: Fix #3594 Blade's Simulacrum worked without being installed (by @Undeemiss) #[3639](https://github.com/danielyxie/bitburner/pull/3639)
|
||||
- blood (by @hydroflame) #[3495](https://github.com/danielyxie/bitburner/pull/3495)
|
||||
- BUGFIX: getAugmentationCost response backwards (by @phyzical) #[3617](https://github.com/danielyxie/bitburner/pull/3617)
|
||||
- BUGFIX: Handle edge case in LZ compression code and fix docs (by @stalefishies) #[3581](https://github.com/danielyxie/bitburner/pull/3581)
|
||||
- BUGFIX: make bonustime for gang in miliseconds (by @phyzical) #[3578](https://github.com/danielyxie/bitburner/pull/3578)
|
||||
- BUGFIX: sleeve stale object refence during augmentation (by @phyzical) #[3601](https://github.com/danielyxie/bitburner/pull/3601)
|
||||
- Bugfix/corp updates (by @phyzical) #[3321](https://github.com/danielyxie/bitburner/pull/3321)
|
||||
- Bump async from 2.6.3 to 2.6.4 (by @dependabot[bot]) #[3463](https://github.com/danielyxie/bitburner/pull/3463)
|
||||
- CODINGCONTRACT: Fix #3391 Double contract reward exploit (by @Undeemiss) #[3646](https://github.com/danielyxie/bitburner/pull/3646)
|
||||
- CODINGCONTRACT: FIX #3484 BREAKING Fixed capitalization in contract name (by @Undeemiss) #[3537](https://github.com/danielyxie/bitburner/pull/3537)
|
||||
- CODINGCONTRACT: New "Proper 2-Coloring of a Graph" contract (by @Undeemiss) #[3530](https://github.com/danielyxie/bitburner/pull/3530)
|
||||
- CODINGCONTRACT: Three new compression contracts (by @stalefishies) #[3541](https://github.com/danielyxie/bitburner/pull/3541)
|
||||
- CODINGCONTRACT: Typo & clarity fixes to description of Encoded Binary to Integer contract (by @ActuallyCurtis) #[3469](https://github.com/danielyxie/bitburner/pull/3469)
|
||||
- CODINGCONTRACT: Updated description of 2-coloring contract (by @Undeemiss) #[3531](https://github.com/danielyxie/bitburner/pull/3531)
|
||||
- COMPANY: Fix #3551 Applying for a new job will not change active employer if player is performing company work (by @Snarling) #[3552](https://github.com/danielyxie/bitburner/pull/3552)
|
||||
- CORPORATIONS: Expose makeProducts on NSDivision interface (by @DavidGrinberg) #[3570](https://github.com/danielyxie/bitburner/pull/3570)
|
||||
- CORPORATIONS: Expose sales cost on NSMaterial interface (by @DavidGrinberg) #[3574](https://github.com/danielyxie/bitburner/pull/3574)
|
||||
- Corrected example grids found in Stanek help (by @Undeemiss) #[3441](https://github.com/danielyxie/bitburner/pull/3441)
|
||||
- Create program action no longer creates duplicates (by @Undeemiss) #[3436](https://github.com/danielyxie/bitburner/pull/3436)
|
||||
- DOCUMENTATION: Add descriptions for compression contracts (by @stalefishies) #[3559](https://github.com/danielyxie/bitburner/pull/3559)
|
||||
- DOCUMENTATION: Add new coding contract descriptions (by @stalefishies) #[3542](https://github.com/danielyxie/bitburner/pull/3542)
|
||||
- DOCUMENTATION: Clarify definition for installAugmentations() (by @PSEUDOSTAGE) #[3560](https://github.com/danielyxie/bitburner/pull/3560)
|
||||
- DOCUMENTATION: FIX #3516 "cannot" misspelled as "cannnot" (by @Undeemiss) #[3533](https://github.com/danielyxie/bitburner/pull/3533)
|
||||
- EDITOR: FIX #3502 Editor theme migration crash (by @nickofolas) #[3503](https://github.com/danielyxie/bitburner/pull/3503)
|
||||
- FEATURE: added logic to allow quitJob to be called from singularity (by @phyzical) #[3577](https://github.com/danielyxie/bitburner/pull/3577)
|
||||
- fix #3395 donating to special factions possible via singularity (by @TheMas3212) #[3456](https://github.com/danielyxie/bitburner/pull/3456)
|
||||
- fix b1tflum3 and destroyW0r1dD43m0n singularity functions to check for sf4 (by @TheMas3212) #[3443](https://github.com/danielyxie/bitburner/pull/3443)
|
||||
- Fix inconsistancy with trying to work for gang factions while running a gang (by @TheMas3212) #[3454](https://github.com/danielyxie/bitburner/pull/3454)
|
||||
- Fix infiltration rep BN mult calculation (by @trambelus) #[3632](https://github.com/danielyxie/bitburner/pull/3632)
|
||||
- Fix script editor settings. (by @hydroflame) #[3504](https://github.com/danielyxie/bitburner/pull/3504)
|
||||
- Fix test/jest/Netscript/DynamicRamCalculation.test.js (by @TheMas3212) #[3455](https://github.com/danielyxie/bitburner/pull/3455)
|
||||
- GRAFTING: Fix Grafting not being handled in singularity stop work (by @nickofolas) #[3568](https://github.com/danielyxie/bitburner/pull/3568)
|
||||
- GRAFTING: Implement sorting options (by @nickofolas) #[3654](https://github.com/danielyxie/bitburner/pull/3654)
|
||||
- INFILTRATION: Added new faction called infiltrators that provide infiltration specific augs. (by @phyzical) #[3241](https://github.com/danielyxie/bitburner/pull/3241)
|
||||
- INFILTRATION: Fix minigame cycle (by @nickofolas) #[3549](https://github.com/danielyxie/bitburner/pull/3549)
|
||||
- INFILTRATION: Fix phyzical WKS aug effects being applied before aug is installed (by @nickofolas) #[3555](https://github.com/danielyxie/bitburner/pull/3555)
|
||||
- INFILTRATION: Fix rep reward being substantially higher than intended (by @nickofolas) #[3562](https://github.com/danielyxie/bitburner/pull/3562)
|
||||
- INFILTRATION: New faction, Shadows of Anarchy, provides various augs to help infiltrations. (by @hydroflame) #[3543](https://github.com/danielyxie/bitburner/pull/3543)
|
||||
- INFILTRATION: Update gameplay UI (by @nickofolas) #[3587](https://github.com/danielyxie/bitburner/pull/3587)
|
||||
- keeping up to date (by @hydroflame) #[3432](https://github.com/danielyxie/bitburner/pull/3432)
|
||||
- Keeping up to date. (by @hydroflame) #[3561](https://github.com/danielyxie/bitburner/pull/3561)
|
||||
- Make .lit and .msg files clickable (by @Chris380) #[3453](https://github.com/danielyxie/bitburner/pull/3453)
|
||||
- MESSAGES: Added the name of NiteSec's server to their .msg (by @Undeemiss) #[3466](https://github.com/danielyxie/bitburner/pull/3466)
|
||||
- MISC: add better typing to Electron.tsx (by @taralx) #[3540](https://github.com/danielyxie/bitburner/pull/3540)
|
||||
- MISC: Added NS function closeTail to close tail windows (by @Undeemiss) #[3666](https://github.com/danielyxie/bitburner/pull/3666)
|
||||
- MISC: Adjust deps to current usage (by @taralx) #[3519](https://github.com/danielyxie/bitburner/pull/3519)
|
||||
- MISC: Close some GitHub issues that do not need action (by @Undeemiss) #[3640](https://github.com/danielyxie/bitburner/pull/3640)
|
||||
- MISC: Closing more GitHub issues I missed last time (by @Undeemiss) #[3665](https://github.com/danielyxie/bitburner/pull/3665)
|
||||
- MISC: Correct BB Skill point achievement name (by @Undeemiss) #[3571](https://github.com/danielyxie/bitburner/pull/3571)
|
||||
- MISC: Correct typos in getScriptRam docs. (by @nzdjb) #[3590](https://github.com/danielyxie/bitburner/pull/3590)
|
||||
- MISC: Fix #3125 BREAKING Renamed BN mult CorporationSoftCap to CorporationSoftcap (by @Undeemiss) #[3638](https://github.com/danielyxie/bitburner/pull/3638)
|
||||
- MISC: FIX #3593 Float errors can no longer prevent full usage of a server's available ram. (by @Snarling) #[3619](https://github.com/danielyxie/bitburner/pull/3619)
|
||||
- MISC: fix typing conflict between jest and cypress (by @taralx) #[3518](https://github.com/danielyxie/bitburner/pull/3518)
|
||||
- MISC: fix typing conflict between jest and cypress (by @taralx) #[3644](https://github.com/danielyxie/bitburner/pull/3644)
|
||||
- MISC: Fixed typo in exceptionAlert.ts (by @Undeemiss) #[3572](https://github.com/danielyxie/bitburner/pull/3572)
|
||||
- MISC: Fixed typos in game options (by @notacompsciguy) #[3584](https://github.com/danielyxie/bitburner/pull/3584)
|
||||
- MISC: HammingCodingContracts need rework (by @Hedrauta) #[3479](https://github.com/danielyxie/bitburner/pull/3479)
|
||||
- MISC: Implemented infinite loop safety net. (by @hydroflame) #[3624](https://github.com/danielyxie/bitburner/pull/3624)
|
||||
- MISC: make jQuery use explicit (by @taralx) #[3517](https://github.com/danielyxie/bitburner/pull/3517)
|
||||
- MISC: Make tutorial explain ns1 vs ns2 better (by @hydroflame) #[3586](https://github.com/danielyxie/bitburner/pull/3586)
|
||||
- MISC: Remove comments that describe nonexistent augs (by @Undeemiss) #[3569](https://github.com/danielyxie/bitburner/pull/3569)
|
||||
- MISC: update @types/numeral and fix type errors (by @taralx) #[3521](https://github.com/danielyxie/bitburner/pull/3521)
|
||||
- MISC: Update logic for stats page BitNode level (by @nickofolas) #[3512](https://github.com/danielyxie/bitburner/pull/3512)
|
||||
- MISC: upgrade to eslint v8 (by @taralx) #[3523](https://github.com/danielyxie/bitburner/pull/3523)
|
||||
- MISC: Wrap most of the API in the new api wrapper (by @hydroflame) #[3627](https://github.com/danielyxie/bitburner/pull/3627)
|
||||
- OPTIONS: Fix sliders not sliding correctly (by @nickofolas) #[3642](https://github.com/danielyxie/bitburner/pull/3642)
|
||||
- REFACTOR: augmentation cost, rep cost and level to be calculated in place (by @phyzical) #[3544](https://github.com/danielyxie/bitburner/pull/3544)
|
||||
- REFACTOR: augmentation isSpecial adjustments (by @phyzical) #[3564](https://github.com/danielyxie/bitburner/pull/3564)
|
||||
- Reran npm format and lint to fix formatting (by @Undeemiss) #[3434](https://github.com/danielyxie/bitburner/pull/3434)
|
||||
- Revert "MISC: fix typing conflict between jest and cypress" (by @hydroflame) #[3608](https://github.com/danielyxie/bitburner/pull/3608)
|
||||
- Revert "MISC: HammingCodingContracts need rework" (by @hydroflame) #[3500](https://github.com/danielyxie/bitburner/pull/3500)
|
||||
- revert theme (by @hydroflame) #[3451](https://github.com/danielyxie/bitburner/pull/3451)
|
||||
- Singularity: Fix #3489 Disable checkTixApiAccess for purchase4SMarketData (by @DavidGrinberg) #[3490](https://github.com/danielyxie/bitburner/pull/3490)
|
||||
- SLEEVES: Fix issues with Sleeve UI crashing when Sleeve task faction becomes gang faction (by @nickofolas) #[3557](https://github.com/danielyxie/bitburner/pull/3557)
|
||||
- STANEK: Fix #3196 Charging booster fragments throws an error (by @Undeemiss) #[3637](https://github.com/danielyxie/bitburner/pull/3637)
|
||||
- STANEK: FIX #3277 Can no longer overlap rotated fragments (by @Undeemiss) #[3460](https://github.com/danielyxie/bitburner/pull/3460)
|
||||
- STANEK: FIX #3282 Added NS function stanek.acceptGift (by @Undeemiss) #[3513](https://github.com/danielyxie/bitburner/pull/3513)
|
||||
- STANEK: Properly reapply entropy in Stanek's Gift (by @nickofolas) #[3673](https://github.com/danielyxie/bitburner/pull/3673)
|
||||
- STANEK: Stanek NS functions correctly throw errors when stanek not installed (by @Undeemiss) #[3660](https://github.com/danielyxie/bitburner/pull/3660)
|
||||
- Started collecting lore so that additions to it are simpler (by @Undeemiss) #[3465](https://github.com/danielyxie/bitburner/pull/3465)
|
||||
- TERMINAL: FIX #3492 Allow cd .. even when destination directory is empty (by @Snarling) #[3525](https://github.com/danielyxie/bitburner/pull/3525)
|
||||
- TERMINAL: FIX #3651 Make directory name regex more flexible (by @Dane-Horn) #[3653](https://github.com/danielyxie/bitburner/pull/3653)
|
||||
- TOOLING: Add GitHub action to validate PR titles (by @MartinFournier) #[3471](https://github.com/danielyxie/bitburner/pull/3471)
|
||||
- UI FIX #3485 - Allow bulk purchasing when smart supply is enabled (by @phyzical) #[3486](https://github.com/danielyxie/bitburner/pull/3486)
|
||||
- UI: Change text color of Augmentations page backup button (by @nickofolas) #[3511](https://github.com/danielyxie/bitburner/pull/3511)
|
||||
- UI: FIX #1754 Stanek effect summary & slight tweak. (by @borisflagell) #[3622](https://github.com/danielyxie/bitburner/pull/3622)
|
||||
- UI: FIX #2228,#2958 Fix tab highlights and highlight files not on home. (by @phyzical) #[2989](https://github.com/danielyxie/bitburner/pull/2989)
|
||||
- UI: FIX #2256 Hacknet server's upgrade tooltip were not handling RAM… (by @borisflagell) #[3532](https://github.com/danielyxie/bitburner/pull/3532)
|
||||
- UI: FIX #2741 Allow using modifier keys inside the typing infiltration (by @Dane-Horn) #[3634](https://github.com/danielyxie/bitburner/pull/3634)
|
||||
- UI: FIX #2829 Remove defeated NPC gangs from territory page (by @Dane-Horn) #[3633](https://github.com/danielyxie/bitburner/pull/3633)
|
||||
- UI: FIX #3313 Streamline the GraftingRoot page by making it rerender. (by @borisflagell) #[3558](https://github.com/danielyxie/bitburner/pull/3558)
|
||||
- UI: FIX #3341 Enable touch-clicks in react-draggable (by @Snarling) #[3488](https://github.com/danielyxie/bitburner/pull/3488)
|
||||
- UI: FIX #3415 Tweak Manage Gang button visibility (by @borisflagell) #[3528](https://github.com/danielyxie/bitburner/pull/3528)
|
||||
- UI: FIX #3457 autocomplete suggestions no longer require hovering terminal input (by @Snarling) #[3493](https://github.com/danielyxie/bitburner/pull/3493)
|
||||
- UI: FIX #3473 'mv' now says destination script is running instead of returning an error (by @Hoekstraa) #[3474](https://github.com/danielyxie/bitburner/pull/3474)
|
||||
- UI: FIX #3522 realigned autocomplete popup (by @Snarling) #[3524](https://github.com/danielyxie/bitburner/pull/3524)
|
||||
- UI: FIX #3592 Sidebar and bash shortcuts now work on MacOS with US-like layouts (by @Hoekstraa) #[3605](https://github.com/danielyxie/bitburner/pull/3605)
|
||||
- UI: Fix Agility BitNode multiplier not appearing in UI (by @nickofolas) #[3662](https://github.com/danielyxie/bitburner/pull/3662)
|
||||
- UI: Fix exclusive augs not always showing as purchasable through gangs when they should (by @nickofolas) #[3676](https://github.com/danielyxie/bitburner/pull/3676)
|
||||
- UI: Fix the achievement covenant icon was not shown (by @Risenafis) #[3510](https://github.com/danielyxie/bitburner/pull/3510)
|
||||
- UI: Fix z-index of modals overriding everything (by @nickofolas) #[3620](https://github.com/danielyxie/bitburner/pull/3620)
|
||||
- UI: lightweight description update on "increase maximum money" hash spending option. (by @borisflagell) #[3547](https://github.com/danielyxie/bitburner/pull/3547)
|
||||
- UI: Minor improvements to log boxes (by @nickofolas) #[3641](https://github.com/danielyxie/bitburner/pull/3641)
|
||||
- UI: Overhaul GameOptions UI (by @nickofolas) #[3505](https://github.com/danielyxie/bitburner/pull/3505)
|
||||
- UI: Positioning improved for tail titlebar buttons, and tail window has minimum size constraints. (by @Snarling) #[3548](https://github.com/danielyxie/bitburner/pull/3548)
|
||||
- UI: Redesign purchasable Augmentations (by @nickofolas) #[3545](https://github.com/danielyxie/bitburner/pull/3545)
|
||||
- UI: Refactor and redesign WorkInProgress interface (by @nickofolas) #[3611](https://github.com/danielyxie/bitburner/pull/3611)
|
||||
- UI: Refactors, redesigns, and new section to stats page (by @nickofolas) #[3626](https://github.com/danielyxie/bitburner/pull/3626)
|
||||
- UI: Sort and color Graft Augmentation list (by @jaype87) #[3616](https://github.com/danielyxie/bitburner/pull/3616)
|
||||
- UI: Update Factions list interface (by @nickofolas) #[3675](https://github.com/danielyxie/bitburner/pull/3675)
|
||||
- WORK: FIX #3435 Quitting the active job now sets first remaining job as active (by @Snarling) #[3507](https://github.com/danielyxie/bitburner/pull/3507)
|
||||
- WORK: Refactor work types to use 'enum's instead of constants (by @nickofolas) #[3612](https://github.com/danielyxie/bitburner/pull/3612)
|
||||
|
||||
#### Other Changes
|
||||
|
||||
- increase donation counter (by @hydroflame) - [8456410](https://github.com/danielyxie/bitburner/commit/84564100e90c46ae4b816853c2cdea0bc309af4d)
|
||||
- allbuild commit 7f9e3775 (by @hydroflame) - [791c19c](https://github.com/danielyxie/bitburner/commit/791c19c4fe447c9231bfb423b9fc48114e783b43)
|
||||
- allbuild commit bcbda22a (by @hydroflame) - [032c440](https://github.com/danielyxie/bitburner/commit/032c440eaeb069eecd720ec2f8e069f705a0c1b4)
|
||||
- fix documentation for getDarkwebPrograms (by @hydroflame) - [4056956](https://github.com/danielyxie/bitburner/commit/4056956c2ada37946333bdad44cb0b6eb3909bf8)
|
||||
- support ASNI (by @hydroflame) - [36c7ef1](https://github.com/danielyxie/bitburner/commit/36c7ef1ad7ea8bb69fca23bce5883a3c2e23f1e0)
|
||||
- allbuild commit 22b6d0d5 (by @hydroflame) - [b46718d](https://github.com/danielyxie/bitburner/commit/b46718d188880ecf716ae045861d81d61e00af4b)
|
||||
- allbuild commit 36c7ef1a (by @hydroflame) - [d0ebf5e](https://github.com/danielyxie/bitburner/commit/d0ebf5e14e0498cb063fde35d63c9f59f2c01e35)
|
||||
- Update documentation for employee (by @hydroflame) - [100e81c](https://github.com/danielyxie/bitburner/commit/100e81c8ab4a408f74cc9bd9ffe2b8bad3d03462)
|
||||
- allbuild commit c799b291 (by @hydroflame) - [f5f5879](https://github.com/danielyxie/bitburner/commit/f5f5879fc380678d978e2b0a29ba7b6f0b4c9ec0)
|
||||
- ideas (by @hydroflame) - [0121fee](https://github.com/danielyxie/bitburner/commit/0121fee6e4c690d01650d1e68a80ea363bb48bce)
|
||||
- allbuild commit 0121fee6 (by @hydroflame) - [5c417e9](https://github.com/danielyxie/bitburner/commit/5c417e9b4df236df8bf3e2f8262b7bce87c934df)
|
||||
- Update codebase for stanek (by @hydroflame) - [c2b4a5b](https://github.com/danielyxie/bitburner/commit/c2b4a5b52a2162d2e49c7317b0a60a349984eb47)
|
||||
- fix lint (by @hydroflame) - [4cc518f](https://github.com/danielyxie/bitburner/commit/4cc518f37723aafb3168b64cd689408afdb74877)
|
||||
- Fix (by @hydroflame) - [9af553f](https://github.com/danielyxie/bitburner/commit/9af553f63cb1380795550648b0134b608564fab8)
|
||||
- Fix stanek leaking classes (by @hydroflame) - [fda3f02](https://github.com/danielyxie/bitburner/commit/fda3f02d73dba27034128c9be5e810a51e475e38)
|
||||
- fix conflicts (by @hydroflame) - [ca1a2aa](https://github.com/danielyxie/bitburner/commit/ca1a2aad333fa838b6d0e57f89e1cedba086a4a0)
|
||||
- Nerf noodle bar.
|
||||
|
||||
* Nerf noodle bar.
|
||||
`,
|
||||
};
|
||||
|
@ -159,7 +159,7 @@ export class Corporation {
|
||||
if (this.unlockUpgrades[6] === 1) {
|
||||
upgrades += 0.1;
|
||||
}
|
||||
return Math.pow(dividends, BitNodeMultipliers.CorporationSoftCap + upgrades);
|
||||
return Math.pow(dividends, BitNodeMultipliers.CorporationSoftcap + upgrades);
|
||||
}
|
||||
|
||||
determineValuation(): number {
|
||||
|
@ -162,11 +162,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
<Tooltip
|
||||
title={tutorial ? <Typography>Purchase your required materials to get production started!</Typography> : ""}
|
||||
>
|
||||
<Button
|
||||
color={tutorial ? "error" : "primary"}
|
||||
onClick={() => setPurchaseMaterialOpen(true)}
|
||||
disabled={props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)}
|
||||
>
|
||||
<Button color={tutorial ? "error" : "primary"} onClick={() => setPurchaseMaterialOpen(true)}>
|
||||
{purchaseButtonText}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@ -174,6 +170,9 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
mat={mat}
|
||||
warehouse={warehouse}
|
||||
open={purchaseMaterialOpen}
|
||||
disablePurchaseLimit={
|
||||
props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)
|
||||
}
|
||||
onClose={() => setPurchaseMaterialOpen(false)}
|
||||
/>
|
||||
|
||||
|
@ -106,6 +106,7 @@ interface IProps {
|
||||
onClose: () => void;
|
||||
mat: Material;
|
||||
warehouse: Warehouse;
|
||||
disablePurchaseLimit: boolean;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player purchase a Material
|
||||
@ -143,6 +144,7 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
|
||||
<Typography>
|
||||
Enter the amount of {props.mat.name} you would like to purchase per second. This material's cost changes
|
||||
constantly.
|
||||
{props.disablePurchaseLimit ? "Note: Purchase amount is disabled as smart supply is enabled" : ""}
|
||||
</Typography>
|
||||
<TextField
|
||||
value={buyAmt}
|
||||
@ -150,10 +152,15 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
|
||||
autoFocus={true}
|
||||
placeholder="Purchase amount"
|
||||
type="number"
|
||||
disabled={props.disablePurchaseLimit}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<Button onClick={purchaseMaterial}>Confirm</Button>
|
||||
<Button onClick={clearPurchase}>Clear Purchase</Button>
|
||||
<Button disabled={props.disablePurchaseLimit} onClick={purchaseMaterial}>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button disabled={props.disablePurchaseLimit} onClick={clearPurchase}>
|
||||
Clear Purchase
|
||||
</Button>
|
||||
{division.hasResearch("Bulk Purchasing") && (
|
||||
<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
|
||||
)}
|
||||
|
@ -68,13 +68,7 @@ export class ActiveFragment {
|
||||
}
|
||||
|
||||
copy(): ActiveFragment {
|
||||
// We have to do a round trip because the constructor.
|
||||
const fragment = FragmentById(this.id);
|
||||
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
|
||||
const c = new ActiveFragment({ x: this.x, y: this.y, rotation: this.rotation, fragment: fragment });
|
||||
c.highestCharge = this.highestCharge;
|
||||
c.numCharge = this.numCharge;
|
||||
return c;
|
||||
return Object.assign({}, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,13 +76,7 @@ export class Fragment {
|
||||
}
|
||||
|
||||
copy(): Fragment {
|
||||
return new Fragment(
|
||||
this.id,
|
||||
this.shape.map((a) => a.slice()),
|
||||
this.type,
|
||||
this.power,
|
||||
this.limit,
|
||||
);
|
||||
return Object.assign({}, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +136,9 @@ export class StaneksGift implements IStaneksGift {
|
||||
}
|
||||
|
||||
updateMults(p: IPlayer): void {
|
||||
p.reapplyAllAugmentations(true);
|
||||
p.reapplyAllSourceFiles();
|
||||
// applyEntropy also reapplies all augmentations and source files
|
||||
// This wraps up the reset nicely
|
||||
p.applyEntropy(p.entropy);
|
||||
|
||||
for (const aFrag of this.fragments) {
|
||||
const fragment = aFrag.fragment();
|
||||
|
@ -1,5 +0,0 @@
|
||||
import { StanekConstants } from "../data/Constants";
|
||||
|
||||
export function CalculateCharge(ram: number): number {
|
||||
return ram * Math.pow(1 + Math.log2(ram) * StanekConstants.RAMBonus, 0.7);
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
export function CalculateEffect(avgCharge: number, numCharge: number, power: number, boost: number): number {
|
||||
export function CalculateEffect(highestCharge: number, numCharge: number, power: number, boost: number): number {
|
||||
return (
|
||||
1 +
|
||||
(Math.log(avgCharge + 1) / (Math.log(1.8) * 100)) *
|
||||
(Math.log(highestCharge + 1) / 60) *
|
||||
Math.pow((numCharge + 1) / 5, 0.07) *
|
||||
power *
|
||||
boost *
|
||||
|
88
src/CotMG/ui/ActiveFragmentSummary.tsx
Normal file
88
src/CotMG/ui/ActiveFragmentSummary.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import React from "react";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { FragmentType, Effect } from "../FragmentType";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Table from "@mui/material/Table";
|
||||
import { TableBody, TableCell, TableRow } from "@mui/material";
|
||||
|
||||
type IProps = {
|
||||
gift: IStaneksGift;
|
||||
};
|
||||
|
||||
function formatEffect(effect: number, type: FragmentType): string {
|
||||
if (Effect(type).includes("+x%")) {
|
||||
return Effect(type).replace(/-*x%/, numeralWrapper.formatPercentage(effect - 1));
|
||||
} else if (Effect(type).includes("-x%")) {
|
||||
const perc = numeralWrapper.formatPercentage(1 - 1 / effect);
|
||||
return Effect(type).replace(/-x%/, perc);
|
||||
} else {
|
||||
return Effect(type);
|
||||
}
|
||||
}
|
||||
|
||||
export function ActiveFragmentSummary(props: IProps): React.ReactElement {
|
||||
const summary: { coordinate: { x: number; y: number }[]; effect: number; type: FragmentType }[] = [];
|
||||
// Iterate through Active Fragment
|
||||
props.gift.fragments.forEach((fragment: ActiveFragment) => {
|
||||
const f = fragment.fragment();
|
||||
// Discard ToolBrush and Booster.
|
||||
if (![FragmentType.Booster, FragmentType.None, FragmentType.Delete].includes(f.type)) {
|
||||
// Check for an existing entry in summary for this fragment's type
|
||||
const entry = summary.find((e) => {
|
||||
return e.type === f.type;
|
||||
});
|
||||
if (entry) {
|
||||
// If there's one, update the existing entry
|
||||
entry.effect *= props.gift.effect(fragment);
|
||||
entry.coordinate.push({ x: fragment.x, y: fragment.y });
|
||||
} else {
|
||||
// If there's none, create a new entry
|
||||
summary.push({
|
||||
coordinate: [{ x: fragment.x, y: fragment.y }],
|
||||
effect: props.gift.effect(fragment),
|
||||
type: f.type,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Paper sx={{ mb: 1 }}>
|
||||
<Typography variant="h5">Summary of active fragments:</Typography>
|
||||
<Table sx={{ display: "table", width: "100%" }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
|
||||
<Typography>Coordinate</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
|
||||
<Typography>Effect</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{summary.map((entry) => {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
|
||||
<Typography>
|
||||
{entry.coordinate.map((coord) => {
|
||||
return "[" + coord.x + "," + coord.y + "]";
|
||||
})}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
|
||||
<Typography>{formatEffect(entry.effect, entry.type)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
);
|
||||
}
|
@ -25,23 +25,21 @@ export function FragmentInspector(props: IProps): React.ReactElement {
|
||||
|
||||
if (props.fragment === undefined) {
|
||||
return (
|
||||
<Paper>
|
||||
<Paper sx={{ flexGrow: 1 }}>
|
||||
<Typography>
|
||||
[X, Y] {props.x}, {props.y}
|
||||
<br />
|
||||
<br />
|
||||
ID: N/A
|
||||
<br />
|
||||
Effect: N/A
|
||||
<br />
|
||||
Magnitude: N/A
|
||||
Base Power: N/A
|
||||
<br />
|
||||
Charge: N/A
|
||||
<br />
|
||||
Heat: N/A
|
||||
root [X, Y] N/A
|
||||
<br />
|
||||
Effect: N/A
|
||||
<br />
|
||||
[X, Y] N/A
|
||||
<br />
|
||||
[X, Y] {props.x}, {props.y}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
@ -63,8 +61,11 @@ export function FragmentInspector(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<Paper sx={{ flexGrow: 1 }}>
|
||||
<Typography>
|
||||
[X, Y] {props.x}, {props.y}
|
||||
<br />
|
||||
<br />
|
||||
ID: {props.fragment.id}
|
||||
<br />
|
||||
Effect: {effect}
|
||||
@ -73,10 +74,8 @@ export function FragmentInspector(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
Charge: {charge}
|
||||
<br />
|
||||
<br />
|
||||
root [X, Y] {props.fragment.x}, {props.fragment.y}
|
||||
<br />
|
||||
[X, Y] {props.x}, {props.y}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
|
@ -9,6 +9,9 @@ import Button from "@mui/material/Button";
|
||||
import { Table } from "../../ui/React/Table";
|
||||
import { Grid } from "./Grid";
|
||||
import { zeros, calculateGrid } from "../Helper";
|
||||
import { ActiveFragmentSummary } from "./ActiveFragmentSummary";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
gift: IStaneksGift;
|
||||
@ -84,9 +87,8 @@ export function MainBoard(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={clear}>Clear</Button>
|
||||
<Box display="flex">
|
||||
<Table>
|
||||
<Box display="flex" sx={{ mb: 1 }}>
|
||||
<Table sx={{ mr: 1 }}>
|
||||
<Grid
|
||||
width={props.gift.width()}
|
||||
height={props.gift.height()}
|
||||
@ -99,7 +101,22 @@ export function MainBoard(props: IProps): React.ReactElement {
|
||||
</Table>
|
||||
<FragmentInspector gift={props.gift} x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
||||
</Box>
|
||||
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
||||
<Box display="flex" sx={{ mb: 1 }}>
|
||||
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
||||
</Box>
|
||||
|
||||
<ActiveFragmentSummary gift={props.gift} />
|
||||
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
WARNING : This will remove all active fragment from the grid. <br />
|
||||
All cumulated charges will be lost.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={clear}>Clear grid</Button>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import Typography from "@mui/material/Typography";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { Fragments } from "../Fragment";
|
||||
import { DummyGrid } from "./DummyGrid";
|
||||
import Container from "@mui/material/Container";
|
||||
|
||||
type IProps = {
|
||||
staneksGift: IStaneksGift;
|
||||
@ -22,7 +23,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||
}
|
||||
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
|
||||
return (
|
||||
<>
|
||||
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">
|
||||
Stanek's Gift
|
||||
<Info
|
||||
@ -184,18 +185,18 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
<Typography sx={{ mb: 1 }}>
|
||||
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 the neighboring fragments (not diagonally). Use Q/E to rotate fragments.
|
||||
</Typography>
|
||||
{staneksGift.storedCycles > 5 && (
|
||||
<Typography>
|
||||
<Typography sx={{ mb: 1 }}>
|
||||
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
|
||||
</Typography>
|
||||
)}
|
||||
<MainBoard gift={staneksGift} />
|
||||
</>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
|
||||
import { IRouter } from "../ui/Router";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { CrimeType } from "../utils/WorkType";
|
||||
|
||||
interface IConstructorParams {
|
||||
hacking_success_weight?: number;
|
||||
@ -42,7 +43,7 @@ export class Crime {
|
||||
time = 0;
|
||||
|
||||
// Corresponding type in CONSTANTS. Contains a description for the crime activity
|
||||
type = "";
|
||||
type: CrimeType;
|
||||
|
||||
// Weighting factors that determine how stats affect the success rate of this crime
|
||||
hacking_success_weight = 0;
|
||||
@ -61,7 +62,15 @@ export class Crime {
|
||||
charisma_exp = 0;
|
||||
intelligence_exp = 0;
|
||||
|
||||
constructor(name = "", type = "", time = 0, money = 0, difficulty = 0, karma = 0, params: IConstructorParams = {}) {
|
||||
constructor(
|
||||
name = "",
|
||||
type: CrimeType,
|
||||
time = 0,
|
||||
money = 0,
|
||||
difficulty = 0,
|
||||
karma = 0,
|
||||
params: IConstructorParams = {},
|
||||
) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
|
@ -9,7 +9,7 @@ export function determineCrimeSuccess(p: IPlayer, type: string): boolean {
|
||||
let found = false;
|
||||
for (const i of Object.keys(Crimes)) {
|
||||
const crime = Crimes[i];
|
||||
if (crime.type == type) {
|
||||
if (crime.type === type) {
|
||||
chance = crime.successRate(p);
|
||||
found = true;
|
||||
break;
|
||||
|
@ -3,8 +3,10 @@ import { Crime } from "./Crime";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { CrimeType } from "../utils/WorkType";
|
||||
|
||||
export const Crimes: IMap<Crime> = {
|
||||
Shoplift: new Crime("Shoplift", CONSTANTS.CrimeShoplift, 2e3, 15e3, 1 / 20, 0.1, {
|
||||
Shoplift: new Crime("Shoplift", CrimeType.Shoplift, 2e3, 15e3, 1 / 20, 0.1, {
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
@ -12,7 +14,7 @@ export const Crimes: IMap<Crime> = {
|
||||
agility_exp: 2,
|
||||
}),
|
||||
|
||||
RobStore: new Crime("Rob Store", CONSTANTS.CrimeRobStore, 60e3, 400e3, 1 / 5, 0.5, {
|
||||
RobStore: new Crime("Rob Store", CrimeType.RobStore, 60e3, 400e3, 1 / 5, 0.5, {
|
||||
hacking_exp: 30,
|
||||
dexterity_exp: 45,
|
||||
agility_exp: 45,
|
||||
@ -24,7 +26,7 @@ export const Crimes: IMap<Crime> = {
|
||||
intelligence_exp: 7.5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Mug: new Crime("Mug", CONSTANTS.CrimeMug, 4e3, 36e3, 1 / 5, 0.25, {
|
||||
Mug: new Crime("Mug", CrimeType.Mug, 4e3, 36e3, 1 / 5, 0.25, {
|
||||
strength_exp: 3,
|
||||
defense_exp: 3,
|
||||
dexterity_exp: 3,
|
||||
@ -36,7 +38,7 @@ export const Crimes: IMap<Crime> = {
|
||||
agility_success_weight: 0.5,
|
||||
}),
|
||||
|
||||
Larceny: new Crime("Larceny", CONSTANTS.CrimeLarceny, 90e3, 800e3, 1 / 3, 1.5, {
|
||||
Larceny: new Crime("Larceny", CrimeType.Larceny, 90e3, 800e3, 1 / 3, 1.5, {
|
||||
hacking_exp: 45,
|
||||
dexterity_exp: 60,
|
||||
agility_exp: 60,
|
||||
@ -48,7 +50,7 @@ export const Crimes: IMap<Crime> = {
|
||||
intelligence_exp: 15 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
DealDrugs: new Crime("Deal Drugs", CONSTANTS.CrimeDrugs, 10e3, 120e3, 1, 0.5, {
|
||||
DealDrugs: new Crime("Deal Drugs", CrimeType.Drugs, 10e3, 120e3, 1, 0.5, {
|
||||
dexterity_exp: 5,
|
||||
agility_exp: 5,
|
||||
charisma_exp: 10,
|
||||
@ -58,7 +60,7 @@ export const Crimes: IMap<Crime> = {
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
BondForgery: new Crime("Bond Forgery", CONSTANTS.CrimeBondForgery, 300e3, 4.5e6, 1 / 2, 0.1, {
|
||||
BondForgery: new Crime("Bond Forgery", CrimeType.BondForgery, 300e3, 4.5e6, 1 / 2, 0.1, {
|
||||
hacking_exp: 100,
|
||||
dexterity_exp: 150,
|
||||
charisma_exp: 15,
|
||||
@ -69,7 +71,7 @@ export const Crimes: IMap<Crime> = {
|
||||
intelligence_exp: 60 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
TraffickArms: new Crime("Traffick Arms", CONSTANTS.CrimeTraffickArms, 40e3, 600e3, 2, 1, {
|
||||
TraffickArms: new Crime("Traffick Arms", CrimeType.TraffickArms, 40e3, 600e3, 2, 1, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
@ -83,7 +85,7 @@ export const Crimes: IMap<Crime> = {
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
Homicide: new Crime("Homicide", CONSTANTS.CrimeHomicide, 3e3, 45e3, 1, 3, {
|
||||
Homicide: new Crime("Homicide", CrimeType.Homicide, 3e3, 45e3, 1, 3, {
|
||||
strength_exp: 2,
|
||||
defense_exp: 2,
|
||||
dexterity_exp: 2,
|
||||
@ -97,7 +99,7 @@ export const Crimes: IMap<Crime> = {
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
GrandTheftAuto: new Crime("Grand Theft Auto", CONSTANTS.CrimeGrandTheftAuto, 80e3, 1.6e6, 8, 5, {
|
||||
GrandTheftAuto: new Crime("Grand Theft Auto", CrimeType.GrandTheftAuto, 80e3, 1.6e6, 8, 5, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
@ -113,7 +115,7 @@ export const Crimes: IMap<Crime> = {
|
||||
intelligence_exp: 16 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Kidnap: new Crime("Kidnap", CONSTANTS.CrimeKidnap, 120e3, 3.6e6, 5, 6, {
|
||||
Kidnap: new Crime("Kidnap", CrimeType.Kidnap, 120e3, 3.6e6, 5, 6, {
|
||||
strength_exp: 80,
|
||||
defense_exp: 80,
|
||||
dexterity_exp: 80,
|
||||
@ -128,7 +130,7 @@ export const Crimes: IMap<Crime> = {
|
||||
intelligence_exp: 26 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Assassination: new Crime("Assassination", CONSTANTS.CrimeAssassination, 300e3, 12e6, 8, 10, {
|
||||
Assassination: new Crime("Assassination", CrimeType.Assassination, 300e3, 12e6, 8, 10, {
|
||||
strength_exp: 300,
|
||||
defense_exp: 300,
|
||||
dexterity_exp: 300,
|
||||
@ -143,7 +145,7 @@ export const Crimes: IMap<Crime> = {
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
Heist: new Crime("Heist", CONSTANTS.CrimeHeist, 600e3, 120e6, 18, 15, {
|
||||
Heist: new Crime("Heist", CrimeType.Heist, 600e3, 120e6, 18, 15, {
|
||||
hacking_exp: 450,
|
||||
strength_exp: 450,
|
||||
defense_exp: 450,
|
||||
|
@ -11,11 +11,41 @@ import { exportScripts } from "./Terminal/commands/download";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { hash } from "./hash/hash";
|
||||
|
||||
interface IReturnWebStatus extends IReturnStatus {
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
appNotifier: {
|
||||
terminal: (message: string, type?: string) => void;
|
||||
toast: (message: string, type: ToastVariant, duration?: number) => void;
|
||||
};
|
||||
appSaveFns: {
|
||||
triggerSave: () => Promise<void>;
|
||||
triggerGameExport: () => void;
|
||||
triggerScriptsExport: () => void;
|
||||
getSaveData: () => { save: string; fileName: string };
|
||||
getSaveInfo: (base64save: string) => Promise<ImportPlayerData | undefined>;
|
||||
pushSaveData: (base64save: string, automatic?: boolean) => void;
|
||||
};
|
||||
electronBridge: {
|
||||
send: (channel: string, data?: unknown) => void;
|
||||
receive: (channel: string, func: (...args: any[]) => void) => void;
|
||||
};
|
||||
}
|
||||
interface Document {
|
||||
getFiles: () => IReturnWebStatus;
|
||||
deleteFile: (filename: string) => IReturnWebStatus;
|
||||
saveFile: (filename: string, code: string) => IReturnWebStatus;
|
||||
}
|
||||
}
|
||||
|
||||
export function initElectron(): void {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
if (userAgent.indexOf(" electron/") > -1) {
|
||||
// Electron-specific code
|
||||
(document as any).achievements = [];
|
||||
document.achievements = [];
|
||||
initWebserver();
|
||||
initAppNotifier();
|
||||
initSaveFunctions();
|
||||
@ -24,11 +54,6 @@ export function initElectron(): void {
|
||||
}
|
||||
|
||||
function initWebserver(): void {
|
||||
interface IReturnWebStatus extends IReturnStatus {
|
||||
data?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
}
|
||||
function normalizeFileName(filename: string): string {
|
||||
filename = filename.replace(/\/\/+/g, "/");
|
||||
filename = removeLeadingSlash(filename);
|
||||
@ -38,7 +63,7 @@ function initWebserver(): void {
|
||||
return filename;
|
||||
}
|
||||
|
||||
(document as any).getFiles = function (): IReturnWebStatus {
|
||||
document.getFiles = function (): IReturnWebStatus {
|
||||
const home = GetServer("home");
|
||||
if (home === null) {
|
||||
return {
|
||||
@ -58,7 +83,7 @@ function initWebserver(): void {
|
||||
};
|
||||
};
|
||||
|
||||
(document as any).deleteFile = function (filename: string): IReturnWebStatus {
|
||||
document.deleteFile = function (filename: string): IReturnWebStatus {
|
||||
filename = normalizeFileName(filename);
|
||||
const home = GetServer("home");
|
||||
if (home === null) {
|
||||
@ -70,7 +95,7 @@ function initWebserver(): void {
|
||||
return home.removeFile(filename);
|
||||
};
|
||||
|
||||
(document as any).saveFile = function (filename: string, code: string): IReturnWebStatus {
|
||||
document.saveFile = function (filename: string, code: string): IReturnWebStatus {
|
||||
filename = normalizeFileName(filename);
|
||||
|
||||
code = Buffer.from(code, "base64").toString();
|
||||
@ -115,7 +140,7 @@ function initAppNotifier(): void {
|
||||
};
|
||||
|
||||
// Will be consumud by the electron wrapper.
|
||||
(window as any).appNotifier = funcs;
|
||||
window.appNotifier = funcs;
|
||||
}
|
||||
|
||||
function initSaveFunctions(): void {
|
||||
@ -149,38 +174,38 @@ function initSaveFunctions(): void {
|
||||
};
|
||||
|
||||
// Will be consumud by the electron wrapper.
|
||||
(window as any).appSaveFns = funcs;
|
||||
window.appSaveFns = funcs;
|
||||
}
|
||||
|
||||
function initElectronBridge(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
const bridge = window.electronBridge;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.receive("get-save-data-request", () => {
|
||||
const data = (window as any).appSaveFns.getSaveData();
|
||||
const data = window.appSaveFns.getSaveData();
|
||||
bridge.send("get-save-data-response", data);
|
||||
});
|
||||
bridge.receive("get-save-info-request", async (save: string) => {
|
||||
const data = await (window as any).appSaveFns.getSaveInfo(save);
|
||||
const data = await window.appSaveFns.getSaveInfo(save);
|
||||
bridge.send("get-save-info-response", data);
|
||||
});
|
||||
bridge.receive("push-save-request", ({ save, automatic = false }: { save: string; automatic: boolean }) => {
|
||||
(window as any).appSaveFns.pushSaveData(save, automatic);
|
||||
window.appSaveFns.pushSaveData(save, automatic);
|
||||
});
|
||||
bridge.receive("trigger-save", () => {
|
||||
return (window as any).appSaveFns
|
||||
return window.appSaveFns
|
||||
.triggerSave()
|
||||
.then(() => {
|
||||
bridge.send("save-completed");
|
||||
})
|
||||
.catch((error: any) => {
|
||||
.catch((error: unknown) => {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
|
||||
});
|
||||
});
|
||||
bridge.receive("trigger-game-export", () => {
|
||||
try {
|
||||
(window as any).appSaveFns.triggerGameExport();
|
||||
window.appSaveFns.triggerGameExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
|
||||
@ -188,7 +213,7 @@ function initElectronBridge(): void {
|
||||
});
|
||||
bridge.receive("trigger-scripts-export", () => {
|
||||
try {
|
||||
(window as any).appSaveFns.triggerScriptsExport();
|
||||
window.appSaveFns.triggerScriptsExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000);
|
||||
@ -197,14 +222,14 @@ function initElectronBridge(): void {
|
||||
}
|
||||
|
||||
export function pushGameSaved(data: SaveData): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
const bridge = window.electronBridge;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-game-saved", data);
|
||||
}
|
||||
|
||||
export function pushGameReady(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
const bridge = window.electronBridge;
|
||||
if (!bridge) return;
|
||||
|
||||
// Send basic information to the electron wrapper
|
||||
@ -222,7 +247,7 @@ export function pushGameReady(): void {
|
||||
}
|
||||
|
||||
export function pushImportResult(wasImported: boolean): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
const bridge = window.electronBridge;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-import-result", { wasImported });
|
||||
@ -230,7 +255,7 @@ export function pushImportResult(wasImported: boolean): void {
|
||||
}
|
||||
|
||||
export function pushDisableRestore(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
const bridge = window.electronBridge;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-disable-restore", { duration: 1000 * 60 });
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
@ -18,7 +18,6 @@ import {
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { InvitationEvent } from "./ui/InvitationModal";
|
||||
import { FactionNames } from "./data/FactionNames";
|
||||
import { updateAugmentationCosts, getNextNeuroFluxLevel } from "../Augmentation/AugmentationHelpers";
|
||||
import { SFC32RNG } from "../Casino/RNG";
|
||||
|
||||
export function inviteToFaction(faction: Faction): void {
|
||||
@ -54,63 +53,41 @@ export function joinFaction(faction: Faction): void {
|
||||
//Returns a boolean indicating whether the player has the prerequisites for the
|
||||
//specified Augmentation
|
||||
export function hasAugmentationPrereqs(aug: Augmentation): boolean {
|
||||
let hasPrereqs = true;
|
||||
if (aug.prereqs && aug.prereqs.length > 0) {
|
||||
for (let i = 0; i < aug.prereqs.length; ++i) {
|
||||
const prereqAug = Augmentations[aug.prereqs[i]];
|
||||
if (prereqAug == null) {
|
||||
console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Player.hasAugmentation(prereqAug, true) === false) {
|
||||
hasPrereqs = false;
|
||||
|
||||
// Check if the aug is purchased
|
||||
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
|
||||
if (Player.queuedAugmentations[j].name === prereqAug.name) {
|
||||
hasPrereqs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasPrereqs;
|
||||
return aug.prereqs.every((aug) => Player.hasAugmentation(aug));
|
||||
}
|
||||
|
||||
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
||||
const hasPrereqs = hasAugmentationPrereqs(aug);
|
||||
const augCosts = aug.getCost(Player);
|
||||
if (!hasPrereqs) {
|
||||
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
|
||||
const txt = `You must first purchase or install ${aug.prereqs
|
||||
.filter((req) => !Player.hasAugmentation(req))
|
||||
.join(",")} before you can purchase this one.`;
|
||||
if (sing) {
|
||||
return txt;
|
||||
} else {
|
||||
dialogBoxCreate(txt);
|
||||
}
|
||||
} else if (aug.baseCost !== 0 && Player.money < aug.baseCost) {
|
||||
} else if (augCosts.moneyCost !== 0 && Player.money < augCosts.moneyCost) {
|
||||
const txt = "You don't have enough money to purchase " + aug.name;
|
||||
if (sing) {
|
||||
return txt;
|
||||
}
|
||||
dialogBoxCreate(txt);
|
||||
} else if (fac.playerReputation < aug.baseRepRequirement) {
|
||||
} else if (fac.playerReputation < augCosts.repCost) {
|
||||
const txt = "You don't have enough faction reputation to purchase " + aug.name;
|
||||
if (sing) {
|
||||
return txt;
|
||||
}
|
||||
dialogBoxCreate(txt);
|
||||
} else if (aug.baseCost === 0 || Player.money >= aug.baseCost) {
|
||||
} else if (augCosts.moneyCost === 0 || Player.money >= augCosts.moneyCost) {
|
||||
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
queuedAugmentation.level = getNextNeuroFluxLevel();
|
||||
queuedAugmentation.level = aug.getLevel(Player);
|
||||
}
|
||||
Player.queuedAugmentations.push(queuedAugmentation);
|
||||
|
||||
Player.loseMoney(aug.baseCost, "augmentations");
|
||||
|
||||
updateAugmentationCosts();
|
||||
Player.loseMoney(augCosts.moneyCost, "augmentations");
|
||||
|
||||
if (sing) {
|
||||
return "You purchased " + aug.name;
|
||||
@ -162,16 +139,14 @@ export function processPassiveFactionRepGain(numCycles: number): void {
|
||||
export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Faction): string[] => {
|
||||
// If player has a gang with this faction, return (almost) all augmentations
|
||||
if (player.hasGangWith(faction.name)) {
|
||||
let augs = Object.values(Augmentations);
|
||||
let augs = Object.values(StaticAugmentations);
|
||||
|
||||
// Remove special augs
|
||||
augs = augs.filter((a) => !a.isSpecial);
|
||||
augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationNames.CongruityImplant);
|
||||
|
||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor, AugmentationNames.CongruityImplant];
|
||||
|
||||
if (player.bitNodeN !== 2) {
|
||||
if (player.bitNodeN === 2) {
|
||||
// TRP is not available outside of BN2 for Gangs
|
||||
blacklist.push(AugmentationNames.TheRedPill);
|
||||
augs.push(StaticAugmentations[AugmentationNames.TheRedPill]);
|
||||
}
|
||||
|
||||
const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`);
|
||||
@ -190,9 +165,6 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
|
||||
};
|
||||
augs = augs.filter(uniqueFilter);
|
||||
|
||||
// Remove blacklisted augs
|
||||
augs = augs.filter((a) => !blacklist.includes(a.name));
|
||||
|
||||
return augs.map((a) => a.name);
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,22 @@
|
||||
/**
|
||||
* Root React Component for displaying a faction's "Purchase Augmentations" page
|
||||
*/
|
||||
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
|
||||
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Faction } from "../Faction";
|
||||
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { hasAugmentationPrereqs, getFactionAugmentationsFiltered } from "../FactionHelpers";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import Table from "@mui/material/Table";
|
||||
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { FactionNames } from "../data/FactionNames";
|
||||
import { Faction } from "../Faction";
|
||||
import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
@ -63,13 +55,13 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
function getAugsSortedByCost(): string[] {
|
||||
const augs = getAugs();
|
||||
augs.sort((augName1, augName2) => {
|
||||
const aug1 = Augmentations[augName1],
|
||||
aug2 = Augmentations[augName2];
|
||||
const aug1 = StaticAugmentations[augName1],
|
||||
aug2 = StaticAugmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
|
||||
return aug1.baseCost - aug2.baseCost;
|
||||
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost;
|
||||
});
|
||||
|
||||
return augs;
|
||||
@ -78,31 +70,32 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
function getAugsSortedByPurchasable(): string[] {
|
||||
const augs = getAugs();
|
||||
function canBuy(augName: string): boolean {
|
||||
const aug = Augmentations[augName];
|
||||
const repCost = aug.baseRepRequirement;
|
||||
const aug = StaticAugmentations[augName];
|
||||
const augCosts = aug.getCost(player);
|
||||
const repCost = augCosts.repCost;
|
||||
const hasReq = props.faction.playerReputation >= repCost;
|
||||
const hasRep = hasAugmentationPrereqs(aug);
|
||||
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost;
|
||||
const hasCost = augCosts.moneyCost !== 0 && player.money > augCosts.moneyCost;
|
||||
return hasCost && hasReq && hasRep;
|
||||
}
|
||||
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
||||
const aug1 = Augmentations[augName1],
|
||||
aug2 = Augmentations[augName2];
|
||||
const aug1 = StaticAugmentations[augName1],
|
||||
aug2 = StaticAugmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
|
||||
return aug1.baseCost - aug2.baseCost;
|
||||
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost;
|
||||
});
|
||||
const cantBuy = augs
|
||||
.filter((aug) => !canBuy(aug))
|
||||
.sort((augName1, augName2) => {
|
||||
const aug1 = Augmentations[augName1],
|
||||
aug2 = Augmentations[augName2];
|
||||
const aug1 = StaticAugmentations[augName1],
|
||||
aug2 = StaticAugmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
||||
return aug1.getCost(player).repCost - aug2.getCost(player).repCost;
|
||||
});
|
||||
|
||||
return buy.concat(cantBuy);
|
||||
@ -111,12 +104,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
function getAugsSortedByReputation(): string[] {
|
||||
const augs = getAugs();
|
||||
augs.sort((augName1, augName2) => {
|
||||
const aug1 = Augmentations[augName1],
|
||||
aug2 = Augmentations[augName2];
|
||||
const aug1 = StaticAugmentations[augName1],
|
||||
aug2 = StaticAugmentations[augName2];
|
||||
if (aug1 == null || aug2 == null) {
|
||||
throw new Error("Invalid Augmentation Names");
|
||||
}
|
||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
||||
return aug1.getCost(player).repCost - aug2.getCost(player).repCost;
|
||||
});
|
||||
|
||||
return augs;
|
||||
@ -137,38 +130,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
aug === AugmentationNames.NeuroFluxGovernor ||
|
||||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
|
||||
);
|
||||
|
||||
const purchaseableAugmentation = (aug: string, owned = false): React.ReactNode => {
|
||||
return (
|
||||
<PurchaseableAugmentation
|
||||
augName={aug}
|
||||
faction={props.faction}
|
||||
key={aug}
|
||||
p={player}
|
||||
rerender={rerender}
|
||||
owned={owned}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
|
||||
|
||||
let ownedElem = <></>;
|
||||
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
|
||||
if (owned.length !== 0) {
|
||||
ownedElem = (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant="h4">Purchased Augmentations</Typography>
|
||||
<Typography>This faction also offers these augmentations but you already own them.</Typography>
|
||||
{owned.map((aug) => purchaseableAugmentation(aug, true))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const multiplierComponent =
|
||||
props.faction.name !== FactionNames.ShadowsOfAnarchy ? (
|
||||
<Typography>
|
||||
Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())}
|
||||
<b>Price multiplier:</b> x {numeralWrapper.formatReallyBigNumber(getGenericAugmentationPriceMultiplier())}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
@ -176,42 +143,78 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={props.routeToMainPage}>Back</Button>
|
||||
<Typography variant="h4">Faction Augmentations</Typography>
|
||||
<Typography>
|
||||
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
|
||||
powerful upgrades that will enhance your abilities.
|
||||
<br />
|
||||
Reputation: <Reputation reputation={props.faction.playerReputation} /> Favor:{" "}
|
||||
<Favor favor={Math.floor(props.faction.favor)} />
|
||||
</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Button onClick={props.routeToMainPage}>Back</Button>
|
||||
<Typography variant="h4">Faction Augmentations - {props.faction.name}</Typography>
|
||||
<Paper sx={{ p: 1, mb: 1 }}>
|
||||
<Typography>
|
||||
These are all of the Augmentations that are available to purchase from <b>{props.faction.name}</b>.
|
||||
Augmentations are powerful upgrades that will enhance your abilities.
|
||||
<br />
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${props.faction.name === FactionNames.ShadowsOfAnarchy ? "2" : "3"}, 1fr)`,
|
||||
justifyItems: "center",
|
||||
my: 1,
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The price of every Augmentation increases for every queued Augmentation and it is reset when you
|
||||
install them.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
{multiplierComponent}
|
||||
</Tooltip>
|
||||
<Typography>
|
||||
The price of every Augmentation increases for every queued Augmentation and it is reset when you install
|
||||
them.
|
||||
<b>Reputation:</b> <Reputation reputation={props.faction.playerReputation} />
|
||||
</Typography>
|
||||
<Typography>
|
||||
<b>Favor:</b> <Favor favor={Math.floor(props.faction.favor)} />
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>
|
||||
Sort by Reputation
|
||||
</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>
|
||||
Sort by Default Order
|
||||
</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
|
||||
Sort by Purchasable
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
|
||||
<PurchasableAugmentations
|
||||
augNames={purchasable}
|
||||
ownedAugNames={owned}
|
||||
player={player}
|
||||
canPurchase={(player, aug) => {
|
||||
const costs = aug.getCost(player);
|
||||
return (
|
||||
hasAugmentationPrereqs(aug) &&
|
||||
props.faction.playerReputation >= costs.repCost &&
|
||||
(costs.moneyCost === 0 || player.money > costs.moneyCost)
|
||||
);
|
||||
}}
|
||||
purchaseAugmentation={(player, aug, showModal) => {
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
showModal(true);
|
||||
} else {
|
||||
purchaseAugmentation(aug, props.faction);
|
||||
rerender();
|
||||
}
|
||||
>
|
||||
{multiplierComponent}
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>Sort by Default Order</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
|
||||
Sort by Purchasable
|
||||
</Button>
|
||||
<br />
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{augListElems}</TableBody>
|
||||
</Table>
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{ownedElem}</TableBody>
|
||||
</Table>
|
||||
}}
|
||||
rep={props.faction.playerReputation}
|
||||
faction={props.faction}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -16,12 +16,10 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../Faction";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import { CreateGangModal } from "./CreateGangModal";
|
||||
|
||||
import { Box, Paper, Typography, Button, Tooltip } from "@mui/material";
|
||||
import { Typography, Button } from "@mui/material";
|
||||
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
|
||||
import { FactionNames } from "../data/FactionNames";
|
||||
import { GangConstants } from "../../Gang/data/Constants";
|
||||
import { GangButton } from "./GangButton";
|
||||
|
||||
type IProps = {
|
||||
@ -30,7 +28,6 @@ type IProps = {
|
||||
};
|
||||
|
||||
// Info text for all options on the UI
|
||||
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and faction reputation";
|
||||
const hackingContractsInfo =
|
||||
"Complete hacking contracts for your faction. " +
|
||||
"Your effectiveness, which determines how much " +
|
||||
|
@ -1,170 +0,0 @@
|
||||
/**
|
||||
* React component for displaying a single augmentation for purchase through
|
||||
* the faction UI
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
|
||||
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Faction } from "../Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
|
||||
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import { TableCell } from "../../ui/React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import { getNextNeuroFluxLevel } from "../../Augmentation/AugmentationHelpers";
|
||||
|
||||
interface IReqProps {
|
||||
augName: string;
|
||||
p: IPlayer;
|
||||
hasReq: boolean;
|
||||
rep: number;
|
||||
hasRep: boolean;
|
||||
cost: number;
|
||||
hasCost: boolean;
|
||||
}
|
||||
|
||||
function Requirements(props: IReqProps): React.ReactElement {
|
||||
const aug = Augmentations[props.augName];
|
||||
if (!props.hasReq) {
|
||||
return (
|
||||
<TableCell key={1} colSpan={2}>
|
||||
<Typography color="error">
|
||||
Requires{" "}
|
||||
{aug.prereqs.map((aug, i) => (
|
||||
<AugFormat key={i} name={aug} />
|
||||
))}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key="f">
|
||||
<TableCell key={1}>
|
||||
<Typography>
|
||||
<Money money={props.cost} player={props.p} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key={2}>
|
||||
<Typography color={props.hasRep ? "primary" : "error"}>
|
||||
Requires <Reputation reputation={props.rep} /> faction reputation
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
augName: string;
|
||||
faction: Faction;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
owned?: boolean;
|
||||
}
|
||||
|
||||
export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(false);
|
||||
const aug = Augmentations[props.augName];
|
||||
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
|
||||
|
||||
if (aug == null) {
|
||||
console.error(
|
||||
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const moneyCost = aug.baseCost;
|
||||
const repCost = aug.baseRepRequirement;
|
||||
const hasReq = hasAugmentationPrereqs(aug);
|
||||
const hasRep = props.faction.playerReputation >= repCost;
|
||||
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost;
|
||||
|
||||
// Determine UI properties
|
||||
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
|
||||
|
||||
// Determine button txt
|
||||
let btnTxt = aug.name;
|
||||
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
btnTxt += ` - Level ${getNextNeuroFluxLevel()}`;
|
||||
}
|
||||
|
||||
let tooltip = <></>;
|
||||
if (typeof aug.info === "string") {
|
||||
tooltip = (
|
||||
<>
|
||||
<span>{aug.info}</span>
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
} else
|
||||
tooltip = (
|
||||
<>
|
||||
{aug.info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
function handleClick(): void {
|
||||
if (color === "error") return;
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
purchaseAugmentation(aug, props.faction);
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{!props.owned && (
|
||||
<TableCell key={0}>
|
||||
<Button onClick={handleClick} color={color}>
|
||||
Buy
|
||||
</Button>
|
||||
<PurchaseAugmentationModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aug={aug}
|
||||
faction={props.faction}
|
||||
rerender={props.rerender}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell key={1}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>{tooltip}</Typography>} placement="top">
|
||||
<Typography>{btnTxt}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
{!props.owned && (
|
||||
<Requirements
|
||||
key={2}
|
||||
augName={props.augName}
|
||||
p={props.p}
|
||||
cost={moneyCost}
|
||||
rep={repCost}
|
||||
hasReq={hasReq}
|
||||
hasRep={hasRep}
|
||||
hasCost={hasCost}
|
||||
/>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
@ -70,7 +70,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
<>
|
||||
<OptionsSlider
|
||||
label=".script exec time (ms)"
|
||||
value={execTime}
|
||||
initialValue={execTime}
|
||||
callback={handleExecTimeChange}
|
||||
step={1}
|
||||
min={5}
|
||||
@ -84,7 +84,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Recently killed scripts size"
|
||||
value={recentScriptsSize}
|
||||
initialValue={recentScriptsSize}
|
||||
callback={handleRecentScriptsSizeChange}
|
||||
step={25}
|
||||
min={0}
|
||||
@ -98,7 +98,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Netscript log size"
|
||||
value={logSize}
|
||||
initialValue={logSize}
|
||||
callback={handleLogSizeChange}
|
||||
step={20}
|
||||
min={20}
|
||||
@ -112,7 +112,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Netscript port size"
|
||||
value={portSize}
|
||||
initialValue={portSize}
|
||||
callback={handlePortSizeChange}
|
||||
step={1}
|
||||
min={20}
|
||||
@ -126,7 +126,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Terminal capacity"
|
||||
value={terminalSize}
|
||||
initialValue={terminalSize}
|
||||
callback={handleTerminalSizeChange}
|
||||
step={50}
|
||||
min={50}
|
||||
@ -141,7 +141,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
/>
|
||||
<OptionsSlider
|
||||
label="Autosave interval (s)"
|
||||
value={autosaveInterval}
|
||||
initialValue={autosaveInterval}
|
||||
callback={handleAutosaveIntervalChange}
|
||||
step={30}
|
||||
min={0}
|
||||
@ -179,6 +179,12 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.InfinityLoopSafety}
|
||||
onChange={(newValue) => (Settings.InfinityLoopSafety = newValue)}
|
||||
text="Script infinite loop safety net"
|
||||
tooltip={<>If this is set the game will attempt to automatically kill scripts stuck in infinite loops.</>}
|
||||
/>
|
||||
</GameOptionsPage>
|
||||
),
|
||||
[GameOptionsTab.INTERFACE]: (
|
||||
|
@ -189,9 +189,9 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
|
||||
title={
|
||||
<Typography>
|
||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game.
|
||||
After using this, save the game and then reload the page. This is different then normal kill in that
|
||||
After using this, save the game and then reload the page. This is different than normal kill in that
|
||||
normal kill will tell the script to shut down while force kill just removes the references to it (and it
|
||||
should crash on it's own). This will not remove the files on your computer. Just forcefully kill all
|
||||
should crash on its own). This will not remove the files on your computer, just forcefully kill all
|
||||
running instances of all scripts.
|
||||
</Typography>
|
||||
}
|
||||
@ -210,7 +210,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
|
||||
title={
|
||||
<Typography>
|
||||
If your save file is extremely big you can use this button to view a map of all the files on every server.
|
||||
Be careful there might be spoilers.
|
||||
Be careful: there might be spoilers.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Slider, Tooltip, Typography, Box } from "@mui/material";
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface IProps {
|
||||
value: any;
|
||||
initialValue: any;
|
||||
callback: (event: any, newValue: number | number[]) => void;
|
||||
step: number;
|
||||
min: number;
|
||||
@ -13,14 +13,21 @@ interface IProps {
|
||||
}
|
||||
|
||||
export const OptionsSlider = (props: IProps): React.ReactElement => {
|
||||
const [value, setValue] = useState(props.initialValue);
|
||||
|
||||
const onChange = (_evt: Event, newValue: number | Array<number>): void => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Tooltip title={<Typography>{props.tooltip}</Typography>}>
|
||||
<Typography>{props.label}</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={props.value}
|
||||
onChange={props.callback}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onChangeCommitted={props.callback}
|
||||
step={props.step}
|
||||
min={props.min}
|
||||
max={props.max}
|
||||
|
@ -85,9 +85,15 @@ export function TerritorySubpage(): React.ReactElement {
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)" }}>
|
||||
{gangNames.map((name) => (
|
||||
<OtherGangTerritory key={name} name={name} />
|
||||
))}
|
||||
{gangNames
|
||||
.sort((a, b) => {
|
||||
if (AllGangs[a].territory <= 0 && AllGangs[b].territory > 0) return 1;
|
||||
if (AllGangs[a].territory > 0 && AllGangs[b].territory <= 0) return -1;
|
||||
return 0;
|
||||
})
|
||||
.map((name) => (
|
||||
<OtherGangTerritory key={name} name={name} />
|
||||
))}
|
||||
</Box>
|
||||
<TerritoryInfoModal open={infoOpen} onClose={() => setInfoOpen(false)} />
|
||||
</Container>
|
||||
@ -114,14 +120,16 @@ function OtherGangTerritory(props: ITerritoryProps): React.ReactElement {
|
||||
const playerPower = AllGangs[gang.facName].power;
|
||||
const power = AllGangs[props.name].power;
|
||||
const clashVictoryChance = playerPower / (power + playerPower);
|
||||
const territory = AllGangs[props.name].territory;
|
||||
const opacity = territory ? 1 : 0.75;
|
||||
return (
|
||||
<Box component={Paper} sx={{ p: 1 }}>
|
||||
<Box component={Paper} sx={{ p: 1, opacity }}>
|
||||
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
{props.name}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<b>Power:</b> {formatNumber(power, 3)} <br />
|
||||
<b>Territory:</b> {formatTerritory(AllGangs[props.name].territory)}% <br />
|
||||
<b>Territory:</b> {formatTerritory(territory)}% <br />
|
||||
<b>Clash Win Chance:</b> {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
@ -60,15 +60,22 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||
}
|
||||
|
||||
const increase =
|
||||
const base_increase =
|
||||
calculateHashGainRate(node.level + multiplier, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult) -
|
||||
node.hashRate;
|
||||
calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult);
|
||||
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
|
||||
|
||||
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult);
|
||||
upgradeLevelButton = (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
+<HashRate hashes={increase} />
|
||||
+<HashRate hashes={modded_increase} /> (effective increase, taking current RAM usage into account)
|
||||
<br />
|
||||
<span style={{ opacity: 0.5 }}>
|
||||
+<HashRate hashes={base_increase} />
|
||||
</span>{" "}
|
||||
(base increase, attained when no script is running)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
@ -109,20 +116,36 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||
}
|
||||
|
||||
const increase =
|
||||
const base_increase =
|
||||
calculateHashGainRate(
|
||||
node.level,
|
||||
0,
|
||||
node.maxRam * Math.pow(2, multiplier),
|
||||
node.cores,
|
||||
props.player.hacknet_node_money_mult,
|
||||
) - node.hashRate;
|
||||
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult);
|
||||
|
||||
const modded_increase =
|
||||
calculateHashGainRate(
|
||||
node.level,
|
||||
node.ramUsed,
|
||||
node.maxRam * Math.pow(2, multiplier),
|
||||
node.cores,
|
||||
props.player.hacknet_node_money_mult,
|
||||
) -
|
||||
calculateHashGainRate(node.level, node.ramUsed, node.maxRam, node.cores, props.player.hacknet_node_money_mult);
|
||||
|
||||
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult);
|
||||
upgradeRamButton = (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
+<HashRate hashes={increase} />
|
||||
+<HashRate hashes={modded_increase} /> (effective increase, taking current RAM usage into account)
|
||||
<br />
|
||||
<span style={{ opacity: 0.5 }}>
|
||||
+<HashRate hashes={base_increase} />
|
||||
</span>{" "}
|
||||
(base increase, attained when no script is running)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
@ -155,15 +178,22 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||
}
|
||||
|
||||
const increase =
|
||||
const base_increase =
|
||||
calculateHashGainRate(node.level, 0, node.maxRam, node.cores + multiplier, props.player.hacknet_node_money_mult) -
|
||||
node.hashRate;
|
||||
calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult);
|
||||
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
|
||||
|
||||
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult);
|
||||
upgradeCoresButton = (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
+<HashRate hashes={increase} />
|
||||
+<HashRate hashes={modded_increase} /> (effective increase, taking current RAM usage into account)
|
||||
<br />
|
||||
<span style={{ opacity: 0.5 }}>
|
||||
+<HashRate hashes={base_increase} />
|
||||
</span>{" "}
|
||||
(base increase, attained when no script is running)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
@ -232,9 +262,31 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||
<Typography>Production:</Typography>
|
||||
</TableCell>
|
||||
<TableCell colSpan={2}>
|
||||
<Typography>
|
||||
<Hashes hashes={node.totalHashesGenerated} /> (<HashRate hashes={node.hashRate} />)
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
<Hashes hashes={node.totalHashesGenerated} /> hashes produced by this server since last augment
|
||||
installation.
|
||||
<br />
|
||||
<HashRate hashes={node.hashRate} /> current production rate.
|
||||
<br />
|
||||
<span style={{ opacity: 0.5 }}>
|
||||
<HashRate hashes={(node.hashRate * node.maxRam) / (node.maxRam - node.ramUsed)} />
|
||||
</span>{" "}
|
||||
max production rate. (achieved when 100% RAM is allocated to it)
|
||||
<br />
|
||||
{numeralWrapper.formatRAM(node.ramUsed)} / {numeralWrapper.formatRAM(node.maxRam)} (
|
||||
{Math.round((100 * node.ramUsed) / node.maxRam)}%) RAM allocated to script.
|
||||
<br />
|
||||
{numeralWrapper.formatRAM(node.maxRam - node.ramUsed)} / {numeralWrapper.formatRAM(node.maxRam)} (
|
||||
{Math.round((100 * (node.maxRam - node.ramUsed)) / node.maxRam)}%) RAM allocated to hash production.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
<Hashes hashes={node.totalHashesGenerated} /> (<HashRate hashes={node.hashRate} />)
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
@ -31,12 +31,12 @@ export function calculateTradeInformationRepReward(
|
||||
const levelBonus = maxLevel * Math.pow(1.01, maxLevel);
|
||||
|
||||
return (
|
||||
Math.pow(reward + 1, 2) *
|
||||
Math.pow(difficulty, 3) *
|
||||
3e3 *
|
||||
Math.pow(reward + 1, 1.1) *
|
||||
Math.pow(difficulty, 1.2) *
|
||||
30 *
|
||||
levelBonus *
|
||||
(player.hasAugmentation(AugmentationNames.WKSharmonizer, true) ? 1.5 : 1) *
|
||||
BitNodeMultipliers.InfiltrationMoney
|
||||
BitNodeMultipliers.InfiltrationRep
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { random } from "../utils";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { Player } from "../../Player";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -38,9 +37,13 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
const [guess, setGuess] = useState("");
|
||||
const hasAugment = Player.hasAugmentation(AugmentationNames.ChaosOfDionysus, true);
|
||||
|
||||
function ignorableKeyboardEvent(event: KeyboardEvent): boolean {
|
||||
return event.key === KEY.BACKSPACE || (event.shiftKey && event.key === "Shift") || event.ctrlKey || event.altKey;
|
||||
}
|
||||
|
||||
function press(this: Document, event: KeyboardEvent): void {
|
||||
event.preventDefault();
|
||||
if (event.key === KEY.BACKSPACE) return;
|
||||
if (ignorableKeyboardEvent(event)) return;
|
||||
const nextGuess = guess + event.key.toUpperCase();
|
||||
if (!answer.startsWith(nextGuess)) props.onFailure();
|
||||
else if (answer === nextGuess) props.onSuccess();
|
||||
@ -48,24 +51,18 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
||||
<Typography variant="h4">Type it{!hasAugment ? " backward" : ""}</Typography>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography style={{ transform: hasAugment ? "none" : "scaleX(-1)", marginLeft: hasAugment ? "50%" : "none" }}>
|
||||
{answer}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography style={{ transform: hasAugment ? "none" : "scaleX(-1)" }}>{answer}</Typography>
|
||||
<Typography>
|
||||
{guess}
|
||||
<BlinkingCursor />
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export function BlinkingCursor(): React.ReactElement {
|
||||
const [on, setOn] = useState(true);
|
||||
@ -6,5 +6,5 @@ export function BlinkingCursor(): React.ReactElement {
|
||||
const i = setInterval(() => setOn((old) => !old), 1000);
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
return <>{on ? "|" : ""}</>;
|
||||
return <>{on ? "|" : <> </>}</>;
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { random } from "../utils";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Player } from "../../Player";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -84,16 +83,16 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||
<Typography variant="h4">Close the brackets</Typography>
|
||||
<Typography style={{ fontSize: "5em" }}>
|
||||
{`${left}${right}`}
|
||||
<BlinkingCursor />
|
||||
</Typography>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { downArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -88,13 +87,11 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4">Say something nice about the guard.</Typography>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||
<Typography variant="h4">Say something nice about the guard</Typography>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="h5" color={upColor}>
|
||||
{upArrowSymbol}
|
||||
</Typography>
|
||||
@ -104,8 +101,8 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
||||
<Typography variant="h5" color={downColor}>
|
||||
{downArrowSymbol}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -154,6 +151,7 @@ const positive = [
|
||||
"patient",
|
||||
"dynamic",
|
||||
"loyal",
|
||||
"based",
|
||||
];
|
||||
|
||||
const negative = [
|
||||
@ -177,4 +175,5 @@ const negative = [
|
||||
"picky",
|
||||
"tactless",
|
||||
"thoughtless",
|
||||
"cringe",
|
||||
];
|
||||
|
@ -1,21 +1,20 @@
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import {
|
||||
random,
|
||||
downArrowSymbol,
|
||||
getArrow,
|
||||
getInverseArrow,
|
||||
leftArrowSymbol,
|
||||
random,
|
||||
rightArrowSymbol,
|
||||
upArrowSymbol,
|
||||
downArrowSymbol,
|
||||
} from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -55,14 +54,14 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||
<Typography variant="h4">Enter the Code!</Typography>
|
||||
<Typography variant="h4">{code[index]}</Typography>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
interface IProps {
|
||||
onFinish: () => void;
|
||||
}
|
||||
@ -13,17 +12,13 @@ export function Countdown(props: IProps): React.ReactElement {
|
||||
props.onFinish();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => setX(x - 1), 200);
|
||||
setTimeout(() => setX(x - 1), 300);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4">Get Ready!</Typography>
|
||||
<Typography variant="h4">{x}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
<Paper sx={{ p: 1, textAlign: "center" }}>
|
||||
<Typography variant="h4">Get Ready!</Typography>
|
||||
<Typography variant="h4">{x}</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Paper, Typography, Box } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -19,6 +18,12 @@ interface Difficulty {
|
||||
symbols: number;
|
||||
}
|
||||
|
||||
interface GridItem {
|
||||
content: string;
|
||||
color: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
@ -76,18 +81,33 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
const flatGrid: GridItem[] = [];
|
||||
grid.map((line, y) =>
|
||||
line.map((cell, x) => {
|
||||
const isCorrectAnswer = cell === answers[currentAnswerIndex];
|
||||
const optionColor = hasAugment && !isCorrectAnswer ? Settings.theme.disabled : Settings.theme.primary;
|
||||
|
||||
if (x === pos[0] && y === pos[1]) {
|
||||
flatGrid.push({ color: optionColor, content: cell, selected: true });
|
||||
return;
|
||||
}
|
||||
|
||||
flatGrid.push({ color: optionColor, content: cell });
|
||||
}),
|
||||
);
|
||||
|
||||
const fontSize = "2em";
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
||||
<Typography variant="h4">Match the symbols!</Typography>
|
||||
<Typography variant="h5" color={Settings.theme.primary}>
|
||||
Targets:{" "}
|
||||
{answers.map((a, i) => {
|
||||
if (i == currentAnswerIndex)
|
||||
return (
|
||||
<span key={`${i}`} style={{ fontSize: "1em", color: "blue" }}>
|
||||
<span key={`${i}`} style={{ fontSize: "1em", color: Settings.theme.infolight }}>
|
||||
{a}
|
||||
</span>
|
||||
);
|
||||
@ -99,34 +119,30 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
||||
})}
|
||||
</Typography>
|
||||
<br />
|
||||
{grid.map((line, y) => (
|
||||
<div key={y}>
|
||||
<Typography>
|
||||
{line.map((cell, x) => {
|
||||
const isCorrectAnswer = cell === answers[currentAnswerIndex];
|
||||
|
||||
if (x == pos[0] && y == pos[1]) {
|
||||
return (
|
||||
<span key={`${x}${y}`} style={{ fontSize: fontSize, color: "blue" }}>
|
||||
{cell}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const optionColor = hasAugment && !isCorrectAnswer ? Settings.theme.disabled : Settings.theme.primary;
|
||||
return (
|
||||
<span key={`${x}${y}`} style={{ fontSize: fontSize, color: optionColor }}>
|
||||
{cell}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${Math.round(difficulty.width)}, 1fr)`,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
{flatGrid.map((item) => (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: fontSize,
|
||||
color: item.color,
|
||||
border: item.selected ? `2px solid ${Settings.theme.infolight}` : "unset",
|
||||
lineHeight: "unset",
|
||||
p: item.selected ? "2px" : "4px",
|
||||
}}
|
||||
>
|
||||
{item.content}
|
||||
</Typography>
|
||||
<br />
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</Box>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,17 @@
|
||||
import { use } from "../../ui/Context";
|
||||
import { Button, Container, Paper, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Countdown } from "./Countdown";
|
||||
import { BracketGame } from "./BracketGame";
|
||||
import { SlashGame } from "./SlashGame";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { use } from "../../ui/Context";
|
||||
import { BackwardGame } from "./BackwardGame";
|
||||
import { BracketGame } from "./BracketGame";
|
||||
import { BribeGame } from "./BribeGame";
|
||||
import { CheatCodeGame } from "./CheatCodeGame";
|
||||
import { Countdown } from "./Countdown";
|
||||
import { Cyberpunk2077Game } from "./Cyberpunk2077Game";
|
||||
import { MinesweeperGame } from "./MinesweeperGame";
|
||||
import { WireCuttingGame } from "./WireCuttingGame";
|
||||
import { SlashGame } from "./SlashGame";
|
||||
import { Victory } from "./Victory";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { WireCuttingGame } from "./WireCuttingGame";
|
||||
|
||||
interface IProps {
|
||||
StartingDifficulty: number;
|
||||
@ -139,22 +137,20 @@ export function Game(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<Button onClick={cancel}>Cancel</Button>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography>
|
||||
Level: {level} / {props.MaxLevel}
|
||||
</Typography>
|
||||
<Progress />
|
||||
</Grid>
|
||||
<Container>
|
||||
<Paper sx={{ p: 1, mb: 1, display: "grid", justifyItems: "center", gap: 1 }}>
|
||||
{stage !== Stage.Sell && (
|
||||
<Button sx={{ width: "100%" }} onClick={cancel}>
|
||||
Cancel Infiltration
|
||||
</Button>
|
||||
)}
|
||||
<Typography variant="h5">
|
||||
Level {level} / {props.MaxLevel}
|
||||
</Typography>
|
||||
<Progress />
|
||||
</Paper>
|
||||
|
||||
<Grid item xs={12}>
|
||||
{stageComponent}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
{stageComponent}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +1,13 @@
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Paper } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
const TimerProgress = withStyles((theme: Theme) => ({
|
||||
root: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
bar: {
|
||||
transition: "none",
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
}))(LinearProgress);
|
||||
import { use } from "../../ui/Context";
|
||||
import { ProgressBar } from "../../ui/React/Progress";
|
||||
|
||||
interface IProps {
|
||||
millis: number;
|
||||
onExpire: () => void;
|
||||
noPaper?: boolean;
|
||||
}
|
||||
|
||||
export function GameTimer(props: IProps): React.ReactElement {
|
||||
@ -42,9 +31,11 @@ export function GameTimer(props: IProps): React.ReactElement {
|
||||
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||
// bar physically reaches the end
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<TimerProgress variant="determinate" value={v} color="primary" />
|
||||
</Grid>
|
||||
return props.noPaper ? (
|
||||
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||
) : (
|
||||
<Paper sx={{ p: 1, mb: 1 }}>
|
||||
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { useState } from "react";
|
||||
import { Intro } from "./Intro";
|
||||
import { Game } from "./Game";
|
||||
import { Location } from "../../Locations/Location";
|
||||
import { use } from "../../ui/Context";
|
||||
import { calculateDifficulty, calculateReward } from "../formulas/game";
|
||||
import { Game } from "./Game";
|
||||
import { Intro } from "./Intro";
|
||||
interface IProps {
|
||||
location: Location;
|
||||
}
|
||||
@ -22,24 +22,24 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
|
||||
router.toCity();
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
return (
|
||||
<Intro
|
||||
Location={props.location}
|
||||
Difficulty={difficulty}
|
||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
||||
start={() => setStart(true)}
|
||||
cancel={cancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Game
|
||||
StartingDifficulty={startingSecurityLevel}
|
||||
Difficulty={difficulty}
|
||||
Reward={reward}
|
||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center", height: "calc(100vh - 16px)" }}>
|
||||
{start ? (
|
||||
<Game
|
||||
StartingDifficulty={startingSecurityLevel}
|
||||
Difficulty={difficulty}
|
||||
Reward={reward}
|
||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
||||
/>
|
||||
) : (
|
||||
<Intro
|
||||
Location={props.location}
|
||||
Difficulty={difficulty}
|
||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
||||
start={() => setStart(true)}
|
||||
cancel={cancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Report } from "@mui/icons-material";
|
||||
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Location } from "../../Locations/Location";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
interface IProps {
|
||||
@ -41,9 +41,9 @@ function coloredArrow(difficulty: number): JSX.Element {
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{arrowPart("white", difficulty * 13)}
|
||||
{arrowPart("orange", (difficulty - 1) * 13)}
|
||||
{arrowPart("red", (difficulty - 2) * 13)}
|
||||
{arrowPart(Settings.theme.primary, difficulty * 13)}
|
||||
{arrowPart(Settings.theme.warning, (difficulty - 1) * 13)}
|
||||
{arrowPart(Settings.theme.error, (difficulty - 2) * 13)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -51,65 +51,84 @@ function coloredArrow(difficulty: number): JSX.Element {
|
||||
|
||||
export function Intro(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h4">Infiltrating {props.Location.name}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
Maximum level: {props.MaxLevel}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
Difficulty: {numeralWrapper.format(props.Difficulty * 33.3333, "0")} / 100
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Container sx={{ alignItems: "center" }}>
|
||||
<Paper sx={{ p: 1, mb: 1, display: "grid", justifyItems: "center" }}>
|
||||
<Typography variant="h4">
|
||||
Infiltrating <b>{props.Location.name}</b>
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
<b>Maximum Level: </b>
|
||||
{props.MaxLevel}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color:
|
||||
props.Difficulty > 2
|
||||
? Settings.theme.error
|
||||
: props.Difficulty > 1
|
||||
? Settings.theme.warning
|
||||
: Settings.theme.primary,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<b>Difficulty: </b>
|
||||
{numeralWrapper.format(props.Difficulty * 33.3333, "0")} / 100
|
||||
{props.Difficulty > 1.5 && (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography color="error">
|
||||
This location is too heavily guarded for your current stats. It is recommended that you try training,
|
||||
or finding an easier location.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Report sx={{ ml: 1 }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{props.Difficulty > 1.5 && (
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
Warning: This location is too heavily guarded for your current stats, try training or finding an easier
|
||||
location.
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
|
||||
<Typography
|
||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||
>{`▲ ▲ ▲ ▲`}</Typography>
|
||||
<Typography
|
||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||
>{` Trivial Normal Hard Impossible`}</Typography>
|
||||
</Paper>
|
||||
|
||||
<Grid item xs={10}>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
|
||||
<Typography
|
||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||
>{` ^ ^ ^ ^`}</Typography>
|
||||
<Typography
|
||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||
>{` Trivial Normal Hard Impossible`}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Paper sx={{ p: 1, display: "grid", justifyItems: "center" }}>
|
||||
<Typography sx={{ width: "75%", textAlign: "center" }}>
|
||||
<b>Infiltration</b> is a series of short minigames that get progressively harder. You take damage for failing
|
||||
them. Reaching the maximum level rewards you with intel that you can trade for money or reputation.
|
||||
<br />
|
||||
<br />
|
||||
<b>Gameplay:</b>
|
||||
</Typography>
|
||||
<ul>
|
||||
<Typography>
|
||||
Infiltration is a series of short minigames that get progressively harder. You take damage for failing them.
|
||||
Reaching the maximum level rewards you with intel you can trade for money or reputation.
|
||||
<li>
|
||||
The minigames you play are randomly selected.
|
||||
<br />
|
||||
It might take you a few tries to get used to them.
|
||||
</li>
|
||||
<li>No game requires use of the mouse.</li>
|
||||
<li>
|
||||
<b>Spacebar</b> is the default action/confirm button.
|
||||
</li>
|
||||
<li>
|
||||
The <b>arrow keys</b> and <b>WASD</b> can be used interchangeably.
|
||||
</li>
|
||||
<li>Sometimes the rest of the keyboard is used.</li>
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
The minigames you play are randomly selected. It might take you few tries to get used to them.
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>No game require use of the mouse.</Typography>
|
||||
<br />
|
||||
<Typography>Spacebar is the default action/confirm button.</Typography>
|
||||
<br />
|
||||
<Typography>Everything that uses arrow can also use WASD</Typography>
|
||||
<br />
|
||||
<Typography>Sometimes the rest of the keyboard is used.</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
</ul>
|
||||
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", width: "100%" }}>
|
||||
<Button onClick={props.start}>Start</Button>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button onClick={props.cancel}>Cancel</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,16 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { Close, Flag, Report } from "@mui/icons-material";
|
||||
import { Box, Paper, Typography } from "@mui/material";
|
||||
import { uniqueId } from "lodash";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -81,32 +83,77 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const flatGrid: { flagged?: boolean; current?: boolean; marked?: boolean }[] = [];
|
||||
|
||||
minefield.map((line, y) =>
|
||||
line.map((cell, x) => {
|
||||
if (memoryPhase) {
|
||||
flatGrid.push({ flagged: Boolean(minefield[y][x]) });
|
||||
return;
|
||||
} else if (x === pos[0] && y === pos[1]) {
|
||||
flatGrid.push({ current: true });
|
||||
} else if (answer[y][x]) {
|
||||
flatGrid.push({ marked: true });
|
||||
} else if (hasAugment && minefield[y][x]) {
|
||||
flatGrid.push({ flagged: true });
|
||||
} else {
|
||||
flatGrid.push({});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
||||
<Typography variant="h4">{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}</Typography>
|
||||
{minefield.map((line, y) => (
|
||||
<div key={y}>
|
||||
<Typography>
|
||||
{line.map((cell, x) => {
|
||||
if (memoryPhase) {
|
||||
if (minefield[y][x]) return <span key={x}>[?] </span>;
|
||||
return <span key={x}>[ ] </span>;
|
||||
} else {
|
||||
if (x == pos[0] && y == pos[1]) return <span key={x}>[X] </span>;
|
||||
if (answer[y][x]) return <span key={x}>[.] </span>;
|
||||
if (hasAugment && minefield[y][x]) return <span key={x}>[?] </span>;
|
||||
return <span key={x}>[ ] </span>;
|
||||
}
|
||||
})}
|
||||
</Typography>
|
||||
<br />
|
||||
</div>
|
||||
))}
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${Math.round(difficulty.width)}, 1fr)`,
|
||||
gridTemplateRows: `repeat(${Math.round(difficulty.height)}, 1fr)`,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
{flatGrid.map((item) => {
|
||||
let color: string;
|
||||
let icon: React.ReactElement;
|
||||
|
||||
if (item.marked) {
|
||||
color = Settings.theme.warning;
|
||||
icon = <Flag />;
|
||||
} else if (item.current) {
|
||||
color = Settings.theme.infolight;
|
||||
icon = <Close />;
|
||||
} else if (item.flagged) {
|
||||
color = Settings.theme.error;
|
||||
icon = <Report />;
|
||||
} else {
|
||||
color = Settings.theme.primary;
|
||||
icon = <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography
|
||||
key={`${item}${uniqueId()}`}
|
||||
sx={{
|
||||
color: color,
|
||||
border: `2px solid ${item.current ? Settings.theme.infolight : Settings.theme.primary}`,
|
||||
height: "32px",
|
||||
width: "32px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</Typography>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Box, Paper, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { Player } from "../../Player";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -59,23 +58,25 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||
<Typography variant="h4">Slash when his guard is down!</Typography>
|
||||
|
||||
{hasAugment ? (
|
||||
<>
|
||||
<Typography variant="h4">Guard will drop in...</Typography>
|
||||
<GameTimer millis={timeUntilAttacking} onExpire={props.onFailure} />
|
||||
</>
|
||||
<Box sx={{ my: 1 }}>
|
||||
<Typography variant="h5">Guard will drop in...</Typography>
|
||||
<GameTimer millis={timeUntilAttacking} onExpire={() => null} noPaper />
|
||||
</Box>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{phase === 0 && <Typography variant="h4">Guarding ...</Typography>}
|
||||
{phase === 1 && <Typography variant="h4">Preparing?</Typography>}
|
||||
{phase === 2 && <Typography variant="h4">ATTACKING!</Typography>}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,17 @@
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { Box, Button, MenuItem, Paper, Select, SelectChangeEvent, Typography } from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { FactionNames } from "../../Faction/data/FactionNames";
|
||||
import { inviteToFaction } from "../../Faction/FactionHelpers";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { use } from "../../ui/Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { FactionNames } from "../../Faction/data/FactionNames";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import {
|
||||
calculateInfiltratorsRepReward,
|
||||
calculateSellInformationCashReward,
|
||||
calculateTradeInformationRepReward,
|
||||
} from "../formulas/victory";
|
||||
import { inviteToFaction } from "../../Faction/FactionHelpers";
|
||||
|
||||
interface IProps {
|
||||
StartingDifficulty: number;
|
||||
@ -66,24 +62,22 @@ export function Victory(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h4">Infiltration successful!</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5" color="primary">
|
||||
You{" "}
|
||||
{isMemberOfInfiltrators ? (
|
||||
<>
|
||||
have gained {formatNumber(infiltrationRepGain, 2)} rep for {FactionNames.ShadowsOfAnarchy} and{" "}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
can trade the confidential information you found for money or reputation.
|
||||
</Typography>
|
||||
<Select value={faction} onChange={changeDropdown}>
|
||||
<Paper sx={{ p: 1, textAlign: "center", display: "flex", alignItems: "center", flexDirection: "column" }}>
|
||||
<Typography variant="h4">Infiltration successful!</Typography>
|
||||
<Typography variant="h5" color="primary" width="75%">
|
||||
You{" "}
|
||||
{isMemberOfInfiltrators ? (
|
||||
<>
|
||||
have gained {formatNumber(infiltrationRepGain, 2)} rep for {FactionNames.ShadowsOfAnarchy} and{" "}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
can trade the confidential information you found for money or reputation.
|
||||
</Typography>
|
||||
<Box sx={{ width: "fit-content" }}>
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Select value={faction} onChange={changeDropdown} sx={{ mr: 1 }}>
|
||||
<MenuItem key={"none"} value={"none"}>
|
||||
{"none"}
|
||||
</MenuItem>
|
||||
@ -98,17 +92,15 @@ export function Victory(props: IProps): React.ReactElement {
|
||||
<Button onClick={trade}>
|
||||
Trade for <Reputation reputation={repGain} /> reputation
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button onClick={sell}>
|
||||
Sell for
|
||||
<Money money={moneyGain} />
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Button onClick={quitInfiltration}>Quit</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
</Box>
|
||||
<Button onClick={sell} sx={{ width: "100%" }}>
|
||||
Sell for
|
||||
<Money money={moneyGain} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Button onClick={quitInfiltration} sx={{ width: "100%", mt: 1 }}>
|
||||
Quit
|
||||
</Button>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { Box, Paper, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
@ -102,46 +101,53 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4">Cut the wires with the following properties! (keyboard 1 to 9)</Typography>
|
||||
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
||||
<Typography variant="h4" sx={{ width: "75%", textAlign: "center" }}>
|
||||
Cut the wires with the following properties! (keyboard 1 to 9)
|
||||
</Typography>
|
||||
{questions.map((question, i) => (
|
||||
<Typography key={i}>{question.toString()}</Typography>
|
||||
))}
|
||||
<Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${wires.length}, 1fr)`,
|
||||
columnGap: 3,
|
||||
justifyItems: "center",
|
||||
}}
|
||||
>
|
||||
{new Array(wires.length).fill(0).map((_, i) => {
|
||||
const isCorrectWire = checkWire(i + 1);
|
||||
const color = hasAugment && !isCorrectWire ? Settings.theme.disabled : Settings.theme.primary;
|
||||
return (
|
||||
<span key={i} style={{ color: color }}>
|
||||
{i + 1}
|
||||
</span>
|
||||
<Typography key={i} style={{ color: color }}>
|
||||
{i + 1}
|
||||
</Typography>
|
||||
);
|
||||
})}
|
||||
</Typography>
|
||||
{new Array(8).fill(0).map((_, i) => (
|
||||
<div key={i}>
|
||||
<Typography>
|
||||
{new Array(8).fill(0).map((_, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{wires.map((wire, j) => {
|
||||
if ((i === 3 || i === 4) && cutWires[j]) {
|
||||
return <span key={j}> </span>;
|
||||
return <Typography key={j}></Typography>;
|
||||
}
|
||||
const isCorrectWire = checkWire(j + 1);
|
||||
const wireColor =
|
||||
hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length];
|
||||
return (
|
||||
<span key={j} style={{ color: wireColor }}>
|
||||
|{wire.tpe}|
|
||||
</span>
|
||||
<Typography key={j} style={{ color: wireColor }}>
|
||||
|{wire.tpe}|
|
||||
</Typography>
|
||||
);
|
||||
})}
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Box>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { ITutorialEvents } from "./ui/InteractiveTutorial/ITutorialEvents";
|
||||
// Ordered array of keys to Interactive Tutorial Steps
|
||||
enum iTutorialSteps {
|
||||
Start,
|
||||
NSSelection,
|
||||
GoToCharacterPage, // Click on 'Stats' page
|
||||
CharacterPage, // Introduction to 'Stats' page
|
||||
CharacterGoToTerminalPage, // Go back to Terminal
|
||||
@ -43,6 +44,7 @@ const ITutorial: {
|
||||
isRunning: boolean;
|
||||
stepIsDone: {
|
||||
[iTutorialSteps.Start]: boolean;
|
||||
[iTutorialSteps.NSSelection]: boolean;
|
||||
[iTutorialSteps.GoToCharacterPage]: boolean;
|
||||
[iTutorialSteps.CharacterPage]: boolean;
|
||||
[iTutorialSteps.CharacterGoToTerminalPage]: boolean;
|
||||
@ -80,6 +82,7 @@ const ITutorial: {
|
||||
// Keeps track of whether each step has been done
|
||||
stepIsDone: {
|
||||
[iTutorialSteps.Start]: false,
|
||||
[iTutorialSteps.NSSelection]: false,
|
||||
[iTutorialSteps.GoToCharacterPage]: false,
|
||||
[iTutorialSteps.CharacterPage]: false,
|
||||
[iTutorialSteps.CharacterGoToTerminalPage]: false,
|
||||
|
@ -2,11 +2,11 @@
|
||||
* Location and traveling-related helper functions.
|
||||
* Mostly used for UI
|
||||
*/
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { AddToAllServers, createUniqueRandomIp } from "../Server/AllServers";
|
||||
import { safetlyCreateUniqueServer } from "../Server/ServerHelpers";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
|
||||
@ -25,19 +25,14 @@ export function purchaseTorRouter(p: IPlayer): void {
|
||||
}
|
||||
p.loseMoney(CONSTANTS.TorRouterCost, "other");
|
||||
|
||||
const darkweb = safetlyCreateUniqueServer({
|
||||
ip: createUniqueRandomIp(),
|
||||
hostname: "darkweb",
|
||||
organizationName: "",
|
||||
isConnectedTo: false,
|
||||
adminRights: false,
|
||||
purchasedByPlayer: false,
|
||||
maxRam: 1,
|
||||
});
|
||||
AddToAllServers(darkweb);
|
||||
const darkweb = GetServer(SpecialServers.DarkWeb);
|
||||
if (!darkweb) {
|
||||
throw new Error("Dark web is not a server.");
|
||||
}
|
||||
|
||||
p.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
|
||||
darkweb.serversOnNetwork.push(p.getHomeComputer().hostname);
|
||||
console.log(darkweb);
|
||||
dialogBoxCreate(
|
||||
"You have purchased a TOR router!<br>" +
|
||||
"You now have access to the dark web from your home computer.<br>" +
|
||||
|
@ -17,6 +17,7 @@ import { Money } from "../../ui/React/Money";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
import { Box } from "@mui/material";
|
||||
import { ClassType } from "../../utils/WorkType";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -33,7 +34,7 @@ export function GymLocation(props: IProps): React.ReactElement {
|
||||
return props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
function train(stat: string): void {
|
||||
function train(stat: ClassType): void {
|
||||
const loc = props.loc;
|
||||
props.p.startClass(calculateCost(), loc.expMult, stat);
|
||||
props.p.startFocusing();
|
||||
@ -41,19 +42,19 @@ export function GymLocation(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function trainStrength(): void {
|
||||
train(CONSTANTS.ClassGymStrength);
|
||||
train(ClassType.GymStrength);
|
||||
}
|
||||
|
||||
function trainDefense(): void {
|
||||
train(CONSTANTS.ClassGymDefense);
|
||||
train(ClassType.GymDefense);
|
||||
}
|
||||
|
||||
function trainDexterity(): void {
|
||||
train(CONSTANTS.ClassGymDexterity);
|
||||
train(ClassType.GymDexterity);
|
||||
}
|
||||
|
||||
function trainAgility(): void {
|
||||
train(CONSTANTS.ClassGymAgility);
|
||||
train(ClassType.GymAgility);
|
||||
}
|
||||
|
||||
const cost = CONSTANTS.ClassGymBaseCost * calculateCost();
|
||||
|
@ -32,7 +32,7 @@ export function RamButton(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
const bnMult = BitNodeMultipliers.HomeComputerRamCost === 1 ? "" : `\\cdot ${BitNodeMultipliers.HomeComputerRamCost}`;
|
||||
console.log(BitNodeMultipliers.HomeComputerRamCost);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
|
@ -317,7 +317,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
return renderGrafting();
|
||||
}
|
||||
case LocationName.Sector12CityHall: {
|
||||
return (BitNodeMultipliers.CorporationSoftCap < 0.15 && <></>) || <CreateCorporation />;
|
||||
return (BitNodeMultipliers.CorporationSoftcap < 0.15 && <></>) || <CreateCorporation />;
|
||||
}
|
||||
case LocationName.Sector12NSA: {
|
||||
return renderBladeburner();
|
||||
|
@ -17,6 +17,8 @@ import { Money } from "../../ui/React/Money";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
import { ClassType } from "../../utils/WorkType";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
};
|
||||
@ -32,7 +34,7 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
||||
return props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
function take(stat: string): void {
|
||||
function take(stat: ClassType): void {
|
||||
const loc = props.loc;
|
||||
player.startClass(calculateCost(), loc.expMult, stat);
|
||||
player.startFocusing();
|
||||
@ -40,27 +42,27 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function study(): void {
|
||||
take(CONSTANTS.ClassStudyComputerScience);
|
||||
take(ClassType.StudyComputerScience);
|
||||
}
|
||||
|
||||
function dataStructures(): void {
|
||||
take(CONSTANTS.ClassDataStructures);
|
||||
take(ClassType.DataStructures);
|
||||
}
|
||||
|
||||
function networks(): void {
|
||||
take(CONSTANTS.ClassNetworks);
|
||||
take(ClassType.Networks);
|
||||
}
|
||||
|
||||
function algorithms(): void {
|
||||
take(CONSTANTS.ClassAlgorithms);
|
||||
take(ClassType.Algorithms);
|
||||
}
|
||||
|
||||
function management(): void {
|
||||
take(CONSTANTS.ClassManagement);
|
||||
take(ClassType.Management);
|
||||
}
|
||||
|
||||
function leadership(): void {
|
||||
take(CONSTANTS.ClassLeadership);
|
||||
take(ClassType.Leadership);
|
||||
}
|
||||
|
||||
const costMult: number = calculateCost();
|
||||
|
@ -5,6 +5,8 @@ import type { WorkerScript } from "./WorkerScript";
|
||||
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
|
||||
import { Player } from "../Player";
|
||||
import { CityName } from "src/Locations/data/CityNames";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
type ExternalFunction = (...args: any[]) => any;
|
||||
type ExternalAPI = {
|
||||
@ -91,8 +93,14 @@ function wrapFunction(
|
||||
getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
|
||||
},
|
||||
};
|
||||
const safetyEnabled = Settings.InfinityLoopSafety;
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
helpers.updateDynamicRam(ctx.function, getRamCost(Player, ...tree, ctx.function));
|
||||
if (safetyEnabled) workerScript.infiniteLoopSafetyCounter++;
|
||||
if (workerScript.infiniteLoopSafetyCounter > CONSTANTS.InfiniteLoopLimit)
|
||||
throw new Error(
|
||||
`Infinite loop without sleep detected. ${CONSTANTS.InfiniteLoopLimit} ns functions were called without sleep. This will cause your UI to hang.`,
|
||||
);
|
||||
return func(ctx)(...args);
|
||||
}
|
||||
const parent = getNestedProperty(wrappedAPI, ...tree);
|
||||
|
@ -51,6 +51,7 @@ export const RamCostConstants: IMap<number> = {
|
||||
ScriptCodingContractBaseRamCost: 10,
|
||||
ScriptSleeveBaseRamCost: 4,
|
||||
ScriptGetOwnedSourceFiles: 5,
|
||||
ScriptClearTerminalCost: 0.2,
|
||||
|
||||
ScriptSingularityFn1RamCost: 2,
|
||||
ScriptSingularityFn2RamCost: 3,
|
||||
@ -156,6 +157,7 @@ const singularity: IMap<any> = {
|
||||
getUpgradeHomeCoresCost: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 2),
|
||||
workForCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||
applyToCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||
quitJob: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||
getCompanyRep: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
||||
getCompanyFavor: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
||||
getCompanyFavorGain: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 4),
|
||||
@ -357,6 +359,7 @@ export const RamCosts: IMap<any> = {
|
||||
enableLog: 0,
|
||||
isLogEnabled: 0,
|
||||
getScriptLogs: 0,
|
||||
clearTerminal: RamCostConstants.ScriptClearTerminalCost,
|
||||
nuke: RamCostConstants.ScriptPortProgramRamCost,
|
||||
brutessh: RamCostConstants.ScriptPortProgramRamCost,
|
||||
ftpcrack: RamCostConstants.ScriptPortProgramRamCost,
|
||||
|
@ -111,6 +111,11 @@ export class WorkerScript {
|
||||
*/
|
||||
atExit: any;
|
||||
|
||||
/**
|
||||
* Once this counter reaches it's limit the script crashes. It is reset when a promise completes.
|
||||
*/
|
||||
infiniteLoopSafetyCounter = 0;
|
||||
|
||||
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) {
|
||||
this.name = runningScriptObj.filename;
|
||||
this.hostname = runningScriptObj.server;
|
||||
|
@ -14,6 +14,7 @@ export function netscriptDelay(time: number, workerScript: WorkerScript): Promis
|
||||
workerScript.delay = null;
|
||||
workerScript.delayReject = undefined;
|
||||
|
||||
workerScript.infiniteLoopSafetyCounter = 0;
|
||||
if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript));
|
||||
else resolve();
|
||||
}, time);
|
||||
|
@ -55,7 +55,7 @@ import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
|
||||
|
||||
import { LogBoxEvents } from "./ui/React/LogBoxManager";
|
||||
import { LogBoxEvents, LogBoxCloserEvents } from "./ui/React/LogBoxManager";
|
||||
import { arrayToString } from "./utils/helpers/arrayToString";
|
||||
import { isString } from "./utils/helpers/isString";
|
||||
|
||||
@ -82,6 +82,7 @@ import {
|
||||
Gang as IGang,
|
||||
Bladeburner as IBladeburner,
|
||||
Stanek as IStanek,
|
||||
Sleeve as ISleeve,
|
||||
Infiltration as IInfiltration,
|
||||
RunningScript as IRunningScript,
|
||||
RecentScript as IRecentScript,
|
||||
@ -93,6 +94,12 @@ import {
|
||||
BitNodeMultipliers as IBNMults,
|
||||
Server as IServerDef,
|
||||
RunningScript as IRunningScriptDef,
|
||||
Grafting as IGrafting,
|
||||
UserInterface as IUserInterface,
|
||||
TIX as ITIX,
|
||||
Corporation as ICorporation,
|
||||
CodingContract as ICodingContract,
|
||||
Hacknet as IHacknet,
|
||||
// ToastVariant,
|
||||
} from "./ScriptEditor/NetscriptDefinitions";
|
||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
||||
@ -360,7 +367,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
}
|
||||
};
|
||||
|
||||
const hack = function (
|
||||
const hack = async function (
|
||||
hostname: string,
|
||||
manual: boolean,
|
||||
{ threads: requestedThreads, stock }: any = {},
|
||||
@ -524,23 +531,35 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
},
|
||||
};
|
||||
|
||||
const gang = NetscriptGang(Player, workerScript, helper);
|
||||
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
||||
const extra = NetscriptExtra(Player, workerScript, helper);
|
||||
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
||||
const formulas = NetscriptFormulas(Player, workerScript, helper);
|
||||
|
||||
const gang = wrapAPI(helper, {}, workerScript, NetscriptGang(Player, workerScript), "gang").gang as unknown as IGang;
|
||||
const sleeve = wrapAPI(helper, {}, workerScript, NetscriptSleeve(Player), "sleeve").sleeve as unknown as ISleeve;
|
||||
const hacknet = wrapAPI(helper, {}, workerScript, NetscriptHacknet(Player, workerScript), "hacknet")
|
||||
.hacknet as unknown as IHacknet;
|
||||
const bladeburner = wrapAPI(helper, {}, workerScript, NetscriptBladeburner(Player, workerScript), "bladeburner")
|
||||
.bladeburner as unknown as IBladeburner;
|
||||
const codingcontract = wrapAPI(
|
||||
helper,
|
||||
{},
|
||||
workerScript,
|
||||
NetscriptCodingContract(Player, workerScript),
|
||||
"codingcontract",
|
||||
).codingcontract as unknown as ICodingContract;
|
||||
const infiltration = wrapAPI(helper, {}, workerScript, NetscriptInfiltration(Player), "infiltration")
|
||||
.infiltration as unknown as IInfiltration;
|
||||
const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek")
|
||||
.stanek as unknown as IStanek;
|
||||
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
||||
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
||||
const corporation = NetscriptCorporation(Player, workerScript, helper);
|
||||
const formulas = NetscriptFormulas(Player, workerScript, helper);
|
||||
const corporation = wrapAPI(helper, {}, workerScript, NetscriptCorporation(Player, workerScript), "corporation")
|
||||
.corporation as unknown as ICorporation;
|
||||
const singularity = wrapAPI(helper, {}, workerScript, NetscriptSingularity(Player, workerScript), "singularity")
|
||||
.singularity as unknown as ISingularity;
|
||||
const stockmarket = NetscriptStockMarket(Player, workerScript, helper);
|
||||
const ui = NetscriptUserInterface(Player, workerScript, helper);
|
||||
const grafting = NetscriptGrafting(Player, workerScript, helper);
|
||||
const stockmarket = wrapAPI(helper, {}, workerScript, NetscriptStockMarket(Player, workerScript), "stock")
|
||||
.stock as unknown as ITIX;
|
||||
const ui = wrapAPI(helper, {}, workerScript, NetscriptUserInterface(), "ui").ui as unknown as IUserInterface;
|
||||
const grafting = wrapAPI(helper, {}, workerScript, NetscriptGrafting(Player), "grafting")
|
||||
.grafting as unknown as IGrafting;
|
||||
|
||||
const base: INS = {
|
||||
...singularity,
|
||||
@ -988,6 +1007,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
|
||||
LogBoxEvents.emit(runningScriptObj);
|
||||
},
|
||||
closeTail: function (_pid: unknown = workerScript.scriptRef.pid): void {
|
||||
updateDynamicRam("closeTail", getRamCost(Player, "closeTail"));
|
||||
const pid = helper.number("closeTail", "pid", _pid);
|
||||
//Emit an event to tell the game to close the tail window if it exists
|
||||
LogBoxCloserEvents.emit(pid);
|
||||
},
|
||||
nuke: function (_hostname: unknown): boolean {
|
||||
updateDynamicRam("nuke", getRamCost(Player, "nuke"));
|
||||
const hostname = helper.string("tail", "hostname", _hostname);
|
||||
@ -1233,16 +1258,21 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
killall: function (_hostname: unknown = workerScript.hostname): boolean {
|
||||
killall: function (_hostname: unknown = workerScript.hostname, _safetyguard: unknown = true): boolean {
|
||||
updateDynamicRam("killall", getRamCost(Player, "killall"));
|
||||
const hostname = helper.string("killall", "hostname", _hostname);
|
||||
const safetyguard = helper.boolean(_safetyguard);
|
||||
if (hostname === undefined) {
|
||||
throw makeRuntimeErrorMsg("killall", "Takes 1 argument");
|
||||
throw makeRuntimeErrorMsg("killall", "Usage: killall(hostname, [safetyguard boolean])");
|
||||
}
|
||||
const server = safeGetServer(hostname, "killall");
|
||||
const scriptsRunning = server.runningScripts.length > 0;
|
||||
|
||||
let scriptsKilled = 0;
|
||||
|
||||
for (let i = server.runningScripts.length - 1; i >= 0; --i) {
|
||||
if (safetyguard === true && server.runningScripts[i].pid == workerScript.pid) continue;
|
||||
killWorkerScript(server.runningScripts[i], server.hostname, false);
|
||||
++scriptsKilled;
|
||||
}
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
workerScript.log(
|
||||
@ -1250,7 +1280,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
() => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`,
|
||||
);
|
||||
|
||||
return scriptsRunning;
|
||||
return scriptsKilled > 0;
|
||||
},
|
||||
exit: function (): void {
|
||||
updateDynamicRam("exit", getRamCost(Player, "exit"));
|
||||
|
@ -1,18 +1,13 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Bladeburner } from "../Bladeburner/Bladeburner";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { IAction } from "src/Bladeburner/IAction";
|
||||
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptBladeburner(
|
||||
player: IPlayer,
|
||||
workerScript: WorkerScript,
|
||||
helper: INetscriptHelper,
|
||||
): INetscriptBladeburner {
|
||||
const checkBladeburnerAccess = function (func: string, skipjoined = false): void {
|
||||
export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript): InternalAPI<INetscriptBladeburner> {
|
||||
const checkBladeburnerAccess = function (ctx: NetscriptContext, skipjoined = false): void {
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Must have joined bladeburner");
|
||||
const apiAccess =
|
||||
@ -22,353 +17,354 @@ export function NetscriptBladeburner(
|
||||
});
|
||||
if (!apiAccess) {
|
||||
const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`;
|
||||
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied);
|
||||
throw ctx.makeRuntimeErrorMsg(apiDenied);
|
||||
}
|
||||
if (!skipjoined) {
|
||||
const bladeburnerAccess = bladeburner instanceof Bladeburner;
|
||||
if (!bladeburnerAccess) {
|
||||
const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`;
|
||||
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied);
|
||||
throw ctx.makeRuntimeErrorMsg(bladeburnerDenied);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const checkBladeburnerCity = function (func: string, city: string): void {
|
||||
const checkBladeburnerCity = function (ctx: NetscriptContext, city: string): void {
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Must have joined bladeburner");
|
||||
if (!bladeburner.cities.hasOwnProperty(city)) {
|
||||
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid city: ${city}`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid city: ${city}`);
|
||||
}
|
||||
};
|
||||
|
||||
const getBladeburnerActionObject = function (func: string, type: string, name: string): IAction {
|
||||
const getBladeburnerActionObject = function (ctx: NetscriptContext, type: string, name: string): IAction {
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Must have joined bladeburner");
|
||||
const actionId = bladeburner.getActionIdFromTypeAndName(type, name);
|
||||
if (!actionId) {
|
||||
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`);
|
||||
}
|
||||
const actionObj = bladeburner.getActionObject(actionId);
|
||||
if (!actionObj) {
|
||||
throw helper.makeRuntimeErrorMsg(`bladeburner.${func}`, `Invalid action type='${type}', name='${name}'`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid action type='${type}', name='${name}'`);
|
||||
}
|
||||
|
||||
return actionObj;
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "bladeburner", funcName));
|
||||
|
||||
return {
|
||||
getContractNames: function (): string[] {
|
||||
updateRam("getContractNames");
|
||||
checkBladeburnerAccess("getContractNames");
|
||||
getContractNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getContractNamesNetscriptFn();
|
||||
},
|
||||
getOperationNames: function (): string[] {
|
||||
updateRam("getOperationNames");
|
||||
checkBladeburnerAccess("getOperationNames");
|
||||
getOperationNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getOperationNamesNetscriptFn();
|
||||
},
|
||||
getBlackOpNames: function (): string[] {
|
||||
updateRam("getBlackOpNames");
|
||||
checkBladeburnerAccess("getBlackOpNames");
|
||||
getBlackOpNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getBlackOpNamesNetscriptFn();
|
||||
},
|
||||
getBlackOpRank: function (_blackOpName: unknown): number {
|
||||
updateRam("getBlackOpRank");
|
||||
const blackOpName = helper.string("getBlackOpRank", "blackOpName", _blackOpName);
|
||||
checkBladeburnerAccess("getBlackOpRank");
|
||||
const action: any = getBladeburnerActionObject("getBlackOpRank", "blackops", blackOpName);
|
||||
return action.reqdRank;
|
||||
},
|
||||
getGeneralActionNames: function (): string[] {
|
||||
updateRam("getGeneralActionNames");
|
||||
checkBladeburnerAccess("getGeneralActionNames");
|
||||
getBlackOpRank:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_blackOpName: unknown): number => {
|
||||
const blackOpName = ctx.helper.string("blackOpName", _blackOpName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action: any = getBladeburnerActionObject(ctx, "blackops", blackOpName);
|
||||
return action.reqdRank;
|
||||
},
|
||||
getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getGeneralActionNamesNetscriptFn();
|
||||
},
|
||||
getSkillNames: function (): string[] {
|
||||
updateRam("getSkillNames");
|
||||
checkBladeburnerAccess("getSkillNames");
|
||||
getSkillNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getSkillNamesNetscriptFn();
|
||||
},
|
||||
startAction: function (_type: unknown, _name: unknown): boolean {
|
||||
updateRam("startAction");
|
||||
const type = helper.string("startAction", "type", _type);
|
||||
const name = helper.string("startAction", "name", _name);
|
||||
checkBladeburnerAccess("startAction");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.startActionNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.startAction", e);
|
||||
}
|
||||
},
|
||||
stopBladeburnerAction: function (): void {
|
||||
updateRam("stopBladeburnerAction");
|
||||
checkBladeburnerAccess("stopBladeburnerAction");
|
||||
startAction:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): boolean => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.startActionNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
stopBladeburnerAction: (ctx: NetscriptContext) => (): void => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.resetAction();
|
||||
},
|
||||
getCurrentAction: function (): BladeburnerCurAction {
|
||||
updateRam("getCurrentAction");
|
||||
checkBladeburnerAccess("getCurrentAction");
|
||||
getCurrentAction: (ctx: NetscriptContext) => (): BladeburnerCurAction => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.getTypeAndNameFromActionId(bladeburner.action);
|
||||
},
|
||||
getActionTime: function (_type: unknown, _name: unknown): number {
|
||||
updateRam("getActionTime");
|
||||
const type = helper.string("getActionTime", "type", _type);
|
||||
const name = helper.string("getActionTime", "name", _name);
|
||||
checkBladeburnerAccess("getActionTime");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getActionTime", e);
|
||||
}
|
||||
},
|
||||
getActionEstimatedSuccessChance: function (_type: unknown, _name: unknown): [number, number] {
|
||||
updateRam("getActionEstimatedSuccessChance");
|
||||
const type = helper.string("getActionEstimatedSuccessChance", "type", _type);
|
||||
const name = helper.string("getActionEstimatedSuccessChance", "name", _name);
|
||||
checkBladeburnerAccess("getActionEstimatedSuccessChance");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e);
|
||||
}
|
||||
},
|
||||
getActionRepGain: function (_type: unknown, _name: unknown, _level: unknown): number {
|
||||
updateRam("getActionRepGain");
|
||||
const type = helper.string("getActionRepGain", "type", _type);
|
||||
const name = helper.string("getActionRepGain", "name", _name);
|
||||
const level = helper.number("getActionRepGain", "level", _level);
|
||||
checkBladeburnerAccess("getActionRepGain");
|
||||
const action = getBladeburnerActionObject("getActionRepGain", type, name);
|
||||
let rewardMultiplier;
|
||||
if (level == null || isNaN(level)) {
|
||||
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
|
||||
} else {
|
||||
rewardMultiplier = Math.pow(action.rewardFac, level - 1);
|
||||
}
|
||||
getActionTime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getActionEstimatedSuccessChance:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): [number, number] => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getActionRepGain:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown, _level: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
const level = ctx.helper.number("level", _level);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
let rewardMultiplier;
|
||||
if (level == null || isNaN(level)) {
|
||||
rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
|
||||
} else {
|
||||
rewardMultiplier = Math.pow(action.rewardFac, level - 1);
|
||||
}
|
||||
|
||||
return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank;
|
||||
},
|
||||
getActionCountRemaining: function (_type: unknown, _name: unknown): number {
|
||||
updateRam("getActionCountRemaining");
|
||||
const type = helper.string("getActionCountRemaining", "type", _type);
|
||||
const name = helper.string("getActionCountRemaining", "name", _name);
|
||||
checkBladeburnerAccess("getActionCountRemaining");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e);
|
||||
}
|
||||
},
|
||||
getActionMaxLevel: function (_type: unknown, _name: unknown): number {
|
||||
updateRam("getActionMaxLevel");
|
||||
const type = helper.string("getActionMaxLevel", "type", _type);
|
||||
const name = helper.string("getActionMaxLevel", "name", _name);
|
||||
checkBladeburnerAccess("getActionMaxLevel");
|
||||
const action = getBladeburnerActionObject("getActionMaxLevel", type, name);
|
||||
return action.maxLevel;
|
||||
},
|
||||
getActionCurrentLevel: function (_type: unknown, _name: unknown): number {
|
||||
updateRam("getActionCurrentLevel");
|
||||
const type = helper.string("getActionCurrentLevel", "type", _type);
|
||||
const name = helper.string("getActionCurrentLevel", "name", _name);
|
||||
checkBladeburnerAccess("getActionCurrentLevel");
|
||||
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name);
|
||||
return action.level;
|
||||
},
|
||||
getActionAutolevel: function (_type: unknown, _name: unknown): boolean {
|
||||
updateRam("getActionAutolevel");
|
||||
const type = helper.string("getActionAutolevel", "type", _type);
|
||||
const name = helper.string("getActionAutolevel", "name", _name);
|
||||
checkBladeburnerAccess("getActionAutolevel");
|
||||
const action = getBladeburnerActionObject("getActionCurrentLevel", type, name);
|
||||
return action.autoLevel;
|
||||
},
|
||||
setActionAutolevel: function (_type: unknown, _name: unknown, _autoLevel: unknown = true): void {
|
||||
updateRam("setActionAutolevel");
|
||||
const type = helper.string("setActionAutolevel", "type", _type);
|
||||
const name = helper.string("setActionAutolevel", "name", _name);
|
||||
const autoLevel = helper.boolean(_autoLevel);
|
||||
checkBladeburnerAccess("setActionAutolevel");
|
||||
const action = getBladeburnerActionObject("setActionAutolevel", type, name);
|
||||
action.autoLevel = autoLevel;
|
||||
},
|
||||
setActionLevel: function (_type: unknown, _name: unknown, _level: unknown = 1): void {
|
||||
updateRam("setActionLevel");
|
||||
const type = helper.string("setActionLevel", "type", _type);
|
||||
const name = helper.string("setActionLevel", "name", _name);
|
||||
const level = helper.number("setActionLevel", "level", _level);
|
||||
checkBladeburnerAccess("setActionLevel");
|
||||
const action = getBladeburnerActionObject("setActionLevel", type, name);
|
||||
if (level < 1 || level > action.maxLevel) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"bladeburner.setActionLevel",
|
||||
`Level must be between 1 and ${action.maxLevel}, is ${level}`,
|
||||
);
|
||||
}
|
||||
action.level = level;
|
||||
},
|
||||
getRank: function (): number {
|
||||
updateRam("getRank");
|
||||
checkBladeburnerAccess("getRank");
|
||||
return action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank;
|
||||
},
|
||||
getActionCountRemaining:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getActionMaxLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
return action.maxLevel;
|
||||
},
|
||||
getActionCurrentLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
return action.level;
|
||||
},
|
||||
getActionAutolevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): boolean => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
return action.autoLevel;
|
||||
},
|
||||
setActionAutolevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown, _autoLevel: unknown = true): void => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
const autoLevel = ctx.helper.boolean(_autoLevel);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
action.autoLevel = autoLevel;
|
||||
},
|
||||
setActionLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown, _level: unknown = 1): void => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
const level = ctx.helper.number("level", _level);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const action = getBladeburnerActionObject(ctx, type, name);
|
||||
if (level < 1 || level > action.maxLevel) {
|
||||
ctx.helper.makeRuntimeErrorMsg(`Level must be between 1 and ${action.maxLevel}, is ${level}`);
|
||||
}
|
||||
action.level = level;
|
||||
},
|
||||
getRank: (ctx: NetscriptContext) => (): number => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.rank;
|
||||
},
|
||||
getSkillPoints: function (): number {
|
||||
updateRam("getSkillPoints");
|
||||
checkBladeburnerAccess("getSkillPoints");
|
||||
getSkillPoints: (ctx: NetscriptContext) => (): number => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.skillPoints;
|
||||
},
|
||||
getSkillLevel: function (_skillName: unknown): number {
|
||||
updateRam("getSkillLevel");
|
||||
const skillName = helper.string("getSkillLevel", "skillName", _skillName);
|
||||
checkBladeburnerAccess("getSkillLevel");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillLevel", e);
|
||||
}
|
||||
},
|
||||
getSkillUpgradeCost: function (_skillName: unknown): number {
|
||||
updateRam("getSkillUpgradeCost");
|
||||
const skillName = helper.string("getSkillUpgradeCost", "skillName", _skillName);
|
||||
checkBladeburnerAccess("getSkillUpgradeCost");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e);
|
||||
}
|
||||
},
|
||||
upgradeSkill: function (_skillName: unknown): boolean {
|
||||
updateRam("upgradeSkill");
|
||||
const skillName = helper.string("upgradeSkill", "skillName", _skillName);
|
||||
checkBladeburnerAccess("upgradeSkill");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.upgradeSkill", e);
|
||||
}
|
||||
},
|
||||
getTeamSize: function (_type: unknown, _name: unknown): number {
|
||||
updateRam("getTeamSize");
|
||||
const type = helper.string("getTeamSize", "type", _type);
|
||||
const name = helper.string("getTeamSize", "name", _name);
|
||||
checkBladeburnerAccess("getTeamSize");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.getTeamSize", e);
|
||||
}
|
||||
},
|
||||
setTeamSize: function (_type: unknown, _name: unknown, _size: unknown): number {
|
||||
updateRam("setTeamSize");
|
||||
const type = helper.string("setTeamSize", "type", _type);
|
||||
const name = helper.string("setTeamSize", "name", _name);
|
||||
const size = helper.number("setTeamSize", "size", _size);
|
||||
checkBladeburnerAccess("setTeamSize");
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
|
||||
} catch (e: any) {
|
||||
throw helper.makeRuntimeErrorMsg("bladeburner.setTeamSize", e);
|
||||
}
|
||||
},
|
||||
getCityEstimatedPopulation: function (_cityName: unknown): number {
|
||||
updateRam("getCityEstimatedPopulation");
|
||||
const cityName = helper.string("getCityEstimatedPopulation", "cityName", _cityName);
|
||||
checkBladeburnerAccess("getCityEstimatedPopulation");
|
||||
checkBladeburnerCity("getCityEstimatedPopulation", cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].popEst;
|
||||
},
|
||||
getCityCommunities: function (_cityName: unknown): number {
|
||||
updateRam("getCityCommunities");
|
||||
const cityName = helper.string("getCityCommunities", "cityName", _cityName);
|
||||
checkBladeburnerAccess("getCityCommunities");
|
||||
checkBladeburnerCity("getCityCommunities", cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].comms;
|
||||
},
|
||||
getCityChaos: function (_cityName: unknown): number {
|
||||
updateRam("getCityChaos");
|
||||
const cityName = helper.string("getCityChaos", "cityName", _cityName);
|
||||
checkBladeburnerAccess("getCityChaos");
|
||||
checkBladeburnerCity("getCityChaos", cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].chaos;
|
||||
},
|
||||
getCity: function (): string {
|
||||
updateRam("getCity");
|
||||
checkBladeburnerAccess("getCityChaos");
|
||||
getSkillLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_skillName: unknown): number => {
|
||||
const skillName = ctx.helper.string("skillName", _skillName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getSkillUpgradeCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_skillName: unknown): number => {
|
||||
const skillName = ctx.helper.string("skillName", _skillName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
upgradeSkill:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_skillName: unknown): boolean => {
|
||||
const skillName = ctx.helper.string("skillName", _skillName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getTeamSize:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
setTeamSize:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_type: unknown, _name: unknown, _size: unknown): number => {
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const name = ctx.helper.string("name", _name);
|
||||
const size = ctx.helper.number("size", _size);
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
},
|
||||
getCityEstimatedPopulation:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_cityName: unknown): number => {
|
||||
const cityName = ctx.helper.string("cityName", _cityName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
checkBladeburnerCity(ctx, cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].popEst;
|
||||
},
|
||||
getCityCommunities:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_cityName: unknown): number => {
|
||||
const cityName = ctx.helper.string("cityName", _cityName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
checkBladeburnerCity(ctx, cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].comms;
|
||||
},
|
||||
getCityChaos:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_cityName: unknown): number => {
|
||||
const cityName = ctx.helper.string("cityName", _cityName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
checkBladeburnerCity(ctx, cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.cities[cityName].chaos;
|
||||
},
|
||||
getCity: (ctx: NetscriptContext) => (): string => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.city;
|
||||
},
|
||||
switchCity: function (_cityName: unknown): boolean {
|
||||
updateRam("switchCity");
|
||||
const cityName = helper.string("switchCity", "cityName", _cityName);
|
||||
checkBladeburnerAccess("switchCity");
|
||||
checkBladeburnerCity("switchCity", cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
bladeburner.city = cityName;
|
||||
return true;
|
||||
},
|
||||
getStamina: function (): [number, number] {
|
||||
updateRam("getStamina");
|
||||
checkBladeburnerAccess("getStamina");
|
||||
switchCity:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_cityName: unknown): boolean => {
|
||||
const cityName = ctx.helper.string("cityName", _cityName);
|
||||
checkBladeburnerAccess(ctx);
|
||||
checkBladeburnerCity(ctx, cityName);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
bladeburner.city = cityName;
|
||||
return true;
|
||||
},
|
||||
getStamina: (ctx: NetscriptContext) => (): [number, number] => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return [bladeburner.stamina, bladeburner.maxStamina];
|
||||
},
|
||||
joinBladeburnerFaction: function (): boolean {
|
||||
updateRam("joinBladeburnerFaction");
|
||||
checkBladeburnerAccess("joinBladeburnerFaction", true);
|
||||
joinBladeburnerFaction: (ctx: NetscriptContext) => (): boolean => {
|
||||
checkBladeburnerAccess(ctx, true);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return bladeburner.joinBladeburnerFactionNetscriptFn(workerScript);
|
||||
},
|
||||
joinBladeburnerDivision: function (): boolean {
|
||||
updateRam("joinBladeburnerDivision");
|
||||
joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => {
|
||||
if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) {
|
||||
if (player.bitNodeN === 8) {
|
||||
return false;
|
||||
@ -382,22 +378,18 @@ export function NetscriptBladeburner(
|
||||
player.agility >= 100
|
||||
) {
|
||||
player.bladeburner = new Bladeburner(player);
|
||||
workerScript.log("joinBladeburnerDivision", () => "You have been accepted into the Bladeburner division");
|
||||
ctx.log(() => "You have been accepted into the Bladeburner division");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
workerScript.log(
|
||||
"joinBladeburnerDivision",
|
||||
() => "You do not meet the requirements for joining the Bladeburner division",
|
||||
);
|
||||
ctx.log(() => "You do not meet the requirements for joining the Bladeburner division");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getBonusTime: function (): number {
|
||||
updateRam("getBonusTime");
|
||||
checkBladeburnerAccess("getBonusTime");
|
||||
getBonusTime: (ctx: NetscriptContext) => (): number => {
|
||||
checkBladeburnerAccess(ctx);
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
return Math.round(bladeburner.storedCycles / 5) * 1000;
|
||||
|
@ -1,132 +1,124 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { is2DArray } from "../utils/helpers/is2DArray";
|
||||
import { CodingContract } from "../CodingContracts";
|
||||
import { CodingAttemptOptions, CodingContract as ICodingContract } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptCodingContract(
|
||||
player: IPlayer,
|
||||
workerScript: WorkerScript,
|
||||
helper: INetscriptHelper,
|
||||
): ICodingContract {
|
||||
const getCodingContract = function (func: string, hostname: string, filename: string): CodingContract {
|
||||
const server = helper.getServer(hostname, func);
|
||||
export function NetscriptCodingContract(player: IPlayer, workerScript: WorkerScript): InternalAPI<ICodingContract> {
|
||||
const getCodingContract = function (
|
||||
ctx: NetscriptContext,
|
||||
func: string,
|
||||
hostname: string,
|
||||
filename: string,
|
||||
): CodingContract {
|
||||
const server = ctx.helper.getServer(hostname);
|
||||
const contract = server.getContract(filename);
|
||||
if (contract == null) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
`codingcontract.${func}`,
|
||||
`Cannot find contract '${filename}' on server '${hostname}'`,
|
||||
);
|
||||
throw ctx.makeRuntimeErrorMsg(`Cannot find contract '${filename}' on server '${hostname}'`);
|
||||
}
|
||||
|
||||
return contract;
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "codingcontract", funcName));
|
||||
|
||||
return {
|
||||
attempt: function (
|
||||
answer: any,
|
||||
_filename: unknown,
|
||||
_hostname: unknown = workerScript.hostname,
|
||||
{ returnReward }: CodingAttemptOptions = { returnReward: false },
|
||||
): boolean | string {
|
||||
updateRam("attempt");
|
||||
const filename = helper.string("attempt", "filename", _filename);
|
||||
const hostname = helper.string("attempt", "hostname", _hostname);
|
||||
const contract = getCodingContract("attempt", hostname, filename);
|
||||
attempt:
|
||||
(ctx: NetscriptContext) =>
|
||||
(
|
||||
answer: any,
|
||||
_filename: unknown,
|
||||
_hostname: unknown = workerScript.hostname,
|
||||
{ returnReward }: CodingAttemptOptions = { returnReward: false },
|
||||
): boolean | string => {
|
||||
const filename = ctx.helper.string("filename", _filename);
|
||||
const hostname = ctx.helper.string("hostname", _hostname);
|
||||
const contract = getCodingContract(ctx, "attempt", hostname, filename);
|
||||
|
||||
// Convert answer to string. If the answer is a 2D array, then we have to
|
||||
// manually add brackets for the inner arrays
|
||||
if (is2DArray(answer)) {
|
||||
const answerComponents = [];
|
||||
for (let i = 0; i < answer.length; ++i) {
|
||||
answerComponents.push(["[", answer[i].toString(), "]"].join(""));
|
||||
}
|
||||
|
||||
answer = answerComponents.join(",");
|
||||
} else {
|
||||
answer = String(answer);
|
||||
}
|
||||
|
||||
const creward = contract.reward;
|
||||
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
|
||||
|
||||
const serv = helper.getServer(hostname, "codingcontract.attempt");
|
||||
if (contract.isSolution(answer)) {
|
||||
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
|
||||
workerScript.log(
|
||||
"codingcontract.attempt",
|
||||
() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`,
|
||||
);
|
||||
serv.removeContract(filename);
|
||||
return returnReward ? reward : true;
|
||||
} else {
|
||||
++contract.tries;
|
||||
if (contract.tries >= contract.getMaxNumTries()) {
|
||||
workerScript.log(
|
||||
"codingcontract.attempt",
|
||||
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
|
||||
);
|
||||
serv.removeContract(filename);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"codingcontract.attempt",
|
||||
() =>
|
||||
`Coding Contract attempt '${filename}' failed. ${
|
||||
contract.getMaxNumTries() - contract.tries
|
||||
} attempts remaining.`,
|
||||
);
|
||||
}
|
||||
|
||||
return returnReward ? "" : false;
|
||||
}
|
||||
},
|
||||
getContractType: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string {
|
||||
updateRam("getContractType");
|
||||
const filename = helper.string("getContractType", "filename", _filename);
|
||||
const hostname = helper.string("getContractType", "hostname", _hostname);
|
||||
const contract = getCodingContract("getContractType", hostname, filename);
|
||||
return contract.getType();
|
||||
},
|
||||
getData: function (_filename: unknown, _hostname: unknown = workerScript.hostname): any {
|
||||
updateRam("getData");
|
||||
const filename = helper.string("getContractType", "filename", _filename);
|
||||
const hostname = helper.string("getContractType", "hostname", _hostname);
|
||||
const contract = getCodingContract("getData", hostname, filename);
|
||||
const data = contract.getData();
|
||||
if (data.constructor === Array) {
|
||||
// For two dimensional arrays, we have to copy the internal arrays using
|
||||
// slice() as well. As of right now, no contract has arrays that have
|
||||
// more than two dimensions
|
||||
const copy = data.slice();
|
||||
for (let i = 0; i < copy.length; ++i) {
|
||||
if (data[i].constructor === Array) {
|
||||
copy[i] = data[i].slice();
|
||||
// Convert answer to string. If the answer is a 2D array, then we have to
|
||||
// manually add brackets for the inner arrays
|
||||
if (is2DArray(answer)) {
|
||||
const answerComponents = [];
|
||||
for (let i = 0; i < answer.length; ++i) {
|
||||
answerComponents.push(["[", answer[i].toString(), "]"].join(""));
|
||||
}
|
||||
|
||||
answer = answerComponents.join(",");
|
||||
} else {
|
||||
answer = String(answer);
|
||||
}
|
||||
|
||||
return copy;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
getDescription: function (_filename: unknown, _hostname: unknown = workerScript.hostname): string {
|
||||
updateRam("getDescription");
|
||||
const filename = helper.string("getDescription", "filename", _filename);
|
||||
const hostname = helper.string("getDescription", "hostname", _hostname);
|
||||
const contract = getCodingContract("getDescription", hostname, filename);
|
||||
return contract.getDescription();
|
||||
},
|
||||
getNumTriesRemaining: function (_filename: unknown, _hostname: unknown = workerScript.hostname): number {
|
||||
updateRam("getNumTriesRemaining");
|
||||
const filename = helper.string("getNumTriesRemaining", "filename", _filename);
|
||||
const hostname = helper.string("getNumTriesRemaining", "hostname", _hostname);
|
||||
const contract = getCodingContract("getNumTriesRemaining", hostname, filename);
|
||||
return contract.getMaxNumTries() - contract.tries;
|
||||
},
|
||||
const creward = contract.reward;
|
||||
if (creward === null) throw new Error("Somehow solved a contract that didn't have a reward");
|
||||
|
||||
const serv = ctx.helper.getServer(hostname);
|
||||
if (contract.isSolution(answer)) {
|
||||
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
|
||||
ctx.log(() => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
|
||||
serv.removeContract(filename);
|
||||
return returnReward ? reward : true;
|
||||
} else {
|
||||
++contract.tries;
|
||||
if (contract.tries >= contract.getMaxNumTries()) {
|
||||
ctx.log(() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`);
|
||||
serv.removeContract(filename);
|
||||
} else {
|
||||
ctx.log(
|
||||
() =>
|
||||
`Coding Contract attempt '${filename}' failed. ${
|
||||
contract.getMaxNumTries() - contract.tries
|
||||
} attempts remaining.`,
|
||||
);
|
||||
}
|
||||
|
||||
return returnReward ? "" : false;
|
||||
}
|
||||
},
|
||||
getContractType:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
|
||||
const filename = ctx.helper.string("filename", _filename);
|
||||
const hostname = ctx.helper.string("hostname", _hostname);
|
||||
const contract = getCodingContract(ctx, "getContractType", hostname, filename);
|
||||
return contract.getType();
|
||||
},
|
||||
getData:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_filename: unknown, _hostname: unknown = workerScript.hostname): any => {
|
||||
const filename = ctx.helper.string("filename", _filename);
|
||||
const hostname = ctx.helper.string("hostname", _hostname);
|
||||
const contract = getCodingContract(ctx, "getData", hostname, filename);
|
||||
const data = contract.getData();
|
||||
if (data.constructor === Array) {
|
||||
// For two dimensional arrays, we have to copy the internal arrays using
|
||||
// slice() as well. As of right now, no contract has arrays that have
|
||||
// more than two dimensions
|
||||
const copy = data.slice();
|
||||
for (let i = 0; i < copy.length; ++i) {
|
||||
if (data[i].constructor === Array) {
|
||||
copy[i] = data[i].slice();
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
getDescription:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_filename: unknown, _hostname: unknown = workerScript.hostname): string => {
|
||||
const filename = ctx.helper.string("filename", _filename);
|
||||
const hostname = ctx.helper.string("hostname", _hostname);
|
||||
const contract = getCodingContract(ctx, "getDescription", hostname, filename);
|
||||
return contract.getDescription();
|
||||
},
|
||||
getNumTriesRemaining:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_filename: unknown, _hostname: unknown = workerScript.hostname): number => {
|
||||
const filename = ctx.helper.string("filename", _filename);
|
||||
const hostname = ctx.helper.string("hostname", _hostname);
|
||||
const contract = getCodingContract(ctx, "getNumTriesRemaining", hostname, filename);
|
||||
return contract.getMaxNumTries() - contract.tries;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,6 @@
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { GangConstants } from "../Gang/data/Constants";
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { Gang } from "../Gang/Gang";
|
||||
import { AllGangs } from "../Gang/AllGangs";
|
||||
import { GangMemberTasks } from "../Gang/GangMemberTasks";
|
||||
@ -20,63 +18,60 @@ import {
|
||||
EquipmentStats,
|
||||
GangTaskStats,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGang {
|
||||
const checkGangApiAccess = function (func: string): void {
|
||||
export function NetscriptGang(player: IPlayer, workerScript: WorkerScript): InternalAPI<IGang> {
|
||||
const checkGangApiAccess = function (ctx: NetscriptContext): void {
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Must have joined gang");
|
||||
const hasAccess = gang instanceof Gang;
|
||||
if (!hasAccess) {
|
||||
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `You do not currently have a Gang`);
|
||||
throw ctx.makeRuntimeErrorMsg(`You do not currently have a Gang`);
|
||||
}
|
||||
};
|
||||
|
||||
const getGangMember = function (func: string, name: string): GangMember {
|
||||
const getGangMember = function (ctx: NetscriptContext, name: string): GangMember {
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Must have joined gang");
|
||||
for (const member of gang.members) if (member.name === name) return member;
|
||||
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `Invalid gang member: '${name}'`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid gang member: '${name}'`);
|
||||
};
|
||||
|
||||
const getGangTask = function (func: string, name: string): GangMemberTask {
|
||||
const getGangTask = function (ctx: NetscriptContext, name: string): GangMemberTask {
|
||||
const task = GangMemberTasks[name];
|
||||
if (!task) {
|
||||
throw helper.makeRuntimeErrorMsg(`gang.${func}`, `Invalid task: '${name}'`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid task: '${name}'`);
|
||||
}
|
||||
|
||||
return task;
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void => helper.updateDynamicRam(funcName, getRamCost(player, "gang", funcName));
|
||||
|
||||
return {
|
||||
createGang: function (_faction: unknown): boolean {
|
||||
updateRam("createGang");
|
||||
const faction = helper.string("createGang", "faction", _faction);
|
||||
// this list is copied from Faction/ui/Root.tsx
|
||||
createGang:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_faction: unknown): boolean => {
|
||||
const faction = ctx.helper.string("faction", _faction);
|
||||
// this list is copied from Faction/ui/Root.tsx
|
||||
|
||||
if (!player.canAccessGang() || !GangConstants.Names.includes(faction)) return false;
|
||||
if (player.inGang()) return false;
|
||||
if (!player.factions.includes(faction)) return false;
|
||||
if (!player.canAccessGang() || !GangConstants.Names.includes(faction)) return false;
|
||||
if (player.inGang()) return false;
|
||||
if (!player.factions.includes(faction)) return false;
|
||||
|
||||
const isHacking = faction === FactionNames.NiteSec || faction === FactionNames.TheBlackHand;
|
||||
player.startGang(faction, isHacking);
|
||||
return true;
|
||||
},
|
||||
inGang: function (): boolean {
|
||||
updateRam("inGang");
|
||||
const isHacking = faction === FactionNames.NiteSec || faction === FactionNames.TheBlackHand;
|
||||
player.startGang(faction, isHacking);
|
||||
return true;
|
||||
},
|
||||
inGang: () => (): boolean => {
|
||||
return player.inGang();
|
||||
},
|
||||
getMemberNames: function (): string[] {
|
||||
updateRam("getMemberNames");
|
||||
checkGangApiAccess("getMemberNames");
|
||||
getMemberNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
return gang.members.map((member) => member.name);
|
||||
},
|
||||
getGangInformation: function (): GangGenInfo {
|
||||
updateRam("getGangInformation");
|
||||
checkGangApiAccess("getGangInformation");
|
||||
getGangInformation: (ctx: NetscriptContext) => (): GangGenInfo => {
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
return {
|
||||
@ -94,9 +89,8 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
|
||||
wantedPenalty: gang.getWantedPenalty(),
|
||||
};
|
||||
},
|
||||
getOtherGangInformation: function (): GangOtherInfo {
|
||||
updateRam("getOtherGangInformation");
|
||||
checkGangApiAccess("getOtherGangInformation");
|
||||
getOtherGangInformation: (ctx: NetscriptContext) => (): GangOtherInfo => {
|
||||
checkGangApiAccess(ctx);
|
||||
const cpy: any = {};
|
||||
for (const gang of Object.keys(AllGangs)) {
|
||||
cpy[gang] = Object.assign({}, AllGangs[gang]);
|
||||
@ -104,245 +98,254 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
|
||||
|
||||
return cpy;
|
||||
},
|
||||
getMemberInformation: function (_memberName: unknown): GangMemberInfo {
|
||||
updateRam("getMemberInformation");
|
||||
const memberName = helper.string("getMemberInformation", "memberName", _memberName);
|
||||
checkGangApiAccess("getMemberInformation");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember("getMemberInformation", memberName);
|
||||
return {
|
||||
name: member.name,
|
||||
task: member.task,
|
||||
earnedRespect: member.earnedRespect,
|
||||
hack: member.hack,
|
||||
str: member.str,
|
||||
def: member.def,
|
||||
dex: member.dex,
|
||||
agi: member.agi,
|
||||
cha: member.cha,
|
||||
getMemberInformation:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown): GangMemberInfo => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember(ctx, memberName);
|
||||
return {
|
||||
name: member.name,
|
||||
task: member.task,
|
||||
earnedRespect: member.earnedRespect,
|
||||
hack: member.hack,
|
||||
str: member.str,
|
||||
def: member.def,
|
||||
dex: member.dex,
|
||||
agi: member.agi,
|
||||
cha: member.cha,
|
||||
|
||||
hack_exp: member.hack_exp,
|
||||
str_exp: member.str_exp,
|
||||
def_exp: member.def_exp,
|
||||
dex_exp: member.dex_exp,
|
||||
agi_exp: member.agi_exp,
|
||||
cha_exp: member.cha_exp,
|
||||
hack_exp: member.hack_exp,
|
||||
str_exp: member.str_exp,
|
||||
def_exp: member.def_exp,
|
||||
dex_exp: member.dex_exp,
|
||||
agi_exp: member.agi_exp,
|
||||
cha_exp: member.cha_exp,
|
||||
|
||||
hack_mult: member.hack_mult,
|
||||
str_mult: member.str_mult,
|
||||
def_mult: member.def_mult,
|
||||
dex_mult: member.dex_mult,
|
||||
agi_mult: member.agi_mult,
|
||||
cha_mult: member.cha_mult,
|
||||
hack_mult: member.hack_mult,
|
||||
str_mult: member.str_mult,
|
||||
def_mult: member.def_mult,
|
||||
dex_mult: member.dex_mult,
|
||||
agi_mult: member.agi_mult,
|
||||
cha_mult: member.cha_mult,
|
||||
|
||||
hack_asc_mult: member.calculateAscensionMult(member.hack_asc_points),
|
||||
str_asc_mult: member.calculateAscensionMult(member.str_asc_points),
|
||||
def_asc_mult: member.calculateAscensionMult(member.def_asc_points),
|
||||
dex_asc_mult: member.calculateAscensionMult(member.dex_asc_points),
|
||||
agi_asc_mult: member.calculateAscensionMult(member.agi_asc_points),
|
||||
cha_asc_mult: member.calculateAscensionMult(member.cha_asc_points),
|
||||
hack_asc_mult: member.calculateAscensionMult(member.hack_asc_points),
|
||||
str_asc_mult: member.calculateAscensionMult(member.str_asc_points),
|
||||
def_asc_mult: member.calculateAscensionMult(member.def_asc_points),
|
||||
dex_asc_mult: member.calculateAscensionMult(member.dex_asc_points),
|
||||
agi_asc_mult: member.calculateAscensionMult(member.agi_asc_points),
|
||||
cha_asc_mult: member.calculateAscensionMult(member.cha_asc_points),
|
||||
|
||||
hack_asc_points: member.hack_asc_points,
|
||||
str_asc_points: member.str_asc_points,
|
||||
def_asc_points: member.def_asc_points,
|
||||
dex_asc_points: member.dex_asc_points,
|
||||
agi_asc_points: member.agi_asc_points,
|
||||
cha_asc_points: member.cha_asc_points,
|
||||
hack_asc_points: member.hack_asc_points,
|
||||
str_asc_points: member.str_asc_points,
|
||||
def_asc_points: member.def_asc_points,
|
||||
dex_asc_points: member.dex_asc_points,
|
||||
agi_asc_points: member.agi_asc_points,
|
||||
cha_asc_points: member.cha_asc_points,
|
||||
|
||||
upgrades: member.upgrades.slice(),
|
||||
augmentations: member.augmentations.slice(),
|
||||
upgrades: member.upgrades.slice(),
|
||||
augmentations: member.augmentations.slice(),
|
||||
|
||||
respectGain: member.calculateRespectGain(gang),
|
||||
wantedLevelGain: member.calculateWantedLevelGain(gang),
|
||||
moneyGain: member.calculateMoneyGain(gang),
|
||||
};
|
||||
},
|
||||
canRecruitMember: function (): boolean {
|
||||
updateRam("canRecruitMember");
|
||||
checkGangApiAccess("canRecruitMember");
|
||||
respectGain: member.calculateRespectGain(gang),
|
||||
wantedLevelGain: member.calculateWantedLevelGain(gang),
|
||||
moneyGain: member.calculateMoneyGain(gang),
|
||||
};
|
||||
},
|
||||
canRecruitMember: (ctx: NetscriptContext) => (): boolean => {
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
return gang.canRecruitMember();
|
||||
},
|
||||
recruitMember: function (_memberName: unknown): boolean {
|
||||
updateRam("recruitMember");
|
||||
const memberName = helper.string("recruitMember", "memberName", _memberName);
|
||||
checkGangApiAccess("recruitMember");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const recruited = gang.recruitMember(memberName);
|
||||
if (recruited) {
|
||||
workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`);
|
||||
} else {
|
||||
workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`);
|
||||
}
|
||||
recruitMember:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown): boolean => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const recruited = gang.recruitMember(memberName);
|
||||
if (recruited) {
|
||||
workerScript.log("gang.recruitMember", () => `Successfully recruited Gang Member '${memberName}'`);
|
||||
} else {
|
||||
workerScript.log("gang.recruitMember", () => `Failed to recruit Gang Member '${memberName}'`);
|
||||
}
|
||||
|
||||
return recruited;
|
||||
},
|
||||
getTaskNames: function (): string[] {
|
||||
updateRam("getTaskNames");
|
||||
checkGangApiAccess("getTaskNames");
|
||||
return recruited;
|
||||
},
|
||||
getTaskNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const tasks = gang.getAllTaskNames();
|
||||
tasks.unshift("Unassigned");
|
||||
return tasks;
|
||||
},
|
||||
setMemberTask: function (_memberName: unknown, _taskName: unknown): boolean {
|
||||
updateRam("setMemberTask");
|
||||
const memberName = helper.string("setMemberTask", "memberName", _memberName);
|
||||
const taskName = helper.string("setMemberTask", "taskName", _taskName);
|
||||
checkGangApiAccess("setMemberTask");
|
||||
const member = getGangMember("setMemberTask", memberName);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (!gang.getAllTaskNames().includes(taskName)) {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() =>
|
||||
`Failed to assign Gang Member '${memberName}' to Invalid task '${taskName}'. '${memberName}' is now Unassigned`,
|
||||
);
|
||||
return member.assignToTask("Unassigned");
|
||||
}
|
||||
const success = member.assignToTask(taskName);
|
||||
if (success) {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`,
|
||||
);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() => `Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`,
|
||||
);
|
||||
}
|
||||
setMemberTask:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown, _taskName: unknown): boolean => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
const taskName = ctx.helper.string("taskName", _taskName);
|
||||
checkGangApiAccess(ctx);
|
||||
const member = getGangMember(ctx, memberName);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (!gang.getAllTaskNames().includes(taskName)) {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() =>
|
||||
`Failed to assign Gang Member '${memberName}' to Invalid task '${taskName}'. '${memberName}' is now Unassigned`,
|
||||
);
|
||||
return member.assignToTask("Unassigned");
|
||||
}
|
||||
const success = member.assignToTask(taskName);
|
||||
if (success) {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() => `Successfully assigned Gang Member '${memberName}' to '${taskName}' task`,
|
||||
);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"gang.setMemberTask",
|
||||
() =>
|
||||
`Failed to assign Gang Member '${memberName}' to '${taskName}' task. '${memberName}' is now Unassigned`,
|
||||
);
|
||||
}
|
||||
|
||||
return success;
|
||||
},
|
||||
getTaskStats: function (_taskName: unknown): GangTaskStats {
|
||||
updateRam("getTaskStats");
|
||||
const taskName = helper.string("getTaskStats", "taskName", _taskName);
|
||||
checkGangApiAccess("getTaskStats");
|
||||
const task = getGangTask("getTaskStats", taskName);
|
||||
const copy = Object.assign({}, task);
|
||||
copy.territory = Object.assign({}, task.territory);
|
||||
return copy;
|
||||
},
|
||||
getEquipmentNames: function (): string[] {
|
||||
updateRam("getEquipmentNames");
|
||||
checkGangApiAccess("getEquipmentNames");
|
||||
return success;
|
||||
},
|
||||
getTaskStats:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_taskName: unknown): GangTaskStats => {
|
||||
const taskName = ctx.helper.string("taskName", _taskName);
|
||||
checkGangApiAccess(ctx);
|
||||
const task = getGangTask(ctx, taskName);
|
||||
const copy = Object.assign({}, task);
|
||||
copy.territory = Object.assign({}, task.territory);
|
||||
return copy;
|
||||
},
|
||||
getEquipmentNames: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkGangApiAccess(ctx);
|
||||
return Object.keys(GangMemberUpgrades);
|
||||
},
|
||||
getEquipmentCost: function (_equipName: any): number {
|
||||
updateRam("getEquipmentCost");
|
||||
const equipName = helper.string("getEquipmentCost", "equipName", _equipName);
|
||||
checkGangApiAccess("getEquipmentCost");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const upg = GangMemberUpgrades[equipName];
|
||||
if (upg === null) return Infinity;
|
||||
return gang.getUpgradeCost(upg);
|
||||
},
|
||||
getEquipmentType: function (_equipName: unknown): string {
|
||||
updateRam("getEquipmentType");
|
||||
const equipName = helper.string("getEquipmentType", "equipName", _equipName);
|
||||
checkGangApiAccess("getEquipmentType");
|
||||
const upg = GangMemberUpgrades[equipName];
|
||||
if (upg == null) return "";
|
||||
return upg.getType();
|
||||
},
|
||||
getEquipmentStats: function (_equipName: unknown): EquipmentStats {
|
||||
updateRam("getEquipmentStats");
|
||||
const equipName = helper.string("getEquipmentStats", "equipName", _equipName);
|
||||
checkGangApiAccess("getEquipmentStats");
|
||||
const equipment = GangMemberUpgrades[equipName];
|
||||
if (!equipment) {
|
||||
throw helper.makeRuntimeErrorMsg("getEquipmentStats", `Invalid equipment: ${equipName}`);
|
||||
}
|
||||
const typecheck: EquipmentStats = equipment.mults;
|
||||
return Object.assign({}, typecheck) as any;
|
||||
},
|
||||
purchaseEquipment: function (_memberName: unknown, _equipName: unknown): boolean {
|
||||
updateRam("purchaseEquipment");
|
||||
const memberName = helper.string("purchaseEquipment", "memberName", _memberName);
|
||||
const equipName = helper.string("purchaseEquipment", "equipName", _equipName);
|
||||
checkGangApiAccess("purchaseEquipment");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember("purchaseEquipment", memberName);
|
||||
const equipment = GangMemberUpgrades[equipName];
|
||||
if (!equipment) return false;
|
||||
const res = member.buyUpgrade(equipment, player, gang);
|
||||
if (res) {
|
||||
workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"gang.purchaseEquipment",
|
||||
() => `Failed to purchase '${equipName}' for Gang member '${memberName}'`,
|
||||
);
|
||||
}
|
||||
getEquipmentCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_equipName: any): number => {
|
||||
const equipName = ctx.helper.string("equipName", _equipName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const upg = GangMemberUpgrades[equipName];
|
||||
if (upg === null) return Infinity;
|
||||
return gang.getUpgradeCost(upg);
|
||||
},
|
||||
getEquipmentType:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_equipName: unknown): string => {
|
||||
const equipName = ctx.helper.string("equipName", _equipName);
|
||||
checkGangApiAccess(ctx);
|
||||
const upg = GangMemberUpgrades[equipName];
|
||||
if (upg == null) return "";
|
||||
return upg.getType();
|
||||
},
|
||||
getEquipmentStats:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_equipName: unknown): EquipmentStats => {
|
||||
const equipName = ctx.helper.string("equipName", _equipName);
|
||||
checkGangApiAccess(ctx);
|
||||
const equipment = GangMemberUpgrades[equipName];
|
||||
if (!equipment) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid equipment: ${equipName}`);
|
||||
}
|
||||
const typecheck: EquipmentStats = equipment.mults;
|
||||
return Object.assign({}, typecheck) as any;
|
||||
},
|
||||
purchaseEquipment:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown, _equipName: unknown): boolean => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
const equipName = ctx.helper.string("equipName", _equipName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember(ctx, memberName);
|
||||
const equipment = GangMemberUpgrades[equipName];
|
||||
if (!equipment) return false;
|
||||
const res = member.buyUpgrade(equipment, player, gang);
|
||||
if (res) {
|
||||
workerScript.log("gang.purchaseEquipment", () => `Purchased '${equipName}' for Gang member '${memberName}'`);
|
||||
} else {
|
||||
workerScript.log(
|
||||
"gang.purchaseEquipment",
|
||||
() => `Failed to purchase '${equipName}' for Gang member '${memberName}'`,
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
ascendMember: function (_memberName: unknown): GangMemberAscension | undefined {
|
||||
updateRam("ascendMember");
|
||||
const memberName = helper.string("ascendMember", "memberName", _memberName);
|
||||
checkGangApiAccess("ascendMember");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember("ascendMember", memberName);
|
||||
if (!member.canAscend()) return;
|
||||
return gang.ascendMember(member, workerScript);
|
||||
},
|
||||
getAscensionResult: function (_memberName: unknown): GangMemberAscension | undefined {
|
||||
updateRam("getAscensionResult");
|
||||
const memberName = helper.string("getAscensionResult", "memberName", _memberName);
|
||||
checkGangApiAccess("getAscensionResult");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember("getAscensionResult", memberName);
|
||||
if (!member.canAscend()) return;
|
||||
return {
|
||||
respect: member.earnedRespect,
|
||||
...member.getAscensionResults(),
|
||||
};
|
||||
},
|
||||
setTerritoryWarfare: function (_engage: unknown): void {
|
||||
updateRam("setTerritoryWarfare");
|
||||
const engage = helper.boolean(_engage);
|
||||
checkGangApiAccess("setTerritoryWarfare");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (engage) {
|
||||
gang.territoryWarfareEngaged = true;
|
||||
workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare");
|
||||
} else {
|
||||
gang.territoryWarfareEngaged = false;
|
||||
workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare");
|
||||
}
|
||||
},
|
||||
getChanceToWinClash: function (_otherGang: unknown): number {
|
||||
updateRam("getChanceToWinClash");
|
||||
const otherGang = helper.string("getChanceToWinClash", "otherGang", _otherGang);
|
||||
checkGangApiAccess("getChanceToWinClash");
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (AllGangs[otherGang] == null) {
|
||||
throw helper.makeRuntimeErrorMsg(`gang.getChanceToWinClash`, `Invalid gang: ${otherGang}`);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
ascendMember:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown): GangMemberAscension | undefined => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember(ctx, memberName);
|
||||
if (!member.canAscend()) return;
|
||||
return gang.ascendMember(member, workerScript);
|
||||
},
|
||||
getAscensionResult:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_memberName: unknown): GangMemberAscension | undefined => {
|
||||
const memberName = ctx.helper.string("memberName", _memberName);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
const member = getGangMember(ctx, memberName);
|
||||
if (!member.canAscend()) return;
|
||||
return {
|
||||
respect: member.earnedRespect,
|
||||
...member.getAscensionResults(),
|
||||
};
|
||||
},
|
||||
setTerritoryWarfare:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_engage: unknown): void => {
|
||||
const engage = ctx.helper.boolean(_engage);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (engage) {
|
||||
gang.territoryWarfareEngaged = true;
|
||||
workerScript.log("gang.setTerritoryWarfare", () => "Engaging in Gang Territory Warfare");
|
||||
} else {
|
||||
gang.territoryWarfareEngaged = false;
|
||||
workerScript.log("gang.setTerritoryWarfare", () => "Disengaging in Gang Territory Warfare");
|
||||
}
|
||||
},
|
||||
getChanceToWinClash:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_otherGang: unknown): number => {
|
||||
const otherGang = ctx.helper.string("otherGang", _otherGang);
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
if (AllGangs[otherGang] == null) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid gang: ${otherGang}`);
|
||||
}
|
||||
|
||||
const playerPower = AllGangs[gang.facName].power;
|
||||
const otherPower = AllGangs[otherGang].power;
|
||||
const playerPower = AllGangs[gang.facName].power;
|
||||
const otherPower = AllGangs[otherGang].power;
|
||||
|
||||
return playerPower / (otherPower + playerPower);
|
||||
},
|
||||
getBonusTime: function (): number {
|
||||
updateRam("getBonusTime");
|
||||
checkGangApiAccess("getBonusTime");
|
||||
return playerPower / (otherPower + playerPower);
|
||||
},
|
||||
getBonusTime: (ctx: NetscriptContext) => (): number => {
|
||||
checkGangApiAccess(ctx);
|
||||
const gang = player.gang;
|
||||
if (gang === null) throw new Error("Should not be called without Gang");
|
||||
return Math.round(gang.storedCycles / 5);
|
||||
return Math.round(gang.storedCycles / 5) * 1000;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,104 +1,97 @@
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
|
||||
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../PersonObjects/Grafting/GraftingHelpers";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { Router } from "../ui/GameRoot";
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
|
||||
export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGrafting {
|
||||
const checkGraftingAPIAccess = (func: string): void => {
|
||||
export function NetscriptGrafting(player: IPlayer): InternalAPI<IGrafting> {
|
||||
const checkGraftingAPIAccess = (ctx: NetscriptContext): void => {
|
||||
if (!player.canAccessGrafting()) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
`grafting.${func}`,
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
"You do not currently have access to the Grafting API. This is either because you are not in BitNode 10 or because you do not have Source-File 10",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "grafting", funcName));
|
||||
|
||||
return {
|
||||
getAugmentationGraftPrice: (_augName: unknown): number => {
|
||||
updateRam("getAugmentationGraftPrice");
|
||||
const augName = helper.string("getAugmentationGraftPrice", "augName", _augName);
|
||||
checkGraftingAPIAccess("getAugmentationGraftPrice");
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
||||
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftPrice", `Invalid aug: ${augName}`);
|
||||
}
|
||||
const graftableAug = new GraftableAugmentation(Augmentations[augName]);
|
||||
return graftableAug.cost;
|
||||
},
|
||||
getAugmentationGraftPrice:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_augName: unknown): number => {
|
||||
const augName = ctx.helper.string("augName", _augName);
|
||||
checkGraftingAPIAccess(ctx);
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
|
||||
}
|
||||
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||
return graftableAug.cost;
|
||||
},
|
||||
|
||||
getAugmentationGraftTime: (_augName: string): number => {
|
||||
updateRam("getAugmentationGraftTime");
|
||||
const augName = helper.string("getAugmentationGraftTime", "augName", _augName);
|
||||
checkGraftingAPIAccess("getAugmentationGraftTime");
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
||||
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftTime", `Invalid aug: ${augName}`);
|
||||
}
|
||||
const graftableAug = new GraftableAugmentation(Augmentations[augName]);
|
||||
return calculateGraftingTimeWithBonus(player, graftableAug);
|
||||
},
|
||||
getAugmentationGraftTime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_augName: string): number => {
|
||||
const augName = ctx.helper.string("augName", _augName);
|
||||
checkGraftingAPIAccess(ctx);
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
|
||||
}
|
||||
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||
return calculateGraftingTimeWithBonus(player, graftableAug);
|
||||
},
|
||||
|
||||
getGraftableAugmentations: (): string[] => {
|
||||
updateRam("getGraftableAugmentations");
|
||||
checkGraftingAPIAccess("getGraftableAugmentations");
|
||||
getGraftableAugmentations: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkGraftingAPIAccess(ctx);
|
||||
const graftableAugs = getGraftingAvailableAugs(player);
|
||||
return graftableAugs;
|
||||
},
|
||||
|
||||
graftAugmentation: (_augName: string, _focus: unknown = true): boolean => {
|
||||
updateRam("graftAugmentation");
|
||||
const augName = helper.string("graftAugmentation", "augName", _augName);
|
||||
const focus = helper.boolean(_focus);
|
||||
checkGraftingAPIAccess("graftAugmentation");
|
||||
if (player.city !== CityName.NewTokyo) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"grafting.graftAugmentation",
|
||||
"You must be in New Tokyo to begin grafting an Augmentation.",
|
||||
);
|
||||
}
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
||||
workerScript.log("grafting.graftAugmentation", () => `Invalid aug: ${augName}`);
|
||||
return false;
|
||||
}
|
||||
graftAugmentation:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_augName: string, _focus: unknown = true): boolean => {
|
||||
const augName = ctx.helper.string("augName", _augName);
|
||||
const focus = ctx.helper.boolean(_focus);
|
||||
checkGraftingAPIAccess(ctx);
|
||||
if (player.city !== CityName.NewTokyo) {
|
||||
throw ctx.makeRuntimeErrorMsg("You must be in New Tokyo to begin grafting an Augmentation.");
|
||||
}
|
||||
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||
ctx.log(() => `Invalid aug: ${augName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const wasFocusing = player.focus;
|
||||
if (player.isWorking) {
|
||||
const txt = player.singularityStopWork();
|
||||
workerScript.log("graftAugmentation", () => txt);
|
||||
}
|
||||
const wasFocusing = player.focus;
|
||||
if (player.isWorking) {
|
||||
const txt = player.singularityStopWork();
|
||||
ctx.log(() => txt);
|
||||
}
|
||||
|
||||
const craftableAug = new GraftableAugmentation(Augmentations[augName]);
|
||||
if (player.money < craftableAug.cost) {
|
||||
workerScript.log("grafting.graftAugmentation", () => `You don't have enough money to craft ${augName}`);
|
||||
return false;
|
||||
}
|
||||
const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||
if (player.money < craftableAug.cost) {
|
||||
ctx.log(() => `You don't have enough money to craft ${augName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasAugmentationPrereqs(craftableAug.augmentation)) {
|
||||
workerScript.log("grafting.graftAugmentation", () => `You don't have the pre-requisites for ${augName}`);
|
||||
return false;
|
||||
}
|
||||
if (!hasAugmentationPrereqs(craftableAug.augmentation)) {
|
||||
ctx.log(() => `You don't have the pre-requisites for ${augName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
player.loseMoney(craftableAug.cost, "augmentations");
|
||||
player.startGraftAugmentationWork(augName, craftableAug.time);
|
||||
player.loseMoney(craftableAug.cost, "augmentations");
|
||||
player.startGraftAugmentationWork(augName, craftableAug.time);
|
||||
|
||||
if (focus) {
|
||||
player.startFocusing();
|
||||
Router.toWork();
|
||||
} else if (wasFocusing) {
|
||||
player.stopFocusing();
|
||||
Router.toTerminal();
|
||||
}
|
||||
if (focus) {
|
||||
player.startFocusing();
|
||||
Router.toWork();
|
||||
} else if (wasFocusing) {
|
||||
player.stopFocusing();
|
||||
Router.toTerminal();
|
||||
}
|
||||
|
||||
workerScript.log("grafting.graftAugmentation", () => `Began grafting Augmentation ${augName}.`);
|
||||
return true;
|
||||
},
|
||||
ctx.log(() => `Began grafting Augmentation ${augName}.`);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { HacknetServerConstants } from "../Hacknet/data/Constants";
|
||||
@ -21,12 +20,13 @@ import { HashUpgrade } from "../Hacknet/HashUpgrade";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
|
||||
import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IHacknet {
|
||||
export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript): InternalAPI<IHacknet> {
|
||||
// Utility function to get Hacknet Node object
|
||||
const getHacknetNode = function (i: number, callingFn = ""): HacknetNode | HacknetServer {
|
||||
const getHacknetNode = function (ctx: NetscriptContext, i: number): HacknetNode | HacknetServer {
|
||||
if (i < 0 || i >= player.hacknetNodes.length) {
|
||||
throw helper.makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i);
|
||||
throw ctx.makeRuntimeErrorMsg("Index specified for Hacknet Node is out-of-bounds: " + i);
|
||||
}
|
||||
|
||||
if (hasHacknetServers(player)) {
|
||||
@ -35,8 +35,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
||||
const hserver = GetServer(hi);
|
||||
if (!(hserver instanceof HacknetServer)) throw new Error("hacknet server was not actually hacknet server");
|
||||
if (hserver == null) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
callingFn,
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
`Could not get Hacknet Server for index ${i}. This is probably a bug, please report to game dev`,
|
||||
);
|
||||
}
|
||||
@ -50,162 +49,186 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
||||
};
|
||||
|
||||
return {
|
||||
numNodes: function (): number {
|
||||
numNodes: () => (): number => {
|
||||
return player.hacknetNodes.length;
|
||||
},
|
||||
maxNumNodes: function (): number {
|
||||
maxNumNodes: () => (): number => {
|
||||
if (hasHacknetServers(player)) {
|
||||
return HacknetServerConstants.MaxServers;
|
||||
}
|
||||
return Infinity;
|
||||
},
|
||||
purchaseNode: function (): number {
|
||||
purchaseNode: () => (): number => {
|
||||
return purchaseHacknet(player);
|
||||
},
|
||||
getPurchaseNodeCost: function (): number {
|
||||
getPurchaseNodeCost: () => (): number => {
|
||||
if (hasHacknetServers(player)) {
|
||||
return getCostOfNextHacknetServer(player);
|
||||
} else {
|
||||
return getCostOfNextHacknetNode(player);
|
||||
}
|
||||
},
|
||||
getNodeStats: function (_i: unknown): NodeStats {
|
||||
const i = helper.number("getNodeStats", "i", _i);
|
||||
const node = getHacknetNode(i, "getNodeStats");
|
||||
const hasUpgraded = hasHacknetServers(player);
|
||||
const res: any = {
|
||||
name: node instanceof HacknetServer ? node.hostname : node.name,
|
||||
level: node.level,
|
||||
ram: node instanceof HacknetServer ? node.maxRam : node.ram,
|
||||
ramUsed: node instanceof HacknetServer ? node.ramUsed : undefined,
|
||||
cores: node.cores,
|
||||
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
|
||||
timeOnline: node.onlineTimeSeconds,
|
||||
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
|
||||
};
|
||||
getNodeStats:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown): NodeStats => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
const hasUpgraded = hasHacknetServers(player);
|
||||
const res: any = {
|
||||
name: node instanceof HacknetServer ? node.hostname : node.name,
|
||||
level: node.level,
|
||||
ram: node instanceof HacknetServer ? node.maxRam : node.ram,
|
||||
ramUsed: node instanceof HacknetServer ? node.ramUsed : undefined,
|
||||
cores: node.cores,
|
||||
production: node instanceof HacknetServer ? node.hashRate : node.moneyGainRatePerSecond,
|
||||
timeOnline: node.onlineTimeSeconds,
|
||||
totalProduction: node instanceof HacknetServer ? node.totalHashesGenerated : node.totalMoneyGenerated,
|
||||
};
|
||||
|
||||
if (hasUpgraded && node instanceof HacknetServer) {
|
||||
res.cache = node.cache;
|
||||
res.hashCapacity = node.hashCapacity;
|
||||
}
|
||||
if (hasUpgraded && node instanceof HacknetServer) {
|
||||
res.cache = node.cache;
|
||||
res.hashCapacity = node.hashCapacity;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
upgradeLevel: function (_i: unknown, _n: unknown = 1): boolean {
|
||||
const i = helper.number("upgradeLevel", "i", _i);
|
||||
const n = helper.number("upgradeLevel", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeLevel");
|
||||
return purchaseLevelUpgrade(player, node, n);
|
||||
},
|
||||
upgradeRam: function (_i: unknown, _n: unknown = 1): boolean {
|
||||
const i = helper.number("upgradeRam", "i", _i);
|
||||
const n = helper.number("upgradeRam", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeRam");
|
||||
return purchaseRamUpgrade(player, node, n);
|
||||
},
|
||||
upgradeCore: function (_i: unknown, _n: unknown = 1): boolean {
|
||||
const i = helper.number("upgradeCore", "i", _i);
|
||||
const n = helper.number("upgradeCore", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeCore");
|
||||
return purchaseCoreUpgrade(player, node, n);
|
||||
},
|
||||
upgradeCache: function (_i: unknown, _n: unknown = 1): boolean {
|
||||
const i = helper.number("upgradeCache", "i", _i);
|
||||
const n = helper.number("upgradeCache", "n", _n);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return false;
|
||||
}
|
||||
const node = getHacknetNode(i, "upgradeCache");
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
|
||||
return false;
|
||||
}
|
||||
const res = purchaseCacheUpgrade(player, node, n);
|
||||
if (res) {
|
||||
updateHashManagerCapacity(player);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
getLevelUpgradeCost: function (_i: unknown, _n: unknown = 1): number {
|
||||
const i = helper.number("getLevelUpgradeCost", "i", _i);
|
||||
const n = helper.number("getLevelUpgradeCost", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeLevel");
|
||||
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
|
||||
},
|
||||
getRamUpgradeCost: function (_i: unknown, _n: unknown = 1): number {
|
||||
const i = helper.number("getRamUpgradeCost", "i", _i);
|
||||
const n = helper.number("getRamUpgradeCost", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeRam");
|
||||
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
|
||||
},
|
||||
getCoreUpgradeCost: function (_i: unknown, _n: unknown = 1): number {
|
||||
const i = helper.number("getCoreUpgradeCost", "i", _i);
|
||||
const n = helper.number("getCoreUpgradeCost", "n", _n);
|
||||
const node = getHacknetNode(i, "upgradeCore");
|
||||
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
|
||||
},
|
||||
getCacheUpgradeCost: function (_i: unknown, _n: unknown = 1): number {
|
||||
const i = helper.number("getCacheUpgradeCost", "i", _i);
|
||||
const n = helper.number("getCacheUpgradeCost", "n", _n);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return Infinity;
|
||||
}
|
||||
const node = getHacknetNode(i, "upgradeCache");
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
||||
return -1;
|
||||
}
|
||||
return node.calculateCacheUpgradeCost(n);
|
||||
},
|
||||
numHashes: function (): number {
|
||||
return res;
|
||||
},
|
||||
upgradeLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): boolean => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return purchaseLevelUpgrade(player, node, n);
|
||||
},
|
||||
upgradeRam:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): boolean => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return purchaseRamUpgrade(player, node, n);
|
||||
},
|
||||
upgradeCore:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): boolean => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return purchaseCoreUpgrade(player, node, n);
|
||||
},
|
||||
upgradeCache:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): boolean => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return false;
|
||||
}
|
||||
const node = getHacknetNode(ctx, i);
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
|
||||
return false;
|
||||
}
|
||||
const res = purchaseCacheUpgrade(player, node, n);
|
||||
if (res) {
|
||||
updateHashManagerCapacity(player);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
getLevelUpgradeCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): number => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
|
||||
},
|
||||
getRamUpgradeCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): number => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
|
||||
},
|
||||
getCoreUpgradeCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): number => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
const node = getHacknetNode(ctx, i);
|
||||
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
|
||||
},
|
||||
getCacheUpgradeCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_i: unknown, _n: unknown = 1): number => {
|
||||
const i = ctx.helper.number("i", _i);
|
||||
const n = ctx.helper.number("n", _n);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return Infinity;
|
||||
}
|
||||
const node = getHacknetNode(ctx, i);
|
||||
if (!(node instanceof HacknetServer)) {
|
||||
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
||||
return -1;
|
||||
}
|
||||
return node.calculateCacheUpgradeCost(n);
|
||||
},
|
||||
numHashes: () => (): number => {
|
||||
if (!hasHacknetServers(player)) {
|
||||
return 0;
|
||||
}
|
||||
return player.hashManager.hashes;
|
||||
},
|
||||
hashCapacity: function (): number {
|
||||
hashCapacity: () => (): number => {
|
||||
if (!hasHacknetServers(player)) {
|
||||
return 0;
|
||||
}
|
||||
return player.hashManager.capacity;
|
||||
},
|
||||
hashCost: function (_upgName: unknown): number {
|
||||
const upgName = helper.string("hashCost", "upgName", _upgName);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return Infinity;
|
||||
}
|
||||
hashCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_upgName: unknown): number => {
|
||||
const upgName = ctx.helper.string("upgName", _upgName);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return player.hashManager.getUpgradeCost(upgName);
|
||||
},
|
||||
spendHashes: function (_upgName: unknown, _upgTarget: unknown = ""): boolean {
|
||||
const upgName = helper.string("spendHashes", "upgName", _upgName);
|
||||
const upgTarget = helper.string("spendHashes", "upgTarget", _upgTarget);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return false;
|
||||
}
|
||||
return purchaseHashUpgrade(player, upgName, upgTarget);
|
||||
},
|
||||
getHashUpgrades: function (): string[] {
|
||||
return player.hashManager.getUpgradeCost(upgName);
|
||||
},
|
||||
spendHashes:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_upgName: unknown, _upgTarget: unknown = ""): boolean => {
|
||||
const upgName = ctx.helper.string("upgName", _upgName);
|
||||
const upgTarget = ctx.helper.string("upgTarget", _upgTarget);
|
||||
if (!hasHacknetServers(player)) {
|
||||
return false;
|
||||
}
|
||||
return purchaseHashUpgrade(player, upgName, upgTarget);
|
||||
},
|
||||
getHashUpgrades: () => (): string[] => {
|
||||
if (!hasHacknetServers(player)) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name);
|
||||
},
|
||||
getHashUpgradeLevel: function (_upgName: unknown): number {
|
||||
const upgName = helper.string("getHashUpgradeLevel", "upgName", _upgName);
|
||||
const level = player.hashManager.upgrades[upgName];
|
||||
if (level === undefined) {
|
||||
throw helper.makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`);
|
||||
}
|
||||
return level;
|
||||
},
|
||||
getStudyMult: function (): number {
|
||||
getHashUpgradeLevel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_upgName: unknown): number => {
|
||||
const upgName = ctx.helper.string("upgName", _upgName);
|
||||
const level = player.hashManager.upgrades[upgName];
|
||||
if (level === undefined) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid Hash Upgrade: ${upgName}`);
|
||||
}
|
||||
return level;
|
||||
},
|
||||
getStudyMult: () => (): number => {
|
||||
if (!hasHacknetServers(player)) {
|
||||
return 1;
|
||||
}
|
||||
return player.hashManager.getStudyMult();
|
||||
},
|
||||
getTrainingMult: function (): number {
|
||||
getTrainingMult: () => (): number => {
|
||||
if (!hasHacknetServers(player)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
|
||||
import { startWorkerScript } from "../NetscriptWorker";
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
||||
@ -49,6 +49,7 @@ import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
|
||||
import { enterBitNode } from "../RedPill";
|
||||
import { FactionNames } from "../Faction/data/FactionNames";
|
||||
import { ClassType, WorkType } from "../utils/WorkType";
|
||||
|
||||
export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> {
|
||||
const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation {
|
||||
@ -56,7 +57,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
throw _ctx.helper.makeRuntimeErrorMsg(`Invalid augmentation: '${name}'`);
|
||||
}
|
||||
|
||||
return Augmentations[name];
|
||||
return StaticAugmentations[name];
|
||||
};
|
||||
|
||||
const getFaction = function (_ctx: NetscriptContext, name: string): Faction {
|
||||
@ -83,7 +84,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
if (script.filename === cbScript) {
|
||||
const ramUsage = script.ramUsage;
|
||||
const ramAvailable = home.maxRam - home.ramUsed;
|
||||
if (ramUsage > ramAvailable) {
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
return; // Not enough RAM
|
||||
}
|
||||
const runningScriptObj = new RunningScript(script, []); // No args
|
||||
@ -122,7 +123,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
_ctx.helper.checkSingularityAccess();
|
||||
const augName = _ctx.helper.string("augName", _augName);
|
||||
const aug = getAugmentation(_ctx, augName);
|
||||
return [aug.baseRepRequirement, aug.baseCost];
|
||||
const costs = aug.getCost(player);
|
||||
return [costs.repCost, costs.moneyCost];
|
||||
},
|
||||
getAugmentationPrereq: (_ctx: NetscriptContext) =>
|
||||
function (_augName: unknown): string[] {
|
||||
@ -136,14 +138,14 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
_ctx.helper.checkSingularityAccess();
|
||||
const augName = _ctx.helper.string("augName", _augName);
|
||||
const aug = getAugmentation(_ctx, augName);
|
||||
return aug.baseCost;
|
||||
return aug.getCost(player).moneyCost;
|
||||
},
|
||||
getAugmentationRepReq: (_ctx: NetscriptContext) =>
|
||||
function (_augName: unknown): number {
|
||||
_ctx.helper.checkSingularityAccess();
|
||||
const augName = _ctx.helper.string("augName", _augName);
|
||||
const aug = getAugmentation(_ctx, augName);
|
||||
return aug.baseRepRequirement;
|
||||
return aug.getCost(player).repCost;
|
||||
},
|
||||
getAugmentationStats: (_ctx: NetscriptContext) =>
|
||||
function (_augName: unknown): AugmentationStats {
|
||||
@ -183,7 +185,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
}
|
||||
}
|
||||
|
||||
if (fac.playerReputation < aug.baseRepRequirement) {
|
||||
if (fac.playerReputation < aug.getCost(player).repCost) {
|
||||
_ctx.log(() => `You do not have enough reputation with '${fac.name}'.`);
|
||||
return false;
|
||||
}
|
||||
@ -298,25 +300,25 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
return false;
|
||||
}
|
||||
|
||||
let task = "";
|
||||
let task: ClassType;
|
||||
switch (className.toLowerCase()) {
|
||||
case "Study Computer Science".toLowerCase():
|
||||
task = CONSTANTS.ClassStudyComputerScience;
|
||||
task = ClassType.StudyComputerScience;
|
||||
break;
|
||||
case "Data Structures".toLowerCase():
|
||||
task = CONSTANTS.ClassDataStructures;
|
||||
task = ClassType.DataStructures;
|
||||
break;
|
||||
case "Networks".toLowerCase():
|
||||
task = CONSTANTS.ClassNetworks;
|
||||
task = ClassType.Networks;
|
||||
break;
|
||||
case "Algorithms".toLowerCase():
|
||||
task = CONSTANTS.ClassAlgorithms;
|
||||
task = ClassType.Algorithms;
|
||||
break;
|
||||
case "Management".toLowerCase():
|
||||
task = CONSTANTS.ClassManagement;
|
||||
task = ClassType.Management;
|
||||
break;
|
||||
case "Leadership".toLowerCase():
|
||||
task = CONSTANTS.ClassLeadership;
|
||||
task = ClassType.Leadership;
|
||||
break;
|
||||
default:
|
||||
_ctx.log(() => `Invalid class name: ${className}.`);
|
||||
@ -415,19 +417,19 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
switch (stat.toLowerCase()) {
|
||||
case "strength".toLowerCase():
|
||||
case "str".toLowerCase():
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymStrength);
|
||||
player.startClass(costMult, expMult, ClassType.GymStrength);
|
||||
break;
|
||||
case "defense".toLowerCase():
|
||||
case "def".toLowerCase():
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymDefense);
|
||||
player.startClass(costMult, expMult, ClassType.GymDefense);
|
||||
break;
|
||||
case "dexterity".toLowerCase():
|
||||
case "dex".toLowerCase():
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymDexterity);
|
||||
player.startClass(costMult, expMult, ClassType.GymDexterity);
|
||||
break;
|
||||
case "agility".toLowerCase():
|
||||
case "agi".toLowerCase():
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymAgility);
|
||||
player.startClass(costMult, expMult, ClassType.GymAgility);
|
||||
break;
|
||||
default:
|
||||
_ctx.log(() => `Invalid stat: ${stat}.`);
|
||||
@ -650,11 +652,11 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
}
|
||||
if (
|
||||
!(
|
||||
player.workType == CONSTANTS.WorkTypeFaction ||
|
||||
player.workType == CONSTANTS.WorkTypeCompany ||
|
||||
player.workType == CONSTANTS.WorkTypeCompanyPartTime ||
|
||||
player.workType == CONSTANTS.WorkTypeCreateProgram ||
|
||||
player.workType == CONSTANTS.WorkTypeStudyClass
|
||||
player.workType === WorkType.Faction ||
|
||||
player.workType === WorkType.Company ||
|
||||
player.workType === WorkType.CompanyPartTime ||
|
||||
player.workType === WorkType.CreateProgram ||
|
||||
player.workType === WorkType.StudyClass
|
||||
)
|
||||
) {
|
||||
throw _ctx.helper.makeRuntimeErrorMsg("Cannot change focus for current job");
|
||||
@ -947,6 +949,12 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
}
|
||||
return res;
|
||||
},
|
||||
quitJob: (_ctx: NetscriptContext) =>
|
||||
function (_companyName: unknown): void {
|
||||
_ctx.helper.checkSingularityAccess();
|
||||
const companyName = _ctx.helper.string("companyName", _companyName);
|
||||
player.quitJob(companyName);
|
||||
},
|
||||
getCompanyRep: (_ctx: NetscriptContext) =>
|
||||
function (_companyName: unknown): number {
|
||||
_ctx.helper.checkSingularityAccess();
|
||||
@ -1079,7 +1087,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
||||
_ctx.log(() => `Invalid work type: '${type}`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getFactionRep: (_ctx: NetscriptContext) =>
|
||||
function (_facName: unknown): number {
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
|
||||
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { findCrime } from "../Crime/CrimeHelpers";
|
||||
|
||||
@ -17,22 +14,22 @@ import {
|
||||
SleeveTask,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { checkEnum } from "../utils/helpers/checkEnum";
|
||||
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): ISleeve {
|
||||
const checkSleeveAPIAccess = function (func: string): void {
|
||||
export function NetscriptSleeve(player: IPlayer): InternalAPI<ISleeve> {
|
||||
const checkSleeveAPIAccess = function (ctx: NetscriptContext): void {
|
||||
if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
`sleeve.${func}`,
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
"You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const checkSleeveNumber = function (func: string, sleeveNumber: number): void {
|
||||
const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void {
|
||||
if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) {
|
||||
const msg = `Invalid sleeve number: ${sleeveNumber}`;
|
||||
workerScript.log(func, () => msg);
|
||||
throw helper.makeRuntimeErrorMsg(`sleeve.${func}`, msg);
|
||||
ctx.log(() => msg);
|
||||
throw ctx.makeRuntimeErrorMsg(msg);
|
||||
}
|
||||
};
|
||||
|
||||
@ -50,265 +47,268 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
};
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "sleeve", funcName));
|
||||
|
||||
return {
|
||||
getNumSleeves: function (): number {
|
||||
updateRam("getNumSleeves");
|
||||
checkSleeveAPIAccess("getNumSleeves");
|
||||
getNumSleeves: (ctx: NetscriptContext) => (): number => {
|
||||
checkSleeveAPIAccess(ctx);
|
||||
return player.sleeves.length;
|
||||
},
|
||||
setToShockRecovery: function (_sleeveNumber: unknown): boolean {
|
||||
updateRam("setToShockRecovery");
|
||||
const sleeveNumber = helper.number("setToShockRecovery", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("setToShockRecovery");
|
||||
checkSleeveNumber("setToShockRecovery", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].shockRecovery(player);
|
||||
},
|
||||
setToSynchronize: function (_sleeveNumber: unknown): boolean {
|
||||
updateRam("setToSynchronize");
|
||||
const sleeveNumber = helper.number("setToSynchronize", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("setToSynchronize");
|
||||
checkSleeveNumber("setToSynchronize", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].synchronize(player);
|
||||
},
|
||||
setToCommitCrime: function (_sleeveNumber: unknown, _crimeRoughName: unknown): boolean {
|
||||
updateRam("setToCommitCrime");
|
||||
const sleeveNumber = helper.number("setToCommitCrime", "sleeveNumber", _sleeveNumber);
|
||||
const crimeRoughName = helper.string("setToCommitCrime", "crimeName", _crimeRoughName);
|
||||
checkSleeveAPIAccess("setToCommitCrime");
|
||||
checkSleeveNumber("setToCommitCrime", sleeveNumber);
|
||||
const crime = findCrime(crimeRoughName);
|
||||
if (crime === null) {
|
||||
return false;
|
||||
}
|
||||
return player.sleeves[sleeveNumber].commitCrime(player, crime.name);
|
||||
},
|
||||
setToUniversityCourse: function (_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean {
|
||||
updateRam("setToUniversityCourse");
|
||||
const sleeveNumber = helper.number("setToUniversityCourse", "sleeveNumber", _sleeveNumber);
|
||||
const universityName = helper.string("setToUniversityCourse", "universityName", _universityName);
|
||||
const className = helper.string("setToUniversityCourse", "className", _className);
|
||||
checkSleeveAPIAccess("setToUniversityCourse");
|
||||
checkSleeveNumber("setToUniversityCourse", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
|
||||
},
|
||||
travel: function (_sleeveNumber: unknown, _cityName: unknown): boolean {
|
||||
updateRam("travel");
|
||||
const sleeveNumber = helper.number("travel", "sleeveNumber", _sleeveNumber);
|
||||
const cityName = helper.string("travel", "cityName", _cityName);
|
||||
checkSleeveAPIAccess("travel");
|
||||
checkSleeveNumber("travel", sleeveNumber);
|
||||
if (checkEnum(CityName, cityName)) {
|
||||
return player.sleeves[sleeveNumber].travel(player, cityName);
|
||||
} else {
|
||||
throw helper.makeRuntimeErrorMsg("sleeve.setToCompanyWork", `Invalid city name: '${cityName}'.`);
|
||||
}
|
||||
},
|
||||
setToCompanyWork: function (_sleeveNumber: unknown, acompanyName: unknown): boolean {
|
||||
updateRam("setToCompanyWork");
|
||||
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", _sleeveNumber);
|
||||
const companyName = helper.string("setToCompanyWork", "companyName", acompanyName);
|
||||
checkSleeveAPIAccess("setToCompanyWork");
|
||||
checkSleeveNumber("setToCompanyWork", sleeveNumber);
|
||||
|
||||
// Cannot work at the same company that another sleeve is working at
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
if (i === sleeveNumber) {
|
||||
continue;
|
||||
setToShockRecovery:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].shockRecovery(player);
|
||||
},
|
||||
setToSynchronize:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].synchronize(player);
|
||||
},
|
||||
setToCommitCrime:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _crimeRoughName: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const crimeRoughName = ctx.helper.string("crimeName", _crimeRoughName);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
const crime = findCrime(crimeRoughName);
|
||||
if (crime === null) {
|
||||
return false;
|
||||
}
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sleeve.setToCompanyWork",
|
||||
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
|
||||
return player.sleeves[sleeveNumber].commitCrime(player, crime.name);
|
||||
},
|
||||
setToUniversityCourse:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _universityName: unknown, _className: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const universityName = ctx.helper.string("universityName", _universityName);
|
||||
const className = ctx.helper.string("className", _className);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className);
|
||||
},
|
||||
travel:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _cityName: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const cityName = ctx.helper.string("cityName", _cityName);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
if (checkEnum(CityName, cityName)) {
|
||||
return player.sleeves[sleeveNumber].travel(player, cityName);
|
||||
} else {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid city name: '${cityName}'.`);
|
||||
}
|
||||
},
|
||||
setToCompanyWork:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, acompanyName: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const companyName = ctx.helper.string("companyName", acompanyName);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
// Cannot work at the same company that another sleeve is working at
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
if (i === sleeveNumber) {
|
||||
continue;
|
||||
}
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return player.sleeves[sleeveNumber].workForCompany(player, companyName);
|
||||
},
|
||||
setToFactionWork:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _factionName: unknown, _workType: unknown): boolean | undefined => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const factionName = ctx.helper.string("factionName", _factionName);
|
||||
const workType = ctx.helper.string("workType", _workType);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
// Cannot work at the same faction that another sleeve is working at
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
if (i === sleeveNumber) {
|
||||
continue;
|
||||
}
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (player.gang && player.gang.facName == factionName) {
|
||||
throw ctx.makeRuntimeErrorMsg(
|
||||
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return player.sleeves[sleeveNumber].workForCompany(player, companyName);
|
||||
},
|
||||
setToFactionWork: function (
|
||||
_sleeveNumber: unknown,
|
||||
_factionName: unknown,
|
||||
_workType: unknown,
|
||||
): boolean | undefined {
|
||||
updateRam("setToFactionWork");
|
||||
const sleeveNumber = helper.number("setToFactionWork", "sleeveNumber", _sleeveNumber);
|
||||
const factionName = helper.string("setToFactionWork", "factionName", _factionName);
|
||||
const workType = helper.string("setToFactionWork", "workType", _workType);
|
||||
checkSleeveAPIAccess("setToFactionWork");
|
||||
checkSleeveNumber("setToFactionWork", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType);
|
||||
},
|
||||
setToGymWorkout:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const gymName = ctx.helper.string("gymName", _gymName);
|
||||
const stat = ctx.helper.string("stat", _stat);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
// Cannot work at the same faction that another sleeve is working at
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
if (i === sleeveNumber) {
|
||||
continue;
|
||||
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat);
|
||||
},
|
||||
getSleeveStats:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): SleeveSkills => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
return getSleeveStats(sleeveNumber);
|
||||
},
|
||||
getTask:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): SleeveTask => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
const sl = player.sleeves[sleeveNumber];
|
||||
return {
|
||||
task: SleeveTaskType[sl.currentTask],
|
||||
crime: sl.crimeType,
|
||||
location: sl.currentTaskLocation,
|
||||
gymStatType: sl.gymStatType,
|
||||
factionWorkType: FactionWorkType[sl.factionWorkType],
|
||||
};
|
||||
},
|
||||
getInformation:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): SleeveInformation => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
const sl = player.sleeves[sleeveNumber];
|
||||
return {
|
||||
tor: false,
|
||||
city: sl.city,
|
||||
hp: sl.hp,
|
||||
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player.
|
||||
jobTitle: Object.values(player.jobs),
|
||||
maxHp: sl.max_hp,
|
||||
|
||||
mult: {
|
||||
agility: sl.agility_mult,
|
||||
agilityExp: sl.agility_exp_mult,
|
||||
charisma: sl.charisma_mult,
|
||||
charismaExp: sl.charisma_exp_mult,
|
||||
companyRep: sl.company_rep_mult,
|
||||
crimeMoney: sl.crime_money_mult,
|
||||
crimeSuccess: sl.crime_success_mult,
|
||||
defense: sl.defense_mult,
|
||||
defenseExp: sl.defense_exp_mult,
|
||||
dexterity: sl.dexterity_mult,
|
||||
dexterityExp: sl.dexterity_exp_mult,
|
||||
factionRep: sl.faction_rep_mult,
|
||||
hacking: sl.hacking_mult,
|
||||
hackingExp: sl.hacking_exp_mult,
|
||||
strength: sl.strength_mult,
|
||||
strengthExp: sl.strength_exp_mult,
|
||||
workMoney: sl.work_money_mult,
|
||||
},
|
||||
|
||||
timeWorked: sl.currentTaskTime,
|
||||
earningsForSleeves: {
|
||||
workHackExpGain: sl.earningsForSleeves.hack,
|
||||
workStrExpGain: sl.earningsForSleeves.str,
|
||||
workDefExpGain: sl.earningsForSleeves.def,
|
||||
workDexExpGain: sl.earningsForSleeves.dex,
|
||||
workAgiExpGain: sl.earningsForSleeves.agi,
|
||||
workChaExpGain: sl.earningsForSleeves.cha,
|
||||
workMoneyGain: sl.earningsForSleeves.money,
|
||||
},
|
||||
earningsForPlayer: {
|
||||
workHackExpGain: sl.earningsForPlayer.hack,
|
||||
workStrExpGain: sl.earningsForPlayer.str,
|
||||
workDefExpGain: sl.earningsForPlayer.def,
|
||||
workDexExpGain: sl.earningsForPlayer.dex,
|
||||
workAgiExpGain: sl.earningsForPlayer.agi,
|
||||
workChaExpGain: sl.earningsForPlayer.cha,
|
||||
workMoneyGain: sl.earningsForPlayer.money,
|
||||
},
|
||||
earningsForTask: {
|
||||
workHackExpGain: sl.earningsForTask.hack,
|
||||
workStrExpGain: sl.earningsForTask.str,
|
||||
workDefExpGain: sl.earningsForTask.def,
|
||||
workDexExpGain: sl.earningsForTask.dex,
|
||||
workAgiExpGain: sl.earningsForTask.agi,
|
||||
workChaExpGain: sl.earningsForTask.cha,
|
||||
workMoneyGain: sl.earningsForTask.money,
|
||||
},
|
||||
workRepGain: sl.getRepGain(player),
|
||||
};
|
||||
},
|
||||
getSleeveAugmentations:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): string[] => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
const augs = [];
|
||||
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
|
||||
augs.push(player.sleeves[sleeveNumber].augmentations[i].name);
|
||||
}
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Faction && other.currentTaskLocation === factionName) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sleeve.setToFactionWork",
|
||||
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because Sleeve ${i} is already working for them.`,
|
||||
);
|
||||
return augs;
|
||||
},
|
||||
getSleevePurchasableAugs:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown): AugmentPair[] => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
|
||||
const augs = [];
|
||||
for (let i = 0; i < purchasableAugs.length; i++) {
|
||||
const aug = purchasableAugs[i];
|
||||
augs.push({
|
||||
name: aug.name,
|
||||
cost: aug.baseCost,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (player.gang && player.gang.facName == factionName) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sleeve.setToFactionWork",
|
||||
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
|
||||
);
|
||||
}
|
||||
return augs;
|
||||
},
|
||||
purchaseSleeveAug:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _augName: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const augName = ctx.helper.string("augName", _augName);
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType);
|
||||
},
|
||||
setToGymWorkout: function (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean {
|
||||
updateRam("setToGymWorkout");
|
||||
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", _sleeveNumber);
|
||||
const gymName = helper.string("setToGymWorkout", "gymName", _gymName);
|
||||
const stat = helper.string("setToGymWorkout", "stat", _stat);
|
||||
checkSleeveAPIAccess("setToGymWorkout");
|
||||
checkSleeveNumber("setToGymWorkout", sleeveNumber);
|
||||
if (getSleeveStats(sleeveNumber).shock > 0) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
||||
}
|
||||
|
||||
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat);
|
||||
},
|
||||
getSleeveStats: function (_sleeveNumber: unknown): SleeveSkills {
|
||||
updateRam("getSleeveStats");
|
||||
const sleeveNumber = helper.number("getSleeveStats", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("getSleeveStats");
|
||||
checkSleeveNumber("getSleeveStats", sleeveNumber);
|
||||
return getSleeveStats(sleeveNumber);
|
||||
},
|
||||
getTask: function (_sleeveNumber: unknown): SleeveTask {
|
||||
updateRam("getTask");
|
||||
const sleeveNumber = helper.number("getTask", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("getTask");
|
||||
checkSleeveNumber("getTask", sleeveNumber);
|
||||
const aug = StaticAugmentations[augName];
|
||||
if (!aug) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid aug: ${augName}`);
|
||||
}
|
||||
|
||||
const sl = player.sleeves[sleeveNumber];
|
||||
return {
|
||||
task: SleeveTaskType[sl.currentTask],
|
||||
crime: sl.crimeType,
|
||||
location: sl.currentTaskLocation,
|
||||
gymStatType: sl.gymStatType,
|
||||
factionWorkType: FactionWorkType[sl.factionWorkType],
|
||||
};
|
||||
},
|
||||
getInformation: function (_sleeveNumber: unknown): SleeveInformation {
|
||||
updateRam("getInformation");
|
||||
const sleeveNumber = helper.number("getInformation", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("getInformation");
|
||||
checkSleeveNumber("getInformation", sleeveNumber);
|
||||
|
||||
const sl = player.sleeves[sleeveNumber];
|
||||
return {
|
||||
tor: false,
|
||||
city: sl.city,
|
||||
hp: sl.hp,
|
||||
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player.
|
||||
jobTitle: Object.values(player.jobs),
|
||||
maxHp: sl.max_hp,
|
||||
|
||||
mult: {
|
||||
agility: sl.agility_mult,
|
||||
agilityExp: sl.agility_exp_mult,
|
||||
charisma: sl.charisma_mult,
|
||||
charismaExp: sl.charisma_exp_mult,
|
||||
companyRep: sl.company_rep_mult,
|
||||
crimeMoney: sl.crime_money_mult,
|
||||
crimeSuccess: sl.crime_success_mult,
|
||||
defense: sl.defense_mult,
|
||||
defenseExp: sl.defense_exp_mult,
|
||||
dexterity: sl.dexterity_mult,
|
||||
dexterityExp: sl.dexterity_exp_mult,
|
||||
factionRep: sl.faction_rep_mult,
|
||||
hacking: sl.hacking_mult,
|
||||
hackingExp: sl.hacking_exp_mult,
|
||||
strength: sl.strength_mult,
|
||||
strengthExp: sl.strength_exp_mult,
|
||||
workMoney: sl.work_money_mult,
|
||||
},
|
||||
|
||||
timeWorked: sl.currentTaskTime,
|
||||
earningsForSleeves: {
|
||||
workHackExpGain: sl.earningsForSleeves.hack,
|
||||
workStrExpGain: sl.earningsForSleeves.str,
|
||||
workDefExpGain: sl.earningsForSleeves.def,
|
||||
workDexExpGain: sl.earningsForSleeves.dex,
|
||||
workAgiExpGain: sl.earningsForSleeves.agi,
|
||||
workChaExpGain: sl.earningsForSleeves.cha,
|
||||
workMoneyGain: sl.earningsForSleeves.money,
|
||||
},
|
||||
earningsForPlayer: {
|
||||
workHackExpGain: sl.earningsForPlayer.hack,
|
||||
workStrExpGain: sl.earningsForPlayer.str,
|
||||
workDefExpGain: sl.earningsForPlayer.def,
|
||||
workDexExpGain: sl.earningsForPlayer.dex,
|
||||
workAgiExpGain: sl.earningsForPlayer.agi,
|
||||
workChaExpGain: sl.earningsForPlayer.cha,
|
||||
workMoneyGain: sl.earningsForPlayer.money,
|
||||
},
|
||||
earningsForTask: {
|
||||
workHackExpGain: sl.earningsForTask.hack,
|
||||
workStrExpGain: sl.earningsForTask.str,
|
||||
workDefExpGain: sl.earningsForTask.def,
|
||||
workDexExpGain: sl.earningsForTask.dex,
|
||||
workAgiExpGain: sl.earningsForTask.agi,
|
||||
workChaExpGain: sl.earningsForTask.cha,
|
||||
workMoneyGain: sl.earningsForTask.money,
|
||||
},
|
||||
workRepGain: sl.getRepGain(player),
|
||||
};
|
||||
},
|
||||
getSleeveAugmentations: function (_sleeveNumber: unknown): string[] {
|
||||
updateRam("getSleeveAugmentations");
|
||||
const sleeveNumber = helper.number("getSleeveAugmentations", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("getSleeveAugmentations");
|
||||
checkSleeveNumber("getSleeveAugmentations", sleeveNumber);
|
||||
|
||||
const augs = [];
|
||||
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) {
|
||||
augs.push(player.sleeves[sleeveNumber].augmentations[i].name);
|
||||
}
|
||||
return augs;
|
||||
},
|
||||
getSleevePurchasableAugs: function (_sleeveNumber: unknown): AugmentPair[] {
|
||||
updateRam("getSleevePurchasableAugs");
|
||||
const sleeveNumber = helper.number("getSleevePurchasableAugs", "sleeveNumber", _sleeveNumber);
|
||||
checkSleeveAPIAccess("getSleevePurchasableAugs");
|
||||
checkSleeveNumber("getSleevePurchasableAugs", sleeveNumber);
|
||||
|
||||
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player);
|
||||
const augs = [];
|
||||
for (let i = 0; i < purchasableAugs.length; i++) {
|
||||
const aug = purchasableAugs[i];
|
||||
augs.push({
|
||||
name: aug.name,
|
||||
cost: aug.startingCost,
|
||||
});
|
||||
}
|
||||
|
||||
return augs;
|
||||
},
|
||||
purchaseSleeveAug: function (_sleeveNumber: unknown, _augName: unknown): boolean {
|
||||
updateRam("purchaseSleeveAug");
|
||||
const sleeveNumber = helper.number("purchaseSleeveAug", "sleeveNumber", _sleeveNumber);
|
||||
const augName = helper.string("purchaseSleeveAug", "augName", _augName);
|
||||
checkSleeveAPIAccess("purchaseSleeveAug");
|
||||
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
|
||||
|
||||
if (getSleeveStats(sleeveNumber).shock > 0) {
|
||||
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
||||
}
|
||||
|
||||
const aug = Augmentations[augName];
|
||||
if (!aug) {
|
||||
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
|
||||
}
|
||||
|
||||
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);
|
||||
},
|
||||
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { netscriptDelay } from "../NetscriptEvaluator";
|
||||
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { Fragments, FragmentById } from "../CotMG/Fragment";
|
||||
import { FragmentType } from "../CotMG/FragmentType";
|
||||
|
||||
import {
|
||||
Fragment as IFragment,
|
||||
@ -25,7 +26,7 @@ export function NetscriptStanek(
|
||||
): InternalAPI<IStanek> {
|
||||
function checkStanekAPIAccess(func: string): void {
|
||||
if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) {
|
||||
helper.makeRuntimeErrorMsg(func, "Requires Stanek's Gift installed.");
|
||||
throw helper.makeRuntimeErrorMsg(func, "Stanek's Gift is not installed");
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,15 +43,23 @@ export function NetscriptStanek(
|
||||
},
|
||||
chargeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): Promise<void> {
|
||||
//Get the fragment object using the given coordinates
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("chargeFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
//Check whether the selected fragment can ge charged
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`No fragment with root (${rootX}, ${rootY}).`);
|
||||
if (fragment.fragment().type == FragmentType.Booster) {
|
||||
throw _ctx.makeRuntimeErrorMsg(
|
||||
`The fragment with root (${rootX}, ${rootY}) is a Booster Fragment and thus cannot be charged.`,
|
||||
);
|
||||
}
|
||||
//Charge the fragment
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
_ctx.log(() => `Charged fragment for ${charge} charge.`);
|
||||
staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
_ctx.log(() => `Charged fragment with ${_ctx.workerScript.scriptRef.threads} threads.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
|
||||
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket";
|
||||
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
|
||||
@ -16,301 +14,286 @@ import {
|
||||
} from "../StockMarket/StockMarketCosts";
|
||||
import { Stock } from "../StockMarket/Stock";
|
||||
import { TIX } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): TIX {
|
||||
export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript): InternalAPI<TIX> {
|
||||
/**
|
||||
* Checks if the player has TIX API access. Throws an error if the player does not
|
||||
*/
|
||||
const checkTixApiAccess = function (callingFn: string): void {
|
||||
const checkTixApiAccess = function (ctx: NetscriptContext): void {
|
||||
if (!player.hasWseAccount) {
|
||||
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have WSE Access! Cannot use ${callingFn}()`);
|
||||
throw ctx.makeRuntimeErrorMsg(`You don't have WSE Access! Cannot use ${ctx.function}()`);
|
||||
}
|
||||
if (!player.hasTixApiAccess) {
|
||||
throw helper.makeRuntimeErrorMsg(callingFn, `You don't have TIX API Access! Cannot use ${callingFn}()`);
|
||||
throw ctx.makeRuntimeErrorMsg(`You don't have TIX API Access! Cannot use ${ctx.function}()`);
|
||||
}
|
||||
};
|
||||
|
||||
const getStockFromSymbol = function (symbol: string, callingFn: string): Stock {
|
||||
const getStockFromSymbol = function (ctx: NetscriptContext, symbol: string): Stock {
|
||||
const stock = SymbolToStockMap[symbol];
|
||||
if (stock == null) {
|
||||
throw helper.makeRuntimeErrorMsg(callingFn, `Invalid stock symbol: '${symbol}'`);
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: '${symbol}'`);
|
||||
}
|
||||
|
||||
return stock;
|
||||
};
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "stock", funcName));
|
||||
|
||||
return {
|
||||
getSymbols: function (): string[] {
|
||||
updateRam("getSymbols");
|
||||
checkTixApiAccess("getSymbols");
|
||||
getSymbols: (ctx: NetscriptContext) => (): string[] => {
|
||||
checkTixApiAccess(ctx);
|
||||
return Object.values(StockSymbols);
|
||||
},
|
||||
getPrice: function (_symbol: unknown): number {
|
||||
updateRam("getPrice");
|
||||
const symbol = helper.string("getPrice", "symbol", _symbol);
|
||||
checkTixApiAccess("getPrice");
|
||||
const stock = getStockFromSymbol(symbol, "getPrice");
|
||||
getPrice:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
return stock.price;
|
||||
},
|
||||
getAskPrice: function (_symbol: unknown): number {
|
||||
updateRam("getAskPrice");
|
||||
const symbol = helper.string("getAskPrice", "symbol", _symbol);
|
||||
checkTixApiAccess("getAskPrice");
|
||||
const stock = getStockFromSymbol(symbol, "getAskPrice");
|
||||
return stock.price;
|
||||
},
|
||||
getAskPrice:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
return stock.getAskPrice();
|
||||
},
|
||||
getBidPrice: function (_symbol: unknown): number {
|
||||
updateRam("getBidPrice");
|
||||
const symbol = helper.string("getBidPrice", "symbol", _symbol);
|
||||
checkTixApiAccess("getBidPrice");
|
||||
const stock = getStockFromSymbol(symbol, "getBidPrice");
|
||||
return stock.getAskPrice();
|
||||
},
|
||||
getBidPrice:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
return stock.getBidPrice();
|
||||
},
|
||||
getPosition: function (_symbol: unknown): [number, number, number, number] {
|
||||
updateRam("getPosition");
|
||||
const symbol = helper.string("getPosition", "symbol", _symbol);
|
||||
checkTixApiAccess("getPosition");
|
||||
const stock = SymbolToStockMap[symbol];
|
||||
if (stock == null) {
|
||||
throw helper.makeRuntimeErrorMsg("getPosition", `Invalid stock symbol: ${symbol}`);
|
||||
}
|
||||
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
|
||||
},
|
||||
getMaxShares: function (_symbol: unknown): number {
|
||||
updateRam("getMaxShares");
|
||||
const symbol = helper.string("getMaxShares", "symbol", _symbol);
|
||||
checkTixApiAccess("getMaxShares");
|
||||
const stock = getStockFromSymbol(symbol, "getMaxShares");
|
||||
|
||||
return stock.maxShares;
|
||||
},
|
||||
getPurchaseCost: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
|
||||
updateRam("getPurchaseCost");
|
||||
const symbol = helper.string("getPurchaseCost", "symbol", _symbol);
|
||||
let shares = helper.number("getPurchaseCost", "shares", _shares);
|
||||
const posType = helper.string("getPurchaseCost", "posType", _posType);
|
||||
checkTixApiAccess("getPurchaseCost");
|
||||
const stock = getStockFromSymbol(symbol, "getPurchaseCost");
|
||||
shares = Math.round(shares);
|
||||
|
||||
let pos;
|
||||
const sanitizedPosType = posType.toLowerCase();
|
||||
if (sanitizedPosType.includes("l")) {
|
||||
pos = PositionTypes.Long;
|
||||
} else if (sanitizedPosType.includes("s")) {
|
||||
pos = PositionTypes.Short;
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
const res = getBuyTransactionCost(stock, shares, pos);
|
||||
if (res == null) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
getSaleGain: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
|
||||
updateRam("getSaleGain");
|
||||
const symbol = helper.string("getSaleGain", "symbol", _symbol);
|
||||
let shares = helper.number("getSaleGain", "shares", _shares);
|
||||
const posType = helper.string("getSaleGain", "posType", _posType);
|
||||
checkTixApiAccess("getSaleGain");
|
||||
const stock = getStockFromSymbol(symbol, "getSaleGain");
|
||||
shares = Math.round(shares);
|
||||
|
||||
let pos;
|
||||
const sanitizedPosType = posType.toLowerCase();
|
||||
if (sanitizedPosType.includes("l")) {
|
||||
pos = PositionTypes.Long;
|
||||
} else if (sanitizedPosType.includes("s")) {
|
||||
pos = PositionTypes.Short;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const res = getSellTransactionGain(stock, shares, pos);
|
||||
if (res == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
buy: function (_symbol: unknown, _shares: unknown): number {
|
||||
updateRam("buy");
|
||||
const symbol = helper.string("buy", "symbol", _symbol);
|
||||
const shares = helper.number("buy", "shares", _shares);
|
||||
checkTixApiAccess("buy");
|
||||
const stock = getStockFromSymbol(symbol, "buy");
|
||||
const res = buyStock(stock, shares, workerScript, {});
|
||||
return res ? stock.getAskPrice() : 0;
|
||||
},
|
||||
sell: function (_symbol: unknown, _shares: unknown): number {
|
||||
updateRam("sell");
|
||||
const symbol = helper.string("sell", "symbol", _symbol);
|
||||
const shares = helper.number("sell", "shares", _shares);
|
||||
checkTixApiAccess("sell");
|
||||
const stock = getStockFromSymbol(symbol, "sell");
|
||||
const res = sellStock(stock, shares, workerScript, {});
|
||||
|
||||
return res ? stock.getBidPrice() : 0;
|
||||
},
|
||||
short: function (_symbol: unknown, _shares: unknown): number {
|
||||
updateRam("short");
|
||||
const symbol = helper.string("short", "symbol", _symbol);
|
||||
const shares = helper.number("short", "shares", _shares);
|
||||
checkTixApiAccess("short");
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 1) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"short",
|
||||
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
|
||||
);
|
||||
return stock.getBidPrice();
|
||||
},
|
||||
getPosition:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): [number, number, number, number] => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = SymbolToStockMap[symbol];
|
||||
if (stock == null) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid stock symbol: ${symbol}`);
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "short");
|
||||
const res = shortStock(stock, shares, workerScript, {});
|
||||
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
|
||||
},
|
||||
getMaxShares:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
return res ? stock.getBidPrice() : 0;
|
||||
},
|
||||
sellShort: function (_symbol: unknown, _shares: unknown): number {
|
||||
updateRam("sellShort");
|
||||
const symbol = helper.string("sellShort", "symbol", _symbol);
|
||||
const shares = helper.number("sellShort", "shares", _shares);
|
||||
checkTixApiAccess("sellShort");
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 1) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sellShort",
|
||||
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
|
||||
);
|
||||
return stock.maxShares;
|
||||
},
|
||||
getPurchaseCost:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown, _posType: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
let shares = ctx.helper.number("shares", _shares);
|
||||
const posType = ctx.helper.string("posType", _posType);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
shares = Math.round(shares);
|
||||
|
||||
let pos;
|
||||
const sanitizedPosType = posType.toLowerCase();
|
||||
if (sanitizedPosType.includes("l")) {
|
||||
pos = PositionTypes.Long;
|
||||
} else if (sanitizedPosType.includes("s")) {
|
||||
pos = PositionTypes.Short;
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "sellShort");
|
||||
const res = sellShort(stock, shares, workerScript, {});
|
||||
|
||||
return res ? stock.getAskPrice() : 0;
|
||||
},
|
||||
placeOrder: function (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean {
|
||||
updateRam("placeOrder");
|
||||
const symbol = helper.string("placeOrder", "symbol", _symbol);
|
||||
const shares = helper.number("placeOrder", "shares", _shares);
|
||||
const price = helper.number("placeOrder", "price", _price);
|
||||
const type = helper.string("placeOrder", "type", _type);
|
||||
const pos = helper.string("placeOrder", "pos", _pos);
|
||||
checkTixApiAccess("placeOrder");
|
||||
const res = getBuyTransactionCost(stock, shares, pos);
|
||||
if (res == null) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
getSaleGain:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown, _posType: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
let shares = ctx.helper.number("shares", _shares);
|
||||
const posType = ctx.helper.string("posType", _posType);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
shares = Math.round(shares);
|
||||
|
||||
let pos;
|
||||
const sanitizedPosType = posType.toLowerCase();
|
||||
if (sanitizedPosType.includes("l")) {
|
||||
pos = PositionTypes.Long;
|
||||
} else if (sanitizedPosType.includes("s")) {
|
||||
pos = PositionTypes.Short;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const res = getSellTransactionGain(stock, shares, pos);
|
||||
if (res == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
buy:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
const res = buyStock(stock, shares, workerScript, {});
|
||||
return res ? stock.getAskPrice() : 0;
|
||||
},
|
||||
sell:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
checkTixApiAccess(ctx);
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
const res = sellStock(stock, shares, workerScript, {});
|
||||
|
||||
return res ? stock.getBidPrice() : 0;
|
||||
},
|
||||
short:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
checkTixApiAccess(ctx);
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 1) {
|
||||
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
const res = shortStock(stock, shares, workerScript, {});
|
||||
|
||||
return res ? stock.getBidPrice() : 0;
|
||||
},
|
||||
sellShort:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
checkTixApiAccess(ctx);
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 1) {
|
||||
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
const res = sellShort(stock, shares, workerScript, {});
|
||||
|
||||
return res ? stock.getAskPrice() : 0;
|
||||
},
|
||||
placeOrder:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
const price = ctx.helper.number("price", _price);
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const pos = ctx.helper.string("pos", _pos);
|
||||
checkTixApiAccess(ctx);
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 2) {
|
||||
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
let orderType;
|
||||
let orderPos;
|
||||
const ltype = type.toLowerCase();
|
||||
if (ltype.includes("limit") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.LimitBuy;
|
||||
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.LimitSell;
|
||||
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.StopBuy;
|
||||
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.StopSell;
|
||||
} else {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
|
||||
}
|
||||
|
||||
const lpos = pos.toLowerCase();
|
||||
if (lpos.includes("l")) {
|
||||
orderPos = PositionTypes.Long;
|
||||
} else if (lpos.includes("s")) {
|
||||
orderPos = PositionTypes.Short;
|
||||
} else {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
|
||||
}
|
||||
|
||||
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
|
||||
},
|
||||
cancelOrder:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
const shares = ctx.helper.number("shares", _shares);
|
||||
const price = ctx.helper.number("price", _price);
|
||||
const type = ctx.helper.string("type", _type);
|
||||
const pos = ctx.helper.string("pos", _pos);
|
||||
checkTixApiAccess(ctx);
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 2) {
|
||||
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
if (isNaN(shares) || isNaN(price)) {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`);
|
||||
}
|
||||
let orderType;
|
||||
let orderPos;
|
||||
const ltype = type.toLowerCase();
|
||||
if (ltype.includes("limit") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.LimitBuy;
|
||||
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.LimitSell;
|
||||
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.StopBuy;
|
||||
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.StopSell;
|
||||
} else {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid order type: ${type}`);
|
||||
}
|
||||
|
||||
const lpos = pos.toLowerCase();
|
||||
if (lpos.includes("l")) {
|
||||
orderPos = PositionTypes.Long;
|
||||
} else if (lpos.includes("s")) {
|
||||
orderPos = PositionTypes.Short;
|
||||
} else {
|
||||
throw ctx.makeRuntimeErrorMsg(`Invalid position type: ${pos}`);
|
||||
}
|
||||
const params = {
|
||||
stock: stock,
|
||||
shares: shares,
|
||||
price: price,
|
||||
type: orderType,
|
||||
pos: orderPos,
|
||||
};
|
||||
return cancelOrder(params, workerScript);
|
||||
},
|
||||
getOrders: (ctx: NetscriptContext) => (): any => {
|
||||
checkTixApiAccess(ctx);
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 2) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"placeOrder",
|
||||
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
|
||||
);
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "placeOrder");
|
||||
|
||||
let orderType;
|
||||
let orderPos;
|
||||
const ltype = type.toLowerCase();
|
||||
if (ltype.includes("limit") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.LimitBuy;
|
||||
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.LimitSell;
|
||||
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.StopBuy;
|
||||
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.StopSell;
|
||||
} else {
|
||||
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid order type: ${type}`);
|
||||
}
|
||||
|
||||
const lpos = pos.toLowerCase();
|
||||
if (lpos.includes("l")) {
|
||||
orderPos = PositionTypes.Long;
|
||||
} else if (lpos.includes("s")) {
|
||||
orderPos = PositionTypes.Short;
|
||||
} else {
|
||||
throw helper.makeRuntimeErrorMsg("placeOrder", `Invalid position type: ${pos}`);
|
||||
}
|
||||
|
||||
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
|
||||
},
|
||||
cancelOrder: function (
|
||||
_symbol: unknown,
|
||||
_shares: unknown,
|
||||
_price: unknown,
|
||||
_type: unknown,
|
||||
_pos: unknown,
|
||||
): boolean {
|
||||
updateRam("cancelOrder");
|
||||
const symbol = helper.string("cancelOrder", "symbol", _symbol);
|
||||
const shares = helper.number("cancelOrder", "shares", _shares);
|
||||
const price = helper.number("cancelOrder", "price", _price);
|
||||
const type = helper.string("cancelOrder", "type", _type);
|
||||
const pos = helper.string("cancelOrder", "pos", _pos);
|
||||
checkTixApiAccess("cancelOrder");
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 2) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"cancelOrder",
|
||||
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
|
||||
);
|
||||
}
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "cancelOrder");
|
||||
if (isNaN(shares) || isNaN(price)) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"cancelOrder",
|
||||
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
|
||||
);
|
||||
}
|
||||
let orderType;
|
||||
let orderPos;
|
||||
const ltype = type.toLowerCase();
|
||||
if (ltype.includes("limit") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.LimitBuy;
|
||||
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.LimitSell;
|
||||
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
||||
orderType = OrderTypes.StopBuy;
|
||||
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
||||
orderType = OrderTypes.StopSell;
|
||||
} else {
|
||||
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid order type: ${type}`);
|
||||
}
|
||||
|
||||
const lpos = pos.toLowerCase();
|
||||
if (lpos.includes("l")) {
|
||||
orderPos = PositionTypes.Long;
|
||||
} else if (lpos.includes("s")) {
|
||||
orderPos = PositionTypes.Short;
|
||||
} else {
|
||||
throw helper.makeRuntimeErrorMsg("cancelOrder", `Invalid position type: ${pos}`);
|
||||
}
|
||||
const params = {
|
||||
stock: stock,
|
||||
shares: shares,
|
||||
price: price,
|
||||
type: orderType,
|
||||
pos: orderPos,
|
||||
};
|
||||
return cancelOrder(params, workerScript);
|
||||
},
|
||||
getOrders: function (): any {
|
||||
updateRam("getOrders");
|
||||
checkTixApiAccess("getOrders");
|
||||
if (player.bitNodeN !== 8) {
|
||||
if (player.sourceFileLvl(8) <= 2) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"getOrders",
|
||||
"You must either be in BitNode-8 or have Source-File 8 Level 3.",
|
||||
);
|
||||
throw ctx.makeRuntimeErrorMsg("You must either be in BitNode-8 or have Source-File 8 Level 3.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,103 +317,95 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
|
||||
|
||||
return orders;
|
||||
},
|
||||
getVolatility: function (_symbol: unknown): number {
|
||||
updateRam("getVolatility");
|
||||
const symbol = helper.string("getVolatility", "symbol", _symbol);
|
||||
if (!player.has4SDataTixApi) {
|
||||
throw helper.makeRuntimeErrorMsg("getVolatility", "You don't have 4S Market Data TIX API Access!");
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "getVolatility");
|
||||
getVolatility:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
if (!player.has4SDataTixApi) {
|
||||
throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!");
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
return stock.mv / 100; // Convert from percentage to decimal
|
||||
},
|
||||
getForecast: function (_symbol: unknown): number {
|
||||
updateRam("getForecast");
|
||||
const symbol = helper.string("getForecast", "symbol", _symbol);
|
||||
if (!player.has4SDataTixApi) {
|
||||
throw helper.makeRuntimeErrorMsg("getForecast", "You don't have 4S Market Data TIX API Access!");
|
||||
}
|
||||
const stock = getStockFromSymbol(symbol, "getForecast");
|
||||
|
||||
let forecast = 50;
|
||||
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
|
||||
return forecast / 100; // Convert from percentage to decimal
|
||||
},
|
||||
purchase4SMarketData: function (): boolean {
|
||||
updateRam("purchase4SMarketData");
|
||||
return stock.mv / 100; // Convert from percentage to decimal
|
||||
},
|
||||
getForecast:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_symbol: unknown): number => {
|
||||
const symbol = ctx.helper.string("symbol", _symbol);
|
||||
if (!player.has4SDataTixApi) {
|
||||
throw ctx.makeRuntimeErrorMsg("You don't have 4S Market Data TIX API Access!");
|
||||
}
|
||||
const stock = getStockFromSymbol(ctx, symbol);
|
||||
|
||||
let forecast = 50;
|
||||
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
|
||||
return forecast / 100; // Convert from percentage to decimal
|
||||
},
|
||||
purchase4SMarketData: (ctx: NetscriptContext) => (): boolean => {
|
||||
if (player.has4SData) {
|
||||
workerScript.log("stock.purchase4SMarketData", () => "Already purchased 4S Market Data.");
|
||||
ctx.log(() => "Already purchased 4S Market Data.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player.money < getStockMarket4SDataCost()) {
|
||||
workerScript.log("stock.purchase4SMarketData", () => "Not enough money to purchase 4S Market Data.");
|
||||
ctx.log(() => "Not enough money to purchase 4S Market Data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
player.has4SData = true;
|
||||
player.loseMoney(getStockMarket4SDataCost(), "stock");
|
||||
workerScript.log("stock.purchase4SMarketData", () => "Purchased 4S Market Data");
|
||||
ctx.log(() => "Purchased 4S Market Data");
|
||||
return true;
|
||||
},
|
||||
purchase4SMarketDataTixApi: function (): boolean {
|
||||
updateRam("purchase4SMarketDataTixApi");
|
||||
checkTixApiAccess("purchase4SMarketDataTixApi");
|
||||
purchase4SMarketDataTixApi: (ctx: NetscriptContext) => (): boolean => {
|
||||
checkTixApiAccess(ctx);
|
||||
|
||||
if (player.has4SDataTixApi) {
|
||||
workerScript.log("stock.purchase4SMarketDataTixApi", () => "Already purchased 4S Market Data TIX API");
|
||||
ctx.log(() => "Already purchased 4S Market Data TIX API");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player.money < getStockMarket4STixApiCost()) {
|
||||
workerScript.log(
|
||||
"stock.purchase4SMarketDataTixApi",
|
||||
() => "Not enough money to purchase 4S Market Data TIX API",
|
||||
);
|
||||
ctx.log(() => "Not enough money to purchase 4S Market Data TIX API");
|
||||
return false;
|
||||
}
|
||||
|
||||
player.has4SDataTixApi = true;
|
||||
player.loseMoney(getStockMarket4STixApiCost(), "stock");
|
||||
workerScript.log("stock.purchase4SMarketDataTixApi", () => "Purchased 4S Market Data TIX API");
|
||||
ctx.log(() => "Purchased 4S Market Data TIX API");
|
||||
return true;
|
||||
},
|
||||
purchaseWseAccount: function (): boolean {
|
||||
updateRam("PurchaseWseAccount");
|
||||
|
||||
purchaseWseAccount: (ctx: NetscriptContext) => (): boolean => {
|
||||
if (player.hasWseAccount) {
|
||||
workerScript.log("stock.purchaseWseAccount", () => "Already purchased WSE Account");
|
||||
ctx.log(() => "Already purchased WSE Account");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player.money < getStockMarketWseCost()) {
|
||||
workerScript.log("stock.purchaseWseAccount", () => "Not enough money to purchase WSE Account Access");
|
||||
ctx.log(() => "Not enough money to purchase WSE Account Access");
|
||||
return false;
|
||||
}
|
||||
|
||||
player.hasWseAccount = true;
|
||||
initStockMarketFn();
|
||||
player.loseMoney(getStockMarketWseCost(), "stock");
|
||||
workerScript.log("stock.purchaseWseAccount", () => "Purchased WSE Account Access");
|
||||
ctx.log(() => "Purchased WSE Account Access");
|
||||
return true;
|
||||
},
|
||||
purchaseTixApi: function (): boolean {
|
||||
updateRam("purchaseTixApi");
|
||||
|
||||
purchaseTixApi: (ctx: NetscriptContext) => (): boolean => {
|
||||
if (player.hasTixApiAccess) {
|
||||
workerScript.log("stock.purchaseTixApi", () => "Already purchased TIX API");
|
||||
ctx.log(() => "Already purchased TIX API");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player.money < getStockMarketTixApiCost()) {
|
||||
workerScript.log("stock.purchaseTixApi", () => "Not enough money to purchase TIX API Access");
|
||||
ctx.log(() => "Not enough money to purchase TIX API Access");
|
||||
return false;
|
||||
}
|
||||
|
||||
player.hasTixApiAccess = true;
|
||||
player.loseMoney(getStockMarketTixApiCost(), "stock");
|
||||
workerScript.log("stock.purchaseTixApi", () => "Purchased TIX API");
|
||||
ctx.log(() => "Purchased TIX API");
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
@ -1,7 +1,3 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import {
|
||||
GameInfo,
|
||||
IStyleSettings,
|
||||
@ -14,88 +10,81 @@ import { defaultTheme } from "../Themes/Themes";
|
||||
import { defaultStyles } from "../Themes/Styles";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { hash } from "../hash/hash";
|
||||
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
|
||||
import { Terminal } from "../../src/Terminal";
|
||||
|
||||
export function NetscriptUserInterface(
|
||||
player: IPlayer,
|
||||
workerScript: WorkerScript,
|
||||
helper: INetscriptHelper,
|
||||
): IUserInterface {
|
||||
const updateRam = (funcName: string): void => helper.updateDynamicRam(funcName, getRamCost(player, "ui", funcName));
|
||||
export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
|
||||
return {
|
||||
getTheme: function (): UserInterfaceTheme {
|
||||
updateRam("getTheme");
|
||||
getTheme: () => (): UserInterfaceTheme => {
|
||||
return { ...Settings.theme };
|
||||
},
|
||||
|
||||
getStyles: function (): IStyleSettings {
|
||||
updateRam("getStyles");
|
||||
getStyles: () => (): IStyleSettings => {
|
||||
return { ...Settings.styles };
|
||||
},
|
||||
|
||||
setTheme: function (newTheme: UserInterfaceTheme): void {
|
||||
updateRam("setTheme");
|
||||
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
|
||||
const currentTheme = { ...Settings.theme };
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newTheme)) {
|
||||
if (!currentTheme[key]) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
} else if (!hex.test(newTheme[key] ?? "")) {
|
||||
errors.push(`Invalid color "${key}": ${newTheme[key]}`);
|
||||
} else {
|
||||
currentTheme[key] = newTheme[key];
|
||||
setTheme:
|
||||
(ctx: NetscriptContext) =>
|
||||
(newTheme: UserInterfaceTheme): void => {
|
||||
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
|
||||
const currentTheme = { ...Settings.theme };
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newTheme)) {
|
||||
if (!currentTheme[key]) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
} else if (!hex.test(newTheme[key] ?? "")) {
|
||||
errors.push(`Invalid color "${key}": ${newTheme[key]}`);
|
||||
} else {
|
||||
currentTheme[key] = newTheme[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
Object.assign(Settings.theme, currentTheme);
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.setTheme", () => `Successfully set theme`);
|
||||
} else {
|
||||
workerScript.log("ui.setTheme", () => `Failed to set theme. Errors: ${errors.join(", ")}`);
|
||||
}
|
||||
},
|
||||
|
||||
setStyles: function (newStyles: IStyleSettings): void {
|
||||
updateRam("setStyles");
|
||||
|
||||
const currentStyles = { ...Settings.styles };
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newStyles)) {
|
||||
if (!(currentStyles as any)[key]) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
if (errors.length === 0) {
|
||||
Object.assign(Settings.theme, currentTheme);
|
||||
ThemeEvents.emit();
|
||||
ctx.log(() => `Successfully set theme`);
|
||||
} else {
|
||||
(currentStyles as any)[key] = (newStyles as any)[key];
|
||||
ctx.log(() => `Failed to set theme. Errors: ${errors.join(", ")}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
if (errors.length === 0) {
|
||||
Object.assign(Settings.styles, currentStyles);
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.setStyles", () => `Successfully set styles`);
|
||||
} else {
|
||||
workerScript.log("ui.setStyles", () => `Failed to set styles. Errors: ${errors.join(", ")}`);
|
||||
}
|
||||
},
|
||||
setStyles:
|
||||
(ctx: NetscriptContext) =>
|
||||
(newStyles: IStyleSettings): void => {
|
||||
const currentStyles = { ...Settings.styles };
|
||||
const errors: string[] = [];
|
||||
for (const key of Object.keys(newStyles)) {
|
||||
if (!(currentStyles as any)[key]) {
|
||||
// Invalid key
|
||||
errors.push(`Invalid key "${key}"`);
|
||||
} else {
|
||||
(currentStyles as any)[key] = (newStyles as any)[key];
|
||||
}
|
||||
}
|
||||
|
||||
resetTheme: function (): void {
|
||||
updateRam("resetTheme");
|
||||
if (errors.length === 0) {
|
||||
Object.assign(Settings.styles, currentStyles);
|
||||
ThemeEvents.emit();
|
||||
ctx.log(() => `Successfully set styles`);
|
||||
} else {
|
||||
ctx.log(() => `Failed to set styles. Errors: ${errors.join(", ")}`);
|
||||
}
|
||||
},
|
||||
|
||||
resetTheme: (ctx: NetscriptContext) => (): void => {
|
||||
Settings.theme = { ...defaultTheme };
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.resetTheme", () => `Reinitialized theme to default`);
|
||||
ctx.log(() => `Reinitialized theme to default`);
|
||||
},
|
||||
|
||||
resetStyles: function (): void {
|
||||
updateRam("resetStyles");
|
||||
resetStyles: (ctx: NetscriptContext) => (): void => {
|
||||
Settings.styles = { ...defaultStyles };
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.resetStyles", () => `Reinitialized styles to default`);
|
||||
ctx.log(() => `Reinitialized styles to default`);
|
||||
},
|
||||
|
||||
getGameInfo: function (): GameInfo {
|
||||
updateRam("getGameInfo");
|
||||
getGameInfo: () => (): GameInfo => {
|
||||
const version = CONSTANTS.VersionString;
|
||||
const commit = hash();
|
||||
const platform = navigator.userAgent.toLowerCase().indexOf(" electron/") > -1 ? "Steam" : "Browser";
|
||||
@ -108,5 +97,10 @@ export function NetscriptUserInterface(
|
||||
|
||||
return gameInfo;
|
||||
},
|
||||
|
||||
clearTerminal: (ctx: NetscriptContext) => (): void => {
|
||||
ctx.log(() => `Clearing terminal`);
|
||||
Terminal.clear();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -532,7 +532,7 @@ function createAndAddWorkerScript(
|
||||
const oneRamUsage = getRamUsageFromRunningScript(runningScriptObj);
|
||||
const ramUsage = roundToTwo(oneRamUsage * threads);
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
if (ramUsage > ramAvailable) {
|
||||
if (ramUsage > ramAvailable + 0.001) {
|
||||
dialogBoxCreate(
|
||||
`Not enough RAM to run script ${runningScriptObj.filename} with args ` +
|
||||
`${arrayToString(runningScriptObj.args)}. This likely occurred because you re-loaded ` +
|
||||
@ -750,7 +750,7 @@ export function runScriptFromScript(
|
||||
if (server.hasAdminRights == false) {
|
||||
workerScript.log(caller, () => `You do not have root access on '${server.hostname}'`);
|
||||
return 0;
|
||||
} else if (ramUsage > ramAvailable) {
|
||||
} else if (ramUsage > ramAvailable + 0.001) {
|
||||
workerScript.log(
|
||||
caller,
|
||||
() =>
|
||||
|
@ -18,7 +18,7 @@ export class GraftableAugmentation {
|
||||
}
|
||||
|
||||
get cost(): number {
|
||||
return this.augmentation.startingCost * CONSTANTS.AugmentationGraftingCostMult;
|
||||
return this.augmentation.baseCost * CONSTANTS.AugmentationGraftingCostMult;
|
||||
}
|
||||
|
||||
get time(): number {
|
||||
|
@ -1,14 +1,12 @@
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
|
||||
import { GraftableAugmentation } from "./GraftableAugmentation";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
export const getGraftingAvailableAugs = (player: IPlayer): string[] => {
|
||||
const augs: string[] = [];
|
||||
|
||||
for (const [augName, aug] of Object.entries(Augmentations)) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor || augName === AugmentationNames.TheRedPill || aug.isSpecial)
|
||||
continue;
|
||||
for (const [augName, aug] of Object.entries(StaticAugmentations)) {
|
||||
if (aug.isSpecial) continue;
|
||||
augs.push(augName);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { Construction, CheckBox, CheckBoxOutlineBlank } from "@mui/icons-material";
|
||||
import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material";
|
||||
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Augmentation } from "../../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames";
|
||||
import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
|
||||
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";
|
||||
@ -15,8 +16,8 @@ import { ConfirmationModal } from "../../../ui/React/ConfirmationModal";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../GraftingHelpers";
|
||||
import { GraftableAugmentation } from "../GraftableAugmentation";
|
||||
import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers";
|
||||
|
||||
const GraftableAugmentations: IMap<GraftableAugmentation> = {};
|
||||
|
||||
@ -54,7 +55,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
|
||||
for (const aug of Object.values(Augmentations)) {
|
||||
for (const aug of Object.values(StaticAugmentations)) {
|
||||
const name = aug.name;
|
||||
const graftableAug = new GraftableAugmentation(aug);
|
||||
GraftableAugmentations[name] = graftableAug;
|
||||
@ -62,12 +63,28 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
|
||||
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs(player)[0]);
|
||||
const [graftOpen, setGraftOpen] = useState(false);
|
||||
const selectedAugmentation = StaticAugmentations[selectedAug];
|
||||
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
const getAugsSorted = (): string[] => {
|
||||
const augs = getGraftingAvailableAugs(player);
|
||||
switch (Settings.PurchaseAugmentationsOrder) {
|
||||
case PurchaseAugmentationsOrderSetting.Cost:
|
||||
return augs.sort((a, b) => GraftableAugmentations[a].cost - GraftableAugmentations[b].cost);
|
||||
default:
|
||||
return augs;
|
||||
}
|
||||
};
|
||||
|
||||
const switchSortOrder = (newOrder: PurchaseAugmentationsOrderSetting): void => {
|
||||
Settings.PurchaseAugmentationsOrder = newOrder;
|
||||
rerender();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
@ -90,13 +107,31 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ my: 3 }}>
|
||||
<Typography variant="h5">Graft Augmentations</Typography>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">Graft Augmentations</Typography>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<Button sx={{ width: "100%" }} onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>
|
||||
Sort by Cost
|
||||
</Button>
|
||||
<Button sx={{ width: "100%" }} onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>
|
||||
Sort by Default Order
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
{getGraftingAvailableAugs(player).length > 0 ? (
|
||||
<Paper sx={{ my: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<Paper sx={{ mb: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||
{getGraftingAvailableAugs(player).map((k, i) => (
|
||||
{getAugsSorted().map((k, i) => (
|
||||
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||
<Typography>{k}</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
color: canGraft(player, GraftableAugmentations[k])
|
||||
? Settings.theme.primary
|
||||
: Settings.theme.disabled,
|
||||
}}
|
||||
>
|
||||
{k}
|
||||
</Typography>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
@ -139,34 +174,41 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Typography color={Settings.theme.info}>
|
||||
<b>Time to Graft:</b>{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
|
||||
<Box sx={{ maxHeight: 330, overflowY: "scroll" }}>
|
||||
<Typography color={Settings.theme.info}>
|
||||
<b>Time to Graft:</b>{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
|
||||
)}
|
||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||
</Typography>
|
||||
|
||||
{selectedAugmentation.prereqs.length > 0 && (
|
||||
<AugPreReqsChecklist player={player} aug={selectedAugmentation} />
|
||||
)}
|
||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||
</Typography>
|
||||
{Augmentations[selectedAug].prereqs.length > 0 && (
|
||||
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
|
||||
)}
|
||||
|
||||
<br />
|
||||
<Typography sx={{ maxHeight: 305, overflowY: "scroll" }}>
|
||||
{(() => {
|
||||
const aug = Augmentations[selectedAug];
|
||||
<br />
|
||||
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
<Typography>
|
||||
{(() => {
|
||||
const info =
|
||||
typeof selectedAugmentation.info === "string" ? (
|
||||
<span>{selectedAugmentation.info}</span>
|
||||
) : (
|
||||
selectedAugmentation.info
|
||||
);
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{selectedAugmentation.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
) : (
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user