mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-23 08:03:48 +01:00
Merge branch 'dev' into improvement/work-in-progress-ui
This commit is contained in:
commit
bcb6176952
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
@ -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. |
|
| | | You are given an LZ-encoded string. Decode it and output the original string. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | Example: decoding '5aaabc340533bca' chunk-by-chunk |
|
| | | Example: decoding '5aaabb450723abb' chunk-by-chunk |
|
||||||
| | | 5aaabc -> aaabc |
|
| | | 5aaabb -> aaabb |
|
||||||
| | | 5aaabc34 -> aaabcaab |
|
| | | 5aaabb45 -> aaabbaaab |
|
||||||
| | | 5aaabc340 -> aaabcaab |
|
| | | 5aaabb450 -> aaabbaaab |
|
||||||
| | | 5aaabc34053 -> aaabcaabaabaa |
|
| | | 5aaabb45072 -> aaabbaaababababa |
|
||||||
| | | 5aaabc340533bca -> aaabcaabaabaabca |
|
| | | 5aaabb450723abb -> aaabbaaababababaabb |
|
||||||
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
||||||
| Compression III: LZ Compression | | Lempel-Ziv (LZ) compression is a data compression technique which encodes data using |
|
| 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 |
|
| | | 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. |
|
| | | possible output length. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | Examples (some have other possible encodings of minimal length): |
|
| | | Examples (some have other possible encodings of minimal length): |
|
||||||
| | | abracadabra -> 7abracad47 |
|
| | | abracadabra -> 7abracad47 |
|
||||||
| | | mississippi -> 4miss433ppi |
|
| | | mississippi -> 4miss433ppi |
|
||||||
| | | aAAaAAaAaAA -> 3aAA53035 |
|
| | | aAAaAAaAaAA -> 3aAA53035 |
|
||||||
| | | 2718281828 -> 627182844 |
|
| | | 2718281828 -> 627182844 |
|
||||||
| | | abcdefghijk -> 9abcdefghi02jk |
|
| | | abcdefghijk -> 9abcdefghi02jk |
|
||||||
| | | aaaaaaaaaaa -> 1a911a |
|
| | | aaaaaaaaaaaa -> 3aaa91 |
|
||||||
| | | aaaaaaaaaaaa -> 1a912aa |
|
| | | aaaaaaaaaaaaa -> 1a91031 |
|
||||||
| | | aaaaaaaaaaaaa -> 1a91031 |
|
| | | aaaaaaaaaaaaaa -> 1a91041 |
|
||||||
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
+-----------------------------------------+------------------------------------------------------------------------------------------+
|
||||||
|
@ -334,7 +334,7 @@
|
|||||||
},
|
},
|
||||||
"BLADEBURNER_UNSPENT_100000": {
|
"BLADEBURNER_UNSPENT_100000": {
|
||||||
"ID": "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."
|
"Description": "Have 100 000 unspent bladeburner skill points."
|
||||||
},
|
},
|
||||||
"4S": {
|
"4S": {
|
||||||
|
@ -9,6 +9,18 @@ import { Money } from "../ui/React/Money";
|
|||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { FactionNames } from "../Faction/data/FactionNames";
|
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 {
|
export interface IConstructorParams {
|
||||||
info: string | JSX.Element;
|
info: string | JSX.Element;
|
||||||
@ -410,10 +422,10 @@ function generateStatsDescription(mults: IMap<number>, programs?: string[], star
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Augmentation {
|
export class Augmentation {
|
||||||
// How much money this costs to buy
|
// How much money this costs to buy before multipliers
|
||||||
baseCost = 0;
|
baseCost = 0;
|
||||||
|
|
||||||
// How much faction reputation is required to unlock this
|
// How much faction reputation is required to unlock this before multipliers
|
||||||
baseRepRequirement = 0;
|
baseRepRequirement = 0;
|
||||||
|
|
||||||
// Description of what this Aug is and what it does
|
// 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)
|
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
|
||||||
isSpecial = false;
|
isSpecial = false;
|
||||||
|
|
||||||
// Augmentation level - for repeatable Augs like NeuroFlux Governor
|
|
||||||
level = 0;
|
|
||||||
|
|
||||||
// Name of Augmentation
|
// Name of Augmentation
|
||||||
name = "";
|
name = "";
|
||||||
|
|
||||||
@ -438,12 +447,6 @@ export class Augmentation {
|
|||||||
// The Player/Person classes
|
// The Player/Person classes
|
||||||
mults: IMap<number> = {};
|
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 that offer this aug.
|
||||||
factions: string[] = [];
|
factions: string[] = [];
|
||||||
|
|
||||||
@ -461,17 +464,15 @@ export class Augmentation {
|
|||||||
this.prereqs = params.prereqs ? params.prereqs : [];
|
this.prereqs = params.prereqs ? params.prereqs : [];
|
||||||
|
|
||||||
this.baseRepRequirement = params.repCost;
|
this.baseRepRequirement = params.repCost;
|
||||||
|
Object.freeze(this.baseRepRequirement);
|
||||||
this.baseCost = params.moneyCost;
|
this.baseCost = params.moneyCost;
|
||||||
this.startingCost = this.baseCost;
|
Object.freeze(this.baseCost);
|
||||||
this.startingRepRequirement = this.baseRepRequirement;
|
|
||||||
this.factions = params.factions;
|
this.factions = params.factions;
|
||||||
|
|
||||||
if (params.isSpecial) {
|
if (params.isSpecial) {
|
||||||
this.isSpecial = true;
|
this.isSpecial = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.level = 0;
|
|
||||||
|
|
||||||
// Set multipliers
|
// Set multipliers
|
||||||
if (params.hacking_mult) {
|
if (params.hacking_mult) {
|
||||||
this.mults.hacking_mult = params.hacking_mult;
|
this.mults.hacking_mult = params.hacking_mult;
|
||||||
@ -600,6 +601,61 @@ 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;
|
||||||
|
}
|
||||||
|
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
|
// Adds this Augmentation to all Factions
|
||||||
addToAllFactions(): void {
|
addToAllFactions(): void {
|
||||||
for (const fac of Object.keys(Factions)) {
|
for (const fac of Object.keys(Factions)) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Augmentation } from "./Augmentation";
|
import { Augmentation } from "./Augmentation";
|
||||||
import { Augmentations } from "./Augmentations";
|
import { StaticAugmentations } from "./StaticAugmentations";
|
||||||
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
|
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
|
||||||
import { AugmentationNames } from "./data/AugmentationNames";
|
import { AugmentationNames } from "./data/AugmentationNames";
|
||||||
|
|
||||||
@ -20,30 +20,11 @@ import {
|
|||||||
initNeuroFluxGovernor,
|
initNeuroFluxGovernor,
|
||||||
initUnstableCircadianModulator,
|
initUnstableCircadianModulator,
|
||||||
} from "./data/AugmentationCreator";
|
} from "./data/AugmentationCreator";
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
|
||||||
import { Router } from "../ui/GameRoot";
|
import { Router } from "../ui/GameRoot";
|
||||||
|
|
||||||
export function AddToAugmentations(aug: Augmentation): void {
|
export function AddToStaticAugmentations(aug: Augmentation): void {
|
||||||
const name = aug.name;
|
const name = aug.name;
|
||||||
Augmentations[name] = aug;
|
StaticAugmentations[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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAugmentations(): void {
|
function createAugmentations(): void {
|
||||||
@ -67,96 +48,37 @@ function resetFactionAugmentations(): void {
|
|||||||
|
|
||||||
function initAugmentations(): void {
|
function initAugmentations(): void {
|
||||||
resetFactionAugmentations();
|
resetFactionAugmentations();
|
||||||
clearObject(Augmentations);
|
clearObject(StaticAugmentations);
|
||||||
createAugmentations();
|
createAugmentations();
|
||||||
updateAugmentationCosts();
|
|
||||||
Player.reapplyAllAugmentations();
|
Player.reapplyAllAugmentations();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBaseAugmentationPriceMultiplier(): number {
|
export function getBaseAugmentationPriceMultiplier(): number {
|
||||||
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
|
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
|
||||||
}
|
}
|
||||||
export function getGenericAugmentationPriceMultiplier(): number {
|
export function getGenericAugmentationPriceMultiplier(): number {
|
||||||
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);
|
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)
|
//Resets an Augmentation during (re-initizliation)
|
||||||
function resetAugmentation(aug: Augmentation): void {
|
function resetAugmentation(aug: Augmentation): void {
|
||||||
aug.addToFactions(aug.factions);
|
aug.addToFactions(aug.factions);
|
||||||
const name = aug.name;
|
const name = aug.name;
|
||||||
if (augmentationExists(name)) {
|
if (augmentationExists(name)) {
|
||||||
delete Augmentations[name];
|
delete StaticAugmentations[name];
|
||||||
}
|
}
|
||||||
AddToAugmentations(aug);
|
AddToStaticAugmentations(aug);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
|
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
|
||||||
const augObj = Augmentations[aug.name];
|
const staticAugmentation = StaticAugmentations[aug.name];
|
||||||
|
|
||||||
// Apply multipliers
|
// Apply multipliers
|
||||||
for (const mult of Object.keys(augObj.mults)) {
|
for (const mult of Object.keys(staticAugmentation.mults)) {
|
||||||
const v = Player.getMult(mult) * augObj.mults[mult];
|
const v = Player.getMult(mult) * staticAugmentation.mults[mult];
|
||||||
Player.setMult(mult, v);
|
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
|
// Special logic for Congruity Implant
|
||||||
if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
|
if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
|
||||||
Player.entropy = 0;
|
Player.entropy = 0;
|
||||||
@ -185,7 +107,7 @@ function installAugmentations(force?: boolean): boolean {
|
|||||||
}
|
}
|
||||||
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||||
const ownedAug = Player.queuedAugmentations[i];
|
const ownedAug = Player.queuedAugmentations[i];
|
||||||
const aug = Augmentations[ownedAug.name];
|
const aug = StaticAugmentations[ownedAug.name];
|
||||||
if (aug == null) {
|
if (aug == null) {
|
||||||
console.error(`Invalid augmentation: ${ownedAug.name}`);
|
console.error(`Invalid augmentation: ${ownedAug.name}`);
|
||||||
continue;
|
continue;
|
||||||
@ -215,7 +137,7 @@ function installAugmentations(force?: boolean): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function augmentationExists(name: string): boolean {
|
function augmentationExists(name: string): boolean {
|
||||||
return Augmentations.hasOwnProperty(name);
|
return StaticAugmentations.hasOwnProperty(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRepeatableAug(aug: Augmentation): boolean {
|
export function isRepeatableAug(aug: Augmentation): boolean {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Augmentation } from "./Augmentation";
|
import { Augmentation } from "./Augmentation";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
export const Augmentations: IMap<Augmentation> = {};
|
export const StaticAugmentations: IMap<Augmentation> = {};
|
@ -114,17 +114,6 @@ export enum AugmentationNames {
|
|||||||
StaneksGift2 = "Stanek's Gift - Awakening",
|
StaneksGift2 = "Stanek's Gift - Awakening",
|
||||||
StaneksGift3 = "Stanek's Gift - Serenity",
|
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
|
// Infiltrators MiniGames
|
||||||
MightOfAres = "SoA - Might of Ares", // slash
|
MightOfAres = "SoA - Might of Ares", // slash
|
||||||
WisdomOfAthena = "SoA - Wisdom of Athena", // bracket
|
WisdomOfAthena = "SoA - Wisdom of Athena", // bracket
|
||||||
@ -135,10 +124,4 @@ export enum AugmentationNames {
|
|||||||
HuntOfArtemis = "SoA - Hunt of Artemis", // mine
|
HuntOfArtemis = "SoA - Hunt of Artemis", // mine
|
||||||
KnowledgeOfApollo = "SoA - Knowledge of Apollo", // wire
|
KnowledgeOfApollo = "SoA - Knowledge of Apollo", // wire
|
||||||
WKSharmonizer = "SoA - phyzical WKS harmonizer",
|
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 { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { StaticAugmentations } from "../StaticAugmentations";
|
||||||
import { CONSTANTS } from "../../Constants";
|
import { CONSTANTS } from "../../Constants";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { Info } from "@mui/icons-material";
|
import { Info } from "@mui/icons-material";
|
||||||
@ -39,7 +39,9 @@ const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
|
|||||||
<Typography variant="h5" color={Settings.theme.info}>
|
<Typography variant="h5" color={Settings.theme.info}>
|
||||||
NeuroFlux Governor - Level {level}
|
NeuroFlux Governor - Level {level}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography color={Settings.theme.info}>{Augmentations[AugmentationNames.NeuroFluxGovernor].stats}</Typography>
|
<Typography color={Settings.theme.info}>
|
||||||
|
{StaticAugmentations[AugmentationNames.NeuroFluxGovernor].stats}
|
||||||
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
@ -13,7 +13,7 @@ import React, { useState } from "react";
|
|||||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { StaticAugmentations } from "../StaticAugmentations";
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
|
|
||||||
export function InstalledAugmentations(): React.ReactElement {
|
export function InstalledAugmentations(): React.ReactElement {
|
||||||
@ -77,7 +77,7 @@ export function InstalledAugmentations(): React.ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
<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 info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||||
const tooltip = (
|
const tooltip = (
|
||||||
|
@ -8,7 +8,7 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
|||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { StaticAugmentations } from "../StaticAugmentations";
|
||||||
|
|
||||||
interface IAugmentedStats {
|
interface IAugmentedStats {
|
||||||
[index: string]: number;
|
[index: string]: number;
|
||||||
@ -17,7 +17,7 @@ interface IAugmentedStats {
|
|||||||
function calculateAugmentedStats(): IAugmentedStats {
|
function calculateAugmentedStats(): IAugmentedStats {
|
||||||
const augP: IAugmentedStats = {};
|
const augP: IAugmentedStats = {};
|
||||||
for (const aug of Player.queuedAugmentations) {
|
for (const aug of Player.queuedAugmentations) {
|
||||||
const augObj = Augmentations[aug.name];
|
const augObj = StaticAugmentations[aug.name];
|
||||||
for (const mult of Object.keys(augObj.mults)) {
|
for (const mult of Object.keys(augObj.mults)) {
|
||||||
const v = augP[mult] ? augP[mult] : 1;
|
const v = augP[mult] ? augP[mult] : 1;
|
||||||
augP[mult] = v * augObj.mults[mult];
|
augP[mult] = v * augObj.mults[mult];
|
||||||
|
@ -5,15 +5,14 @@
|
|||||||
import { CheckBox, CheckBoxOutlineBlank, CheckCircle, Info, NewReleases, Report } from "@mui/icons-material";
|
import { CheckBox, CheckBoxOutlineBlank, CheckCircle, Info, NewReleases, Report } from "@mui/icons-material";
|
||||||
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
|
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { getNextNeuroFluxLevel } from "../AugmentationHelpers";
|
|
||||||
import { Faction } from "../../Faction/Faction";
|
import { Faction } from "../../Faction/Faction";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Augmentation } from "../Augmentation";
|
import { Augmentation } from "../Augmentation";
|
||||||
import { Augmentations } from "../Augmentations";
|
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
|
||||||
|
import { StaticAugmentations } from "../StaticAugmentations";
|
||||||
|
|
||||||
interface IPreReqsProps {
|
interface IPreReqsProps {
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
@ -160,10 +159,10 @@ interface IPurchasableAugProps {
|
|||||||
export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
|
export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const aug = Augmentations[props.augName];
|
const aug = StaticAugmentations[props.augName];
|
||||||
|
const augCosts = aug.getCost(props.parent.player);
|
||||||
const cost = props.parent.sleeveAugs ? aug.startingCost : aug.baseCost;
|
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 info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||||
const description = (
|
const description = (
|
||||||
<>
|
<>
|
||||||
@ -205,7 +204,8 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
|
|||||||
<>
|
<>
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
{props.augName}
|
{props.augName}
|
||||||
{props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
{props.augName === AugmentationNames.NeuroFluxGovernor &&
|
||||||
|
` - Level ${aug.getLevel(props.parent.player)}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{description}</Typography>
|
<Typography>{description}</Typography>
|
||||||
</>
|
</>
|
||||||
@ -222,7 +222,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{aug.name}
|
{aug.name}
|
||||||
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel(props.parent.player)}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
|
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
|
||||||
<Exclusive player={props.parent.player} aug={aug} />
|
<Exclusive player={props.parent.player} aug={aug} />
|
||||||
@ -236,14 +236,14 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
|
|||||||
{props.owned || (
|
{props.owned || (
|
||||||
<Box sx={{ display: "grid", alignItems: "center", justifyItems: "left" }}>
|
<Box sx={{ display: "grid", alignItems: "center", justifyItems: "left" }}>
|
||||||
<Requirement
|
<Requirement
|
||||||
fulfilled={aug.baseCost === 0 || props.parent.player.money > cost}
|
fulfilled={cost === 0 || props.parent.player.money > cost}
|
||||||
value={numeralWrapper.formatMoney(cost)}
|
value={numeralWrapper.formatMoney(cost)}
|
||||||
color={Settings.theme.money}
|
color={Settings.theme.money}
|
||||||
/>
|
/>
|
||||||
{props.parent.rep !== undefined && (
|
{props.parent.rep !== undefined && (
|
||||||
<Requirement
|
<Requirement
|
||||||
fulfilled={props.parent.rep >= aug.baseRepRequirement}
|
fulfilled={props.parent.rep >= repCost}
|
||||||
value={`${numeralWrapper.formatReputation(aug.baseRepRequirement)} rep`}
|
value={`${numeralWrapper.formatReputation(repCost)} rep`}
|
||||||
color={Settings.theme.rep}
|
color={Settings.theme.rep}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -44,7 +44,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Would you like to purchase the {props.aug.name} Augmentation for
|
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 />
|
||||||
<br />
|
<br />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
|
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { StaticAugmentations } from "../StaticAugmentations";
|
||||||
import { AugmentationNames } from "../data/AugmentationNames";
|
import { AugmentationNames } from "../data/AugmentationNames";
|
||||||
|
|
||||||
export function PurchasedAugmentations(): React.ReactElement {
|
export function PurchasedAugmentations(): React.ReactElement {
|
||||||
@ -23,7 +23,7 @@ export function PurchasedAugmentations(): React.ReactElement {
|
|||||||
let displayName = ownedAug.name;
|
let displayName = ownedAug.name;
|
||||||
|
|
||||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
||||||
const aug = Augmentations[ownedAug.name];
|
const aug = StaticAugmentations[ownedAug.name];
|
||||||
|
|
||||||
let level = null;
|
let level = null;
|
||||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||||
|
@ -293,7 +293,7 @@ export const CONSTANTS: {
|
|||||||
// BitNode/Source-File related stuff
|
// BitNode/Source-File related stuff
|
||||||
TotalNumBitNodes: 24,
|
TotalNumBitNodes: 24,
|
||||||
|
|
||||||
Donations: 6,
|
Donations: 7,
|
||||||
|
|
||||||
LatestUpdate: `
|
LatestUpdate: `
|
||||||
v1.6.3 - 2022-04-01 Few stanek fixes
|
v1.6.3 - 2022-04-01 Few stanek fixes
|
||||||
|
@ -162,11 +162,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={tutorial ? <Typography>Purchase your required materials to get production started!</Typography> : ""}
|
title={tutorial ? <Typography>Purchase your required materials to get production started!</Typography> : ""}
|
||||||
>
|
>
|
||||||
<Button
|
<Button color={tutorial ? "error" : "primary"} onClick={() => setPurchaseMaterialOpen(true)}>
|
||||||
color={tutorial ? "error" : "primary"}
|
|
||||||
onClick={() => setPurchaseMaterialOpen(true)}
|
|
||||||
disabled={props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)}
|
|
||||||
>
|
|
||||||
{purchaseButtonText}
|
{purchaseButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -174,6 +170,9 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
|||||||
mat={mat}
|
mat={mat}
|
||||||
warehouse={warehouse}
|
warehouse={warehouse}
|
||||||
open={purchaseMaterialOpen}
|
open={purchaseMaterialOpen}
|
||||||
|
disablePurchaseLimit={
|
||||||
|
props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)
|
||||||
|
}
|
||||||
onClose={() => setPurchaseMaterialOpen(false)}
|
onClose={() => setPurchaseMaterialOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ interface IProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
mat: Material;
|
mat: Material;
|
||||||
warehouse: Warehouse;
|
warehouse: Warehouse;
|
||||||
|
disablePurchaseLimit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a popup that lets the player purchase a Material
|
// Create a popup that lets the player purchase a Material
|
||||||
@ -143,6 +144,7 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
|
|||||||
<Typography>
|
<Typography>
|
||||||
Enter the amount of {props.mat.name} you would like to purchase per second. This material's cost changes
|
Enter the amount of {props.mat.name} you would like to purchase per second. This material's cost changes
|
||||||
constantly.
|
constantly.
|
||||||
|
{props.disablePurchaseLimit ? "Note: Purchase amount is disabled as smart supply is enabled" : ""}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
<TextField
|
||||||
value={buyAmt}
|
value={buyAmt}
|
||||||
@ -150,10 +152,15 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
placeholder="Purchase amount"
|
placeholder="Purchase amount"
|
||||||
type="number"
|
type="number"
|
||||||
|
disabled={props.disablePurchaseLimit}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
/>
|
/>
|
||||||
<Button onClick={purchaseMaterial}>Confirm</Button>
|
<Button disabled={props.disablePurchaseLimit} onClick={purchaseMaterial}>
|
||||||
<Button onClick={clearPurchase}>Clear Purchase</Button>
|
Confirm
|
||||||
|
</Button>
|
||||||
|
<Button disabled={props.disablePurchaseLimit} onClick={clearPurchase}>
|
||||||
|
Clear Purchase
|
||||||
|
</Button>
|
||||||
{division.hasResearch("Bulk Purchasing") && (
|
{division.hasResearch("Bulk Purchasing") && (
|
||||||
<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
|
<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||||
import { Augmentation } from "../Augmentation/Augmentation";
|
import { Augmentation } from "../Augmentation/Augmentation";
|
||||||
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
@ -18,7 +18,6 @@ import {
|
|||||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { InvitationEvent } from "./ui/InvitationModal";
|
import { InvitationEvent } from "./ui/InvitationModal";
|
||||||
import { FactionNames } from "./data/FactionNames";
|
import { FactionNames } from "./data/FactionNames";
|
||||||
import { updateAugmentationCosts, getNextNeuroFluxLevel } from "../Augmentation/AugmentationHelpers";
|
|
||||||
import { SFC32RNG } from "../Casino/RNG";
|
import { SFC32RNG } from "../Casino/RNG";
|
||||||
|
|
||||||
export function inviteToFaction(faction: Faction): void {
|
export function inviteToFaction(faction: Faction): void {
|
||||||
@ -59,6 +58,7 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
|
|||||||
|
|
||||||
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
||||||
const hasPrereqs = hasAugmentationPrereqs(aug);
|
const hasPrereqs = hasAugmentationPrereqs(aug);
|
||||||
|
const augCosts = aug.getCost(Player);
|
||||||
if (!hasPrereqs) {
|
if (!hasPrereqs) {
|
||||||
const txt = `You must first purchase or install ${aug.prereqs
|
const txt = `You must first purchase or install ${aug.prereqs
|
||||||
.filter((req) => !Player.hasAugmentation(req))
|
.filter((req) => !Player.hasAugmentation(req))
|
||||||
@ -68,28 +68,26 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
|||||||
} else {
|
} else {
|
||||||
dialogBoxCreate(txt);
|
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;
|
const txt = "You don't have enough money to purchase " + aug.name;
|
||||||
if (sing) {
|
if (sing) {
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
dialogBoxCreate(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;
|
const txt = "You don't have enough faction reputation to purchase " + aug.name;
|
||||||
if (sing) {
|
if (sing) {
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
dialogBoxCreate(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);
|
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||||
queuedAugmentation.level = getNextNeuroFluxLevel();
|
queuedAugmentation.level = aug.getLevel(Player);
|
||||||
}
|
}
|
||||||
Player.queuedAugmentations.push(queuedAugmentation);
|
Player.queuedAugmentations.push(queuedAugmentation);
|
||||||
|
|
||||||
Player.loseMoney(aug.baseCost, "augmentations");
|
Player.loseMoney(augCosts.moneyCost, "augmentations");
|
||||||
|
|
||||||
updateAugmentationCosts();
|
|
||||||
|
|
||||||
if (sing) {
|
if (sing) {
|
||||||
return "You purchased " + aug.name;
|
return "You purchased " + aug.name;
|
||||||
@ -141,14 +139,14 @@ export function processPassiveFactionRepGain(numCycles: number): void {
|
|||||||
export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Faction): string[] => {
|
export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Faction): string[] => {
|
||||||
// If player has a gang with this faction, return (almost) all augmentations
|
// If player has a gang with this faction, return (almost) all augmentations
|
||||||
if (player.hasGangWith(faction.name)) {
|
if (player.hasGangWith(faction.name)) {
|
||||||
let augs = Object.values(Augmentations);
|
let augs = Object.values(StaticAugmentations);
|
||||||
|
|
||||||
// Remove special augs
|
// Remove special augs
|
||||||
augs = augs.filter((a) => !a.isSpecial || a.name != AugmentationNames.CongruityImplant);
|
augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationNames.CongruityImplant);
|
||||||
|
|
||||||
if (player.bitNodeN === 2) {
|
if (player.bitNodeN === 2) {
|
||||||
// TRP is not available outside of BN2 for Gangs
|
// TRP is not available outside of BN2 for Gangs
|
||||||
augs.push(Augmentations[AugmentationNames.TheRedPill]);
|
augs.push(StaticAugmentations[AugmentationNames.TheRedPill]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`);
|
const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`);
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
*/
|
*/
|
||||||
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
|
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
|
||||||
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
|
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
|
||||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
|
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
|
||||||
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
@ -54,13 +55,13 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
function getAugsSortedByCost(): string[] {
|
function getAugsSortedByCost(): string[] {
|
||||||
const augs = getAugs();
|
const augs = getAugs();
|
||||||
augs.sort((augName1, augName2) => {
|
augs.sort((augName1, augName2) => {
|
||||||
const aug1 = Augmentations[augName1],
|
const aug1 = StaticAugmentations[augName1],
|
||||||
aug2 = Augmentations[augName2];
|
aug2 = StaticAugmentations[augName2];
|
||||||
if (aug1 == null || aug2 == null) {
|
if (aug1 == null || aug2 == null) {
|
||||||
throw new Error("Invalid Augmentation Names");
|
throw new Error("Invalid Augmentation Names");
|
||||||
}
|
}
|
||||||
|
|
||||||
return aug1.baseCost - aug2.baseCost;
|
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost;
|
||||||
});
|
});
|
||||||
|
|
||||||
return augs;
|
return augs;
|
||||||
@ -69,31 +70,32 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
function getAugsSortedByPurchasable(): string[] {
|
function getAugsSortedByPurchasable(): string[] {
|
||||||
const augs = getAugs();
|
const augs = getAugs();
|
||||||
function canBuy(augName: string): boolean {
|
function canBuy(augName: string): boolean {
|
||||||
const aug = Augmentations[augName];
|
const aug = StaticAugmentations[augName];
|
||||||
const repCost = aug.baseRepRequirement;
|
const augCosts = aug.getCost(player);
|
||||||
|
const repCost = augCosts.repCost;
|
||||||
const hasReq = props.faction.playerReputation >= repCost;
|
const hasReq = props.faction.playerReputation >= repCost;
|
||||||
const hasRep = hasAugmentationPrereqs(aug);
|
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;
|
return hasCost && hasReq && hasRep;
|
||||||
}
|
}
|
||||||
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
||||||
const aug1 = Augmentations[augName1],
|
const aug1 = StaticAugmentations[augName1],
|
||||||
aug2 = Augmentations[augName2];
|
aug2 = StaticAugmentations[augName2];
|
||||||
if (aug1 == null || aug2 == null) {
|
if (aug1 == null || aug2 == null) {
|
||||||
throw new Error("Invalid Augmentation Names");
|
throw new Error("Invalid Augmentation Names");
|
||||||
}
|
}
|
||||||
|
|
||||||
return aug1.baseCost - aug2.baseCost;
|
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost;
|
||||||
});
|
});
|
||||||
const cantBuy = augs
|
const cantBuy = augs
|
||||||
.filter((aug) => !canBuy(aug))
|
.filter((aug) => !canBuy(aug))
|
||||||
.sort((augName1, augName2) => {
|
.sort((augName1, augName2) => {
|
||||||
const aug1 = Augmentations[augName1],
|
const aug1 = StaticAugmentations[augName1],
|
||||||
aug2 = Augmentations[augName2];
|
aug2 = StaticAugmentations[augName2];
|
||||||
if (aug1 == null || aug2 == null) {
|
if (aug1 == null || aug2 == null) {
|
||||||
throw new Error("Invalid Augmentation Names");
|
throw new Error("Invalid Augmentation Names");
|
||||||
}
|
}
|
||||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
return aug1.getCost(player).repCost - aug2.getCost(player).repCost;
|
||||||
});
|
});
|
||||||
|
|
||||||
return buy.concat(cantBuy);
|
return buy.concat(cantBuy);
|
||||||
@ -102,12 +104,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
function getAugsSortedByReputation(): string[] {
|
function getAugsSortedByReputation(): string[] {
|
||||||
const augs = getAugs();
|
const augs = getAugs();
|
||||||
augs.sort((augName1, augName2) => {
|
augs.sort((augName1, augName2) => {
|
||||||
const aug1 = Augmentations[augName1],
|
const aug1 = StaticAugmentations[augName1],
|
||||||
aug2 = Augmentations[augName2];
|
aug2 = StaticAugmentations[augName2];
|
||||||
if (aug1 == null || aug2 == null) {
|
if (aug1 == null || aug2 == null) {
|
||||||
throw new Error("Invalid Augmentation Names");
|
throw new Error("Invalid Augmentation Names");
|
||||||
}
|
}
|
||||||
return aug1.baseRepRequirement - aug2.baseRepRequirement;
|
return aug1.getCost(player).repCost - aug2.getCost(player).repCost;
|
||||||
});
|
});
|
||||||
|
|
||||||
return augs;
|
return augs;
|
||||||
@ -195,10 +197,11 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
ownedAugNames={owned}
|
ownedAugNames={owned}
|
||||||
player={player}
|
player={player}
|
||||||
canPurchase={(player, aug) => {
|
canPurchase={(player, aug) => {
|
||||||
|
const costs = aug.getCost(player);
|
||||||
return (
|
return (
|
||||||
hasAugmentationPrereqs(aug) &&
|
hasAugmentationPrereqs(aug) &&
|
||||||
props.faction.playerReputation >= aug.baseRepRequirement &&
|
props.faction.playerReputation >= costs.repCost &&
|
||||||
(aug.baseCost === 0 || player.money > aug.baseCost)
|
(costs.moneyCost === 0 || player.money > costs.moneyCost)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
purchaseAugmentation={(player, aug, showModal) => {
|
purchaseAugmentation={(player, aug, showModal) => {
|
||||||
|
@ -16,12 +16,10 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
|||||||
import { Faction } from "../Faction";
|
import { Faction } from "../Faction";
|
||||||
|
|
||||||
import { use } from "../../ui/Context";
|
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 { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
|
||||||
import { FactionNames } from "../data/FactionNames";
|
import { FactionNames } from "../data/FactionNames";
|
||||||
import { GangConstants } from "../../Gang/data/Constants";
|
|
||||||
import { GangButton } from "./GangButton";
|
import { GangButton } from "./GangButton";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
@ -30,7 +28,6 @@ type IProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Info text for all options on the UI
|
// 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 =
|
const hackingContractsInfo =
|
||||||
"Complete hacking contracts for your faction. " +
|
"Complete hacking contracts for your faction. " +
|
||||||
"Your effectiveness, which determines how much " +
|
"Your effectiveness, which determines how much " +
|
||||||
|
@ -189,9 +189,9 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
|
|||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game.
|
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
|
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.
|
running instances of all scripts.
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
@ -210,7 +210,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
|
|||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
If your save file is extremely big you can use this button to view a map of all the files on every server.
|
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>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -60,15 +60,22 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
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) -
|
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);
|
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult);
|
||||||
upgradeLevelButton = (
|
upgradeLevelButton = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<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>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -109,20 +116,36 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
||||||
}
|
}
|
||||||
|
|
||||||
const increase =
|
const base_increase =
|
||||||
calculateHashGainRate(
|
calculateHashGainRate(
|
||||||
node.level,
|
node.level,
|
||||||
0,
|
0,
|
||||||
node.maxRam * Math.pow(2, multiplier),
|
node.maxRam * Math.pow(2, multiplier),
|
||||||
node.cores,
|
node.cores,
|
||||||
props.player.hacknet_node_money_mult,
|
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);
|
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult);
|
||||||
upgradeRamButton = (
|
upgradeRamButton = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<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>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -155,15 +178,22 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
multiplier = Math.min(levelsToMax, purchaseMult as number);
|
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) -
|
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);
|
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult);
|
||||||
upgradeCoresButton = (
|
upgradeCoresButton = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<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>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -232,9 +262,31 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
|||||||
<Typography>Production:</Typography>
|
<Typography>Production:</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell colSpan={2}>
|
<TableCell colSpan={2}>
|
||||||
<Typography>
|
<Tooltip
|
||||||
<Hashes hashes={node.totalHashesGenerated} /> (<HashRate hashes={node.hashRate} />)
|
title={
|
||||||
</Typography>
|
<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>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Paper, Typography } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
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 { IMinigameProps } from "./IMinigameProps";
|
||||||
import { KeyHandler } from "./KeyHandler";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -48,24 +47,18 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<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>
|
<Typography variant="h4">Type it{!hasAugment ? " backward" : ""}</Typography>
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
<Typography style={{ transform: hasAugment ? "none" : "scaleX(-1)" }}>{answer}</Typography>
|
||||||
<Grid item xs={6}>
|
|
||||||
<Typography style={{ transform: hasAugment ? "none" : "scaleX(-1)", marginLeft: hasAugment ? "50%" : "none" }}>
|
|
||||||
{answer}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<Typography>
|
<Typography>
|
||||||
{guess}
|
{guess}
|
||||||
<BlinkingCursor />
|
<BlinkingCursor />
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
export function BlinkingCursor(): React.ReactElement {
|
export function BlinkingCursor(): React.ReactElement {
|
||||||
const [on, setOn] = useState(true);
|
const [on, setOn] = useState(true);
|
||||||
@ -6,5 +6,5 @@ export function BlinkingCursor(): React.ReactElement {
|
|||||||
const i = setInterval(() => setOn((old) => !old), 1000);
|
const i = setInterval(() => setOn((old) => !old), 1000);
|
||||||
return () => clearInterval(i);
|
return () => clearInterval(i);
|
||||||
});
|
});
|
||||||
return <>{on ? "|" : ""}</>;
|
return <>{on ? "|" : <> </>}</>;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Paper, Typography } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
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 { IMinigameProps } from "./IMinigameProps";
|
||||||
import { KeyHandler } from "./KeyHandler";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -84,16 +83,16 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||||
<Grid item xs={12}>
|
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||||
<Typography variant="h4">Close the brackets</Typography>
|
<Typography variant="h4">Close the brackets</Typography>
|
||||||
<Typography style={{ fontSize: "5em" }}>
|
<Typography style={{ fontSize: "5em" }}>
|
||||||
{`${left}${right}`}
|
{`${left}${right}`}
|
||||||
<BlinkingCursor />
|
<BlinkingCursor />
|
||||||
</Typography>
|
</Typography>
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Paper, Typography } from "@mui/material";
|
||||||
import React, { useEffect, useState } from "react";
|
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 { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
import { downArrowSymbol, upArrowSymbol } from "../utils";
|
import { downArrowSymbol, upArrowSymbol } from "../utils";
|
||||||
|
import { interpolate } from "./Difficulty";
|
||||||
|
import { GameTimer } from "./GameTimer";
|
||||||
|
import { IMinigameProps } from "./IMinigameProps";
|
||||||
|
import { KeyHandler } from "./KeyHandler";
|
||||||
|
|
||||||
interface Difficulty {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -88,13 +87,11 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||||
<Grid item xs={12}>
|
<Paper sx={{ display: "grid", justifyItems: "center" }}>
|
||||||
<Typography variant="h4">Say something nice about the guard.</Typography>
|
<Typography variant="h4">Say something nice about the guard</Typography>
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<Typography variant="h5" color={upColor}>
|
<Typography variant="h5" color={upColor}>
|
||||||
{upArrowSymbol}
|
{upArrowSymbol}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -104,8 +101,8 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
|
|||||||
<Typography variant="h5" color={downColor}>
|
<Typography variant="h5" color={downColor}>
|
||||||
{downArrowSymbol}
|
{downArrowSymbol}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +151,7 @@ const positive = [
|
|||||||
"patient",
|
"patient",
|
||||||
"dynamic",
|
"dynamic",
|
||||||
"loyal",
|
"loyal",
|
||||||
|
"based",
|
||||||
];
|
];
|
||||||
|
|
||||||
const negative = [
|
const negative = [
|
||||||
@ -177,4 +175,5 @@ const negative = [
|
|||||||
"picky",
|
"picky",
|
||||||
"tactless",
|
"tactless",
|
||||||
"thoughtless",
|
"thoughtless",
|
||||||
|
"cringe",
|
||||||
];
|
];
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
|
import { Paper, Typography } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Grid from "@mui/material/Grid";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { IMinigameProps } from "./IMinigameProps";
|
import { Player } from "../../Player";
|
||||||
import { KeyHandler } from "./KeyHandler";
|
|
||||||
import { GameTimer } from "./GameTimer";
|
|
||||||
import {
|
import {
|
||||||
random,
|
downArrowSymbol,
|
||||||
getArrow,
|
getArrow,
|
||||||
getInverseArrow,
|
getInverseArrow,
|
||||||
leftArrowSymbol,
|
leftArrowSymbol,
|
||||||
|
random,
|
||||||
rightArrowSymbol,
|
rightArrowSymbol,
|
||||||
upArrowSymbol,
|
upArrowSymbol,
|
||||||
downArrowSymbol,
|
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { interpolate } from "./Difficulty";
|
import { interpolate } from "./Difficulty";
|
||||||
import Typography from "@mui/material/Typography";
|
import { GameTimer } from "./GameTimer";
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { IMinigameProps } from "./IMinigameProps";
|
||||||
import { Player } from "../../Player";
|
import { KeyHandler } from "./KeyHandler";
|
||||||
|
|
||||||
interface Difficulty {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -55,14 +54,14 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<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">Enter the Code!</Typography>
|
||||||
<Typography variant="h4">{code[index]}</Typography>
|
<Typography variant="h4">{code[index]}</Typography>
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { Paper, Typography } from "@mui/material";
|
||||||
import Grid from "@mui/material/Grid";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onFinish: () => void;
|
onFinish: () => void;
|
||||||
}
|
}
|
||||||
@ -13,17 +12,13 @@ export function Countdown(props: IProps): React.ReactElement {
|
|||||||
props.onFinish();
|
props.onFinish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setTimeout(() => setX(x - 1), 200);
|
setTimeout(() => setX(x - 1), 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ p: 1, textAlign: "center" }}>
|
||||||
<Grid container spacing={3}>
|
<Typography variant="h4">Get Ready!</Typography>
|
||||||
<Grid item xs={12}>
|
<Typography variant="h4">{x}</Typography>
|
||||||
<Typography variant="h4">Get Ready!</Typography>
|
</Paper>
|
||||||
<Typography variant="h4">{x}</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Paper, Typography, Box } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
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 { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { Player } from "../../Player";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -19,6 +18,12 @@ interface Difficulty {
|
|||||||
symbols: number;
|
symbols: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GridItem {
|
||||||
|
content: string;
|
||||||
|
color: string;
|
||||||
|
selected?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const difficulties: {
|
const difficulties: {
|
||||||
Trivial: Difficulty;
|
Trivial: Difficulty;
|
||||||
Normal: 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";
|
const fontSize = "2em";
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<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="h4">Match the symbols!</Typography>
|
||||||
<Typography variant="h5" color={Settings.theme.primary}>
|
<Typography variant="h5" color={Settings.theme.primary}>
|
||||||
Targets:{" "}
|
Targets:{" "}
|
||||||
{answers.map((a, i) => {
|
{answers.map((a, i) => {
|
||||||
if (i == currentAnswerIndex)
|
if (i == currentAnswerIndex)
|
||||||
return (
|
return (
|
||||||
<span key={`${i}`} style={{ fontSize: "1em", color: "blue" }}>
|
<span key={`${i}`} style={{ fontSize: "1em", color: Settings.theme.infolight }}>
|
||||||
{a}
|
{a}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -99,34 +119,30 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
|||||||
})}
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
{grid.map((line, y) => (
|
<Box
|
||||||
<div key={y}>
|
sx={{
|
||||||
<Typography>
|
display: "grid",
|
||||||
{line.map((cell, x) => {
|
gridTemplateColumns: `repeat(${Math.round(difficulty.width)}, 1fr)`,
|
||||||
const isCorrectAnswer = cell === answers[currentAnswerIndex];
|
gap: 1,
|
||||||
|
}}
|
||||||
if (x == pos[0] && y == pos[1]) {
|
>
|
||||||
return (
|
{flatGrid.map((item) => (
|
||||||
<span key={`${x}${y}`} style={{ fontSize: fontSize, color: "blue" }}>
|
<Typography
|
||||||
{cell}
|
sx={{
|
||||||
</span>
|
fontSize: fontSize,
|
||||||
);
|
color: item.color,
|
||||||
}
|
border: item.selected ? `2px solid ${Settings.theme.infolight}` : "unset",
|
||||||
|
lineHeight: "unset",
|
||||||
const optionColor = hasAugment && !isCorrectAnswer ? Settings.theme.disabled : Settings.theme.primary;
|
p: item.selected ? "2px" : "4px",
|
||||||
return (
|
}}
|
||||||
<span key={`${x}${y}`} style={{ fontSize: fontSize, color: optionColor }}>
|
>
|
||||||
{cell}
|
{item.content}
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
))}
|
||||||
</div>
|
</Box>
|
||||||
))}
|
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import { use } from "../../ui/Context";
|
import { Button, Container, Paper, Typography } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Grid from "@mui/material/Grid";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import Button from "@mui/material/Button";
|
import { use } from "../../ui/Context";
|
||||||
import { Countdown } from "./Countdown";
|
|
||||||
import { BracketGame } from "./BracketGame";
|
|
||||||
import { SlashGame } from "./SlashGame";
|
|
||||||
import { BackwardGame } from "./BackwardGame";
|
import { BackwardGame } from "./BackwardGame";
|
||||||
|
import { BracketGame } from "./BracketGame";
|
||||||
import { BribeGame } from "./BribeGame";
|
import { BribeGame } from "./BribeGame";
|
||||||
import { CheatCodeGame } from "./CheatCodeGame";
|
import { CheatCodeGame } from "./CheatCodeGame";
|
||||||
|
import { Countdown } from "./Countdown";
|
||||||
import { Cyberpunk2077Game } from "./Cyberpunk2077Game";
|
import { Cyberpunk2077Game } from "./Cyberpunk2077Game";
|
||||||
import { MinesweeperGame } from "./MinesweeperGame";
|
import { MinesweeperGame } from "./MinesweeperGame";
|
||||||
import { WireCuttingGame } from "./WireCuttingGame";
|
import { SlashGame } from "./SlashGame";
|
||||||
import { Victory } from "./Victory";
|
import { Victory } from "./Victory";
|
||||||
import Typography from "@mui/material/Typography";
|
import { WireCuttingGame } from "./WireCuttingGame";
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
StartingDifficulty: number;
|
StartingDifficulty: number;
|
||||||
@ -139,22 +137,20 @@ export function Game(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<Grid container spacing={3}>
|
<Paper sx={{ p: 1, mb: 1, display: "grid", justifyItems: "center", gap: 1 }}>
|
||||||
<Grid item xs={3}>
|
{stage !== Stage.Sell && (
|
||||||
<Button onClick={cancel}>Cancel</Button>
|
<Button sx={{ width: "100%" }} onClick={cancel}>
|
||||||
</Grid>
|
Cancel Infiltration
|
||||||
<Grid item xs={3}>
|
</Button>
|
||||||
<Typography>
|
)}
|
||||||
Level: {level} / {props.MaxLevel}
|
<Typography variant="h5">
|
||||||
</Typography>
|
Level {level} / {props.MaxLevel}
|
||||||
<Progress />
|
</Typography>
|
||||||
</Grid>
|
<Progress />
|
||||||
|
</Paper>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
{stageComponent}
|
||||||
{stageComponent}
|
</Container>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Grid from "@mui/material/Grid";
|
import { Paper } from "@mui/material";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
@ -7,6 +7,7 @@ import { ProgressBar } from "../../ui/React/Progress";
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
millis: number;
|
millis: number;
|
||||||
onExpire: () => void;
|
onExpire: () => void;
|
||||||
|
noPaper?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GameTimer(props: IProps): React.ReactElement {
|
export function GameTimer(props: IProps): React.ReactElement {
|
||||||
@ -30,9 +31,11 @@ export function GameTimer(props: IProps): React.ReactElement {
|
|||||||
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
||||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||||
// bar physically reaches the end
|
// bar physically reaches the end
|
||||||
return (
|
return props.noPaper ? (
|
||||||
<Grid item xs={12}>
|
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||||
|
) : (
|
||||||
|
<Paper sx={{ p: 1, mb: 1 }}>
|
||||||
<ProgressBar variant="determinate" value={v} color="primary" />
|
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||||
</Grid>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Intro } from "./Intro";
|
|
||||||
import { Game } from "./Game";
|
|
||||||
import { Location } from "../../Locations/Location";
|
import { Location } from "../../Locations/Location";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { calculateDifficulty, calculateReward } from "../formulas/game";
|
import { calculateDifficulty, calculateReward } from "../formulas/game";
|
||||||
|
import { Game } from "./Game";
|
||||||
|
import { Intro } from "./Intro";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
location: Location;
|
location: Location;
|
||||||
}
|
}
|
||||||
@ -22,24 +22,24 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
|
|||||||
router.toCity();
|
router.toCity();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!start) {
|
|
||||||
return (
|
|
||||||
<Intro
|
|
||||||
Location={props.location}
|
|
||||||
Difficulty={difficulty}
|
|
||||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
|
||||||
start={() => setStart(true)}
|
|
||||||
cancel={cancel}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Game
|
<div style={{ display: "flex", alignItems: "center", height: "calc(100vh - 16px)" }}>
|
||||||
StartingDifficulty={startingSecurityLevel}
|
{start ? (
|
||||||
Difficulty={difficulty}
|
<Game
|
||||||
Reward={reward}
|
StartingDifficulty={startingSecurityLevel}
|
||||||
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
|
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 React from "react";
|
||||||
import { Location } from "../../Locations/Location";
|
import { Location } from "../../Locations/Location";
|
||||||
import Grid from "@mui/material/Grid";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import Button from "@mui/material/Button";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -41,9 +41,9 @@ function coloredArrow(difficulty: number): JSX.Element {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{arrowPart("white", difficulty * 13)}
|
{arrowPart(Settings.theme.primary, difficulty * 13)}
|
||||||
{arrowPart("orange", (difficulty - 1) * 13)}
|
{arrowPart(Settings.theme.warning, (difficulty - 1) * 13)}
|
||||||
{arrowPart("red", (difficulty - 2) * 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 {
|
export function Intro(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<Container sx={{ alignItems: "center" }}>
|
||||||
<Grid container spacing={3}>
|
<Paper sx={{ p: 1, mb: 1, display: "grid", justifyItems: "center" }}>
|
||||||
<Grid item xs={10}>
|
<Typography variant="h4">
|
||||||
<Typography variant="h4">Infiltrating {props.Location.name}</Typography>
|
Infiltrating <b>{props.Location.name}</b>
|
||||||
</Grid>
|
</Typography>
|
||||||
<Grid item xs={10}>
|
<Typography variant="h6">
|
||||||
<Typography variant="h5" color="primary">
|
<b>Maximum Level: </b>
|
||||||
Maximum level: {props.MaxLevel}
|
{props.MaxLevel}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
<Typography
|
||||||
<Grid item xs={10}>
|
variant="h6"
|
||||||
<Typography variant="h5" color="primary">
|
sx={{
|
||||||
Difficulty: {numeralWrapper.format(props.Difficulty * 33.3333, "0")} / 100
|
color:
|
||||||
</Typography>
|
props.Difficulty > 2
|
||||||
</Grid>
|
? 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 && (
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
|
||||||
<Grid item xs={10}>
|
<Typography
|
||||||
<Typography variant="h5" color="primary">
|
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||||
Warning: This location is too heavily guarded for your current stats, try training or finding an easier
|
>{`▲ ▲ ▲ ▲`}</Typography>
|
||||||
location.
|
<Typography
|
||||||
</Typography>
|
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
||||||
</Grid>
|
>{` Trivial Normal Hard Impossible`}</Typography>
|
||||||
)}
|
</Paper>
|
||||||
|
|
||||||
<Grid item xs={10}>
|
<Paper sx={{ p: 1, display: "grid", justifyItems: "center" }}>
|
||||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
|
<Typography sx={{ width: "75%", textAlign: "center" }}>
|
||||||
<Typography
|
<b>Infiltration</b> is a series of short minigames that get progressively harder. You take damage for failing
|
||||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
them. Reaching the maximum level rewards you with intel that you can trade for money or reputation.
|
||||||
>{` ^ ^ ^ ^`}</Typography>
|
<br />
|
||||||
<Typography
|
<br />
|
||||||
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
|
<b>Gameplay:</b>
|
||||||
>{` Trivial Normal Hard Impossible`}</Typography>
|
</Typography>
|
||||||
</Grid>
|
<ul>
|
||||||
<Grid item xs={10}>
|
|
||||||
<Typography>
|
<Typography>
|
||||||
Infiltration is a series of short minigames that get progressively harder. You take damage for failing them.
|
<li>
|
||||||
Reaching the maximum level rewards you with intel you can trade for money or reputation.
|
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>
|
</Typography>
|
||||||
<br />
|
</ul>
|
||||||
<Typography>
|
|
||||||
The minigames you play are randomly selected. It might take you few tries to get used to them.
|
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", width: "100%" }}>
|
||||||
</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}>
|
|
||||||
<Button onClick={props.start}>Start</Button>
|
<Button onClick={props.start}>Start</Button>
|
||||||
</Grid>
|
|
||||||
<Grid item xs={3}>
|
|
||||||
<Button onClick={props.cancel}>Cancel</Button>
|
<Button onClick={props.cancel}>Cancel</Button>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
</Paper>
|
||||||
</>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { Close, Flag, Report } from "@mui/icons-material";
|
||||||
import Grid from "@mui/material/Grid";
|
import { Box, Paper, Typography } from "@mui/material";
|
||||||
import { IMinigameProps } from "./IMinigameProps";
|
import { uniqueId } from "lodash";
|
||||||
import { KeyHandler } from "./KeyHandler";
|
import React, { useEffect, useState } from "react";
|
||||||
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 { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { Player } from "../../Player";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -81,32 +83,77 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
|||||||
return () => clearInterval(id);
|
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 (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<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>
|
<Typography variant="h4">{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}</Typography>
|
||||||
{minefield.map((line, y) => (
|
<Box
|
||||||
<div key={y}>
|
sx={{
|
||||||
<Typography>
|
display: "grid",
|
||||||
{line.map((cell, x) => {
|
gridTemplateColumns: `repeat(${Math.round(difficulty.width)}, 1fr)`,
|
||||||
if (memoryPhase) {
|
gridTemplateRows: `repeat(${Math.round(difficulty.height)}, 1fr)`,
|
||||||
if (minefield[y][x]) return <span key={x}>[?] </span>;
|
gap: 1,
|
||||||
return <span key={x}>[ ] </span>;
|
}}
|
||||||
} else {
|
>
|
||||||
if (x == pos[0] && y == pos[1]) return <span key={x}>[X] </span>;
|
{flatGrid.map((item) => {
|
||||||
if (answer[y][x]) return <span key={x}>[.] </span>;
|
let color: string;
|
||||||
if (hasAugment && minefield[y][x]) return <span key={x}>[?] </span>;
|
let icon: React.ReactElement;
|
||||||
return <span key={x}>[ ] </span>;
|
|
||||||
}
|
if (item.marked) {
|
||||||
})}
|
color = Settings.theme.warning;
|
||||||
</Typography>
|
icon = <Flag />;
|
||||||
<br />
|
} else if (item.current) {
|
||||||
</div>
|
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} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import { Box, Paper, Typography } from "@mui/material";
|
||||||
import Grid from "@mui/material/Grid";
|
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 { IMinigameProps } from "./IMinigameProps";
|
||||||
import { KeyHandler } from "./KeyHandler";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -59,23 +58,25 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
<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>
|
<Typography variant="h4">Slash when his guard is down!</Typography>
|
||||||
|
|
||||||
{hasAugment ? (
|
{hasAugment ? (
|
||||||
<>
|
<Box sx={{ my: 1 }}>
|
||||||
<Typography variant="h4">Guard will drop in...</Typography>
|
<Typography variant="h5">Guard will drop in...</Typography>
|
||||||
<GameTimer millis={timeUntilAttacking} onExpire={props.onFailure} />
|
<GameTimer millis={timeUntilAttacking} onExpire={() => null} noPaper />
|
||||||
</>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{phase === 0 && <Typography variant="h4">Guarding ...</Typography>}
|
{phase === 0 && <Typography variant="h4">Guarding ...</Typography>}
|
||||||
{phase === 1 && <Typography variant="h4">Preparing?</Typography>}
|
{phase === 1 && <Typography variant="h4">Preparing?</Typography>}
|
||||||
{phase === 2 && <Typography variant="h4">ATTACKING!</Typography>}
|
{phase === 2 && <Typography variant="h4">ATTACKING!</Typography>}
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 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 { Money } from "../../ui/React/Money";
|
||||||
import { Reputation } from "../../ui/React/Reputation";
|
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 { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import {
|
import {
|
||||||
calculateInfiltratorsRepReward,
|
calculateInfiltratorsRepReward,
|
||||||
calculateSellInformationCashReward,
|
calculateSellInformationCashReward,
|
||||||
calculateTradeInformationRepReward,
|
calculateTradeInformationRepReward,
|
||||||
} from "../formulas/victory";
|
} from "../formulas/victory";
|
||||||
import { inviteToFaction } from "../../Faction/FactionHelpers";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
StartingDifficulty: number;
|
StartingDifficulty: number;
|
||||||
@ -66,24 +62,22 @@ export function Victory(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ p: 1, textAlign: "center", display: "flex", alignItems: "center", flexDirection: "column" }}>
|
||||||
<Grid container spacing={3}>
|
<Typography variant="h4">Infiltration successful!</Typography>
|
||||||
<Grid item xs={10}>
|
<Typography variant="h5" color="primary" width="75%">
|
||||||
<Typography variant="h4">Infiltration successful!</Typography>
|
You{" "}
|
||||||
</Grid>
|
{isMemberOfInfiltrators ? (
|
||||||
<Grid item xs={10}>
|
<>
|
||||||
<Typography variant="h5" color="primary">
|
have gained {formatNumber(infiltrationRepGain, 2)} rep for {FactionNames.ShadowsOfAnarchy} and{" "}
|
||||||
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%" }}>
|
||||||
can trade the confidential information you found for money or reputation.
|
<Select value={faction} onChange={changeDropdown} sx={{ mr: 1 }}>
|
||||||
</Typography>
|
|
||||||
<Select value={faction} onChange={changeDropdown}>
|
|
||||||
<MenuItem key={"none"} value={"none"}>
|
<MenuItem key={"none"} value={"none"}>
|
||||||
{"none"}
|
{"none"}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -98,17 +92,15 @@ export function Victory(props: IProps): React.ReactElement {
|
|||||||
<Button onClick={trade}>
|
<Button onClick={trade}>
|
||||||
Trade for <Reputation reputation={repGain} /> reputation
|
Trade for <Reputation reputation={repGain} /> reputation
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Box>
|
||||||
<Grid item xs={3}>
|
<Button onClick={sell} sx={{ width: "100%" }}>
|
||||||
<Button onClick={sell}>
|
Sell for
|
||||||
Sell for
|
<Money money={moneyGain} />
|
||||||
<Money money={moneyGain} />
|
</Button>
|
||||||
</Button>
|
</Box>
|
||||||
</Grid>
|
<Button onClick={quitInfiltration} sx={{ width: "100%", mt: 1 }}>
|
||||||
<Grid item xs={3}>
|
Quit
|
||||||
<Button onClick={quitInfiltration}>Quit</Button>
|
</Button>
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { Box, Paper, Typography } from "@mui/material";
|
||||||
import React, { useEffect, useState } from "react";
|
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 { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
import { Player } from "../../Player";
|
import { Player } from "../../Player";
|
||||||
import { Settings } from "../../Settings/Settings";
|
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 {
|
interface Difficulty {
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
@ -102,46 +101,53 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={3}>
|
<>
|
||||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||||
<Grid item xs={12}>
|
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
||||||
<Typography variant="h4">Cut the wires with the following properties! (keyboard 1 to 9)</Typography>
|
<Typography variant="h4" sx={{ width: "75%", textAlign: "center" }}>
|
||||||
|
Cut the wires with the following properties! (keyboard 1 to 9)
|
||||||
|
</Typography>
|
||||||
{questions.map((question, i) => (
|
{questions.map((question, i) => (
|
||||||
<Typography key={i}>{question.toString()}</Typography>
|
<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) => {
|
{new Array(wires.length).fill(0).map((_, i) => {
|
||||||
const isCorrectWire = checkWire(i + 1);
|
const isCorrectWire = checkWire(i + 1);
|
||||||
const color = hasAugment && !isCorrectWire ? Settings.theme.disabled : Settings.theme.primary;
|
const color = hasAugment && !isCorrectWire ? Settings.theme.disabled : Settings.theme.primary;
|
||||||
return (
|
return (
|
||||||
<span key={i} style={{ color: color }}>
|
<Typography key={i} style={{ color: color }}>
|
||||||
{i + 1}
|
{i + 1}
|
||||||
</span>
|
</Typography>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Typography>
|
{new Array(8).fill(0).map((_, i) => (
|
||||||
{new Array(8).fill(0).map((_, i) => (
|
<React.Fragment key={i}>
|
||||||
<div key={i}>
|
|
||||||
<Typography>
|
|
||||||
{wires.map((wire, j) => {
|
{wires.map((wire, j) => {
|
||||||
if ((i === 3 || i === 4) && cutWires[j]) {
|
if ((i === 3 || i === 4) && cutWires[j]) {
|
||||||
return <span key={j}> </span>;
|
return <Typography key={j}></Typography>;
|
||||||
}
|
}
|
||||||
const isCorrectWire = checkWire(j + 1);
|
const isCorrectWire = checkWire(j + 1);
|
||||||
const wireColor =
|
const wireColor =
|
||||||
hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length];
|
hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length];
|
||||||
return (
|
return (
|
||||||
<span key={j} style={{ color: wireColor }}>
|
<Typography key={j} style={{ color: wireColor }}>
|
||||||
|{wire.tpe}|
|
|{wire.tpe}|
|
||||||
</span>
|
</Typography>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Typography>
|
</React.Fragment>
|
||||||
</div>
|
))}
|
||||||
))}
|
</Box>
|
||||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||||
</Grid>
|
</Paper>
|
||||||
</Grid>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { ITutorialEvents } from "./ui/InteractiveTutorial/ITutorialEvents";
|
|||||||
// Ordered array of keys to Interactive Tutorial Steps
|
// Ordered array of keys to Interactive Tutorial Steps
|
||||||
enum iTutorialSteps {
|
enum iTutorialSteps {
|
||||||
Start,
|
Start,
|
||||||
|
NSSelection,
|
||||||
GoToCharacterPage, // Click on 'Stats' page
|
GoToCharacterPage, // Click on 'Stats' page
|
||||||
CharacterPage, // Introduction to 'Stats' page
|
CharacterPage, // Introduction to 'Stats' page
|
||||||
CharacterGoToTerminalPage, // Go back to Terminal
|
CharacterGoToTerminalPage, // Go back to Terminal
|
||||||
@ -43,6 +44,7 @@ const ITutorial: {
|
|||||||
isRunning: boolean;
|
isRunning: boolean;
|
||||||
stepIsDone: {
|
stepIsDone: {
|
||||||
[iTutorialSteps.Start]: boolean;
|
[iTutorialSteps.Start]: boolean;
|
||||||
|
[iTutorialSteps.NSSelection]: boolean;
|
||||||
[iTutorialSteps.GoToCharacterPage]: boolean;
|
[iTutorialSteps.GoToCharacterPage]: boolean;
|
||||||
[iTutorialSteps.CharacterPage]: boolean;
|
[iTutorialSteps.CharacterPage]: boolean;
|
||||||
[iTutorialSteps.CharacterGoToTerminalPage]: boolean;
|
[iTutorialSteps.CharacterGoToTerminalPage]: boolean;
|
||||||
@ -80,6 +82,7 @@ const ITutorial: {
|
|||||||
// Keeps track of whether each step has been done
|
// Keeps track of whether each step has been done
|
||||||
stepIsDone: {
|
stepIsDone: {
|
||||||
[iTutorialSteps.Start]: false,
|
[iTutorialSteps.Start]: false,
|
||||||
|
[iTutorialSteps.NSSelection]: false,
|
||||||
[iTutorialSteps.GoToCharacterPage]: false,
|
[iTutorialSteps.GoToCharacterPage]: false,
|
||||||
[iTutorialSteps.CharacterPage]: false,
|
[iTutorialSteps.CharacterPage]: false,
|
||||||
[iTutorialSteps.CharacterGoToTerminalPage]: false,
|
[iTutorialSteps.CharacterGoToTerminalPage]: false,
|
||||||
|
@ -156,6 +156,7 @@ const singularity: IMap<any> = {
|
|||||||
getUpgradeHomeCoresCost: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 2),
|
getUpgradeHomeCoresCost: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 2),
|
||||||
workForCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
workForCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||||
applyToCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
applyToCompany: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||||
|
quitJob: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost),
|
||||||
getCompanyRep: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
getCompanyRep: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
||||||
getCompanyFavor: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
getCompanyFavor: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 3),
|
||||||
getCompanyFavorGain: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 4),
|
getCompanyFavorGain: SF4Cost(RamCostConstants.ScriptSingularityFn2RamCost / 4),
|
||||||
|
@ -1233,16 +1233,21 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
killall: function (_hostname: unknown = workerScript.hostname): boolean {
|
killall: function (_hostname: unknown = workerScript.hostname, _safetyguard: unknown = true): boolean {
|
||||||
updateDynamicRam("killall", getRamCost(Player, "killall"));
|
updateDynamicRam("killall", getRamCost(Player, "killall"));
|
||||||
const hostname = helper.string("killall", "hostname", _hostname);
|
const hostname = helper.string("killall", "hostname", _hostname);
|
||||||
|
const safetyguard = helper.boolean(_safetyguard);
|
||||||
if (hostname === undefined) {
|
if (hostname === undefined) {
|
||||||
throw makeRuntimeErrorMsg("killall", "Takes 1 argument");
|
throw makeRuntimeErrorMsg("killall", "Usage: killall(hostname, [safetyguard boolean])");
|
||||||
}
|
}
|
||||||
const server = safeGetServer(hostname, "killall");
|
const server = safeGetServer(hostname, "killall");
|
||||||
const scriptsRunning = server.runningScripts.length > 0;
|
|
||||||
|
let scriptsKilled = 0;
|
||||||
|
|
||||||
for (let i = server.runningScripts.length - 1; i >= 0; --i) {
|
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);
|
killWorkerScript(server.runningScripts[i], server.hostname, false);
|
||||||
|
++scriptsKilled;
|
||||||
}
|
}
|
||||||
WorkerScriptStartStopEventEmitter.emit();
|
WorkerScriptStartStopEventEmitter.emit();
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
@ -1250,7 +1255,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
() => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`,
|
() => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return scriptsRunning;
|
return scriptsKilled > 0;
|
||||||
},
|
},
|
||||||
exit: function (): void {
|
exit: function (): void {
|
||||||
updateDynamicRam("exit", getRamCost(Player, "exit"));
|
updateDynamicRam("exit", getRamCost(Player, "exit"));
|
||||||
|
@ -295,6 +295,7 @@ export function NetscriptCorporation(
|
|||||||
if (office === 0) continue;
|
if (office === 0) continue;
|
||||||
cities.push(office.loc);
|
cities.push(office.loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: division.name,
|
name: division.name,
|
||||||
type: division.type,
|
type: division.type,
|
||||||
@ -309,6 +310,7 @@ export function NetscriptCorporation(
|
|||||||
upgrades: division.upgrades.slice(),
|
upgrades: division.upgrades.slice(),
|
||||||
cities: cities,
|
cities: cities,
|
||||||
products: division.products === undefined ? [] : Object.keys(division.products),
|
products: division.products === undefined ? [] : Object.keys(division.products),
|
||||||
|
makesProducts: division.makesProducts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,6 +361,7 @@ export function NetscriptCorporation(
|
|||||||
const corporation = getCorporation();
|
const corporation = getCorporation();
|
||||||
return {
|
return {
|
||||||
cost: material.bCost,
|
cost: material.bCost,
|
||||||
|
sCost: material.sCost,
|
||||||
name: material.name,
|
name: material.name,
|
||||||
qty: material.qty,
|
qty: material.qty,
|
||||||
qlt: material.qlt,
|
qlt: material.qlt,
|
||||||
|
@ -342,7 +342,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
|
|||||||
checkGangApiAccess("getBonusTime");
|
checkGangApiAccess("getBonusTime");
|
||||||
const gang = player.gang;
|
const gang = player.gang;
|
||||||
if (gang === null) throw new Error("Should not be called without 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,4 +1,4 @@
|
|||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||||
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
|
import { hasAugmentationPrereqs } from "../Faction/FactionHelpers";
|
||||||
import { CityName } from "../Locations/data/CityNames";
|
import { CityName } from "../Locations/data/CityNames";
|
||||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||||
@ -28,10 +28,10 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
|||||||
updateRam("getAugmentationGraftPrice");
|
updateRam("getAugmentationGraftPrice");
|
||||||
const augName = helper.string("getAugmentationGraftPrice", "augName", _augName);
|
const augName = helper.string("getAugmentationGraftPrice", "augName", _augName);
|
||||||
checkGraftingAPIAccess("getAugmentationGraftPrice");
|
checkGraftingAPIAccess("getAugmentationGraftPrice");
|
||||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||||
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftPrice", `Invalid aug: ${augName}`);
|
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftPrice", `Invalid aug: ${augName}`);
|
||||||
}
|
}
|
||||||
const graftableAug = new GraftableAugmentation(Augmentations[augName]);
|
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||||
return graftableAug.cost;
|
return graftableAug.cost;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -39,10 +39,10 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
|||||||
updateRam("getAugmentationGraftTime");
|
updateRam("getAugmentationGraftTime");
|
||||||
const augName = helper.string("getAugmentationGraftTime", "augName", _augName);
|
const augName = helper.string("getAugmentationGraftTime", "augName", _augName);
|
||||||
checkGraftingAPIAccess("getAugmentationGraftTime");
|
checkGraftingAPIAccess("getAugmentationGraftTime");
|
||||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||||
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftTime", `Invalid aug: ${augName}`);
|
throw helper.makeRuntimeErrorMsg("grafting.getAugmentationGraftTime", `Invalid aug: ${augName}`);
|
||||||
}
|
}
|
||||||
const graftableAug = new GraftableAugmentation(Augmentations[augName]);
|
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||||
return calculateGraftingTimeWithBonus(player, graftableAug);
|
return calculateGraftingTimeWithBonus(player, graftableAug);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
|||||||
"You must be in New Tokyo to begin grafting an Augmentation.",
|
"You must be in New Tokyo to begin grafting an Augmentation.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!getGraftingAvailableAugs(player).includes(augName) || !Augmentations.hasOwnProperty(augName)) {
|
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
|
||||||
workerScript.log("grafting.graftAugmentation", () => `Invalid aug: ${augName}`);
|
workerScript.log("grafting.graftAugmentation", () => `Invalid aug: ${augName}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
|||||||
workerScript.log("graftAugmentation", () => txt);
|
workerScript.log("graftAugmentation", () => txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const craftableAug = new GraftableAugmentation(Augmentations[augName]);
|
const craftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
|
||||||
if (player.money < craftableAug.cost) {
|
if (player.money < craftableAug.cost) {
|
||||||
workerScript.log("grafting.graftAugmentation", () => `You don't have enough money to craft ${augName}`);
|
workerScript.log("grafting.graftAugmentation", () => `You don't have enough money to craft ${augName}`);
|
||||||
return false;
|
return false;
|
||||||
|
@ -3,7 +3,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
|||||||
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
|
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
|
||||||
import { startWorkerScript } from "../NetscriptWorker";
|
import { startWorkerScript } from "../NetscriptWorker";
|
||||||
import { Augmentation } from "../Augmentation/Augmentation";
|
import { Augmentation } from "../Augmentation/Augmentation";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||||
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
||||||
@ -56,7 +56,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
|||||||
throw _ctx.helper.makeRuntimeErrorMsg(`Invalid augmentation: '${name}'`);
|
throw _ctx.helper.makeRuntimeErrorMsg(`Invalid augmentation: '${name}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Augmentations[name];
|
return StaticAugmentations[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFaction = function (_ctx: NetscriptContext, name: string): Faction {
|
const getFaction = function (_ctx: NetscriptContext, name: string): Faction {
|
||||||
@ -122,7 +122,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
|||||||
_ctx.helper.checkSingularityAccess();
|
_ctx.helper.checkSingularityAccess();
|
||||||
const augName = _ctx.helper.string("augName", _augName);
|
const augName = _ctx.helper.string("augName", _augName);
|
||||||
const aug = getAugmentation(_ctx, augName);
|
const aug = getAugmentation(_ctx, augName);
|
||||||
return [aug.baseRepRequirement, aug.baseCost];
|
return [aug.getCost(player).moneyCost, aug.getCost(player).repCost];
|
||||||
},
|
},
|
||||||
getAugmentationPrereq: (_ctx: NetscriptContext) =>
|
getAugmentationPrereq: (_ctx: NetscriptContext) =>
|
||||||
function (_augName: unknown): string[] {
|
function (_augName: unknown): string[] {
|
||||||
@ -136,14 +136,14 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
|||||||
_ctx.helper.checkSingularityAccess();
|
_ctx.helper.checkSingularityAccess();
|
||||||
const augName = _ctx.helper.string("augName", _augName);
|
const augName = _ctx.helper.string("augName", _augName);
|
||||||
const aug = getAugmentation(_ctx, augName);
|
const aug = getAugmentation(_ctx, augName);
|
||||||
return aug.baseCost;
|
return aug.getCost(player).moneyCost;
|
||||||
},
|
},
|
||||||
getAugmentationRepReq: (_ctx: NetscriptContext) =>
|
getAugmentationRepReq: (_ctx: NetscriptContext) =>
|
||||||
function (_augName: unknown): number {
|
function (_augName: unknown): number {
|
||||||
_ctx.helper.checkSingularityAccess();
|
_ctx.helper.checkSingularityAccess();
|
||||||
const augName = _ctx.helper.string("augName", _augName);
|
const augName = _ctx.helper.string("augName", _augName);
|
||||||
const aug = getAugmentation(_ctx, augName);
|
const aug = getAugmentation(_ctx, augName);
|
||||||
return aug.baseRepRequirement;
|
return aug.getCost(player).repCost;
|
||||||
},
|
},
|
||||||
getAugmentationStats: (_ctx: NetscriptContext) =>
|
getAugmentationStats: (_ctx: NetscriptContext) =>
|
||||||
function (_augName: unknown): AugmentationStats {
|
function (_augName: unknown): AugmentationStats {
|
||||||
@ -183,7 +183,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}'.`);
|
_ctx.log(() => `You do not have enough reputation with '${fac.name}'.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -947,6 +947,12 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
|||||||
}
|
}
|
||||||
return res;
|
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) =>
|
getCompanyRep: (_ctx: NetscriptContext) =>
|
||||||
function (_companyName: unknown): number {
|
function (_companyName: unknown): number {
|
||||||
_ctx.helper.checkSingularityAccess();
|
_ctx.helper.checkSingularityAccess();
|
||||||
@ -1079,7 +1085,6 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
|
|||||||
_ctx.log(() => `Invalid work type: '${type}`);
|
_ctx.log(() => `Invalid work type: '${type}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
getFactionRep: (_ctx: NetscriptContext) =>
|
getFactionRep: (_ctx: NetscriptContext) =>
|
||||||
function (_facName: unknown): number {
|
function (_facName: unknown): number {
|
||||||
|
@ -5,7 +5,7 @@ import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
|
|||||||
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
|
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
|
||||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||||
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
|
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
|
||||||
import { CityName } from "../Locations/data/CityNames";
|
import { CityName } from "../Locations/data/CityNames";
|
||||||
import { findCrime } from "../Crime/CrimeHelpers";
|
import { findCrime } from "../Crime/CrimeHelpers";
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
const aug = purchasableAugs[i];
|
const aug = purchasableAugs[i];
|
||||||
augs.push({
|
augs.push({
|
||||||
name: aug.name,
|
name: aug.name,
|
||||||
cost: aug.startingCost,
|
cost: aug.baseCost,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +303,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const aug = Augmentations[augName];
|
const aug = StaticAugmentations[augName];
|
||||||
if (!aug) {
|
if (!aug) {
|
||||||
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
|
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Invalid aug: ${augName}`);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export class GraftableAugmentation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get cost(): number {
|
get cost(): number {
|
||||||
return this.augmentation.startingCost * CONSTANTS.AugmentationGraftingCostMult;
|
return this.augmentation.baseCost * CONSTANTS.AugmentationGraftingCostMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
get time(): number {
|
get time(): number {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
|
||||||
import { GraftableAugmentation } from "./GraftableAugmentation";
|
import { GraftableAugmentation } from "./GraftableAugmentation";
|
||||||
import { IPlayer } from "../IPlayer";
|
import { IPlayer } from "../IPlayer";
|
||||||
|
|
||||||
export const getGraftingAvailableAugs = (player: IPlayer): string[] => {
|
export const getGraftingAvailableAugs = (player: IPlayer): string[] => {
|
||||||
const augs: string[] = [];
|
const augs: string[] = [];
|
||||||
|
|
||||||
for (const [augName, aug] of Object.entries(Augmentations)) {
|
for (const [augName, aug] of Object.entries(StaticAugmentations)) {
|
||||||
if (aug.isSpecial) continue;
|
if (aug.isSpecial) continue;
|
||||||
augs.push(augName);
|
augs.push(augName);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { Construction, CheckBox, CheckBoxOutlineBlank } from "@mui/icons-materia
|
|||||||
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
|
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Augmentation } from "../../../Augmentation/Augmentation";
|
import { Augmentation } from "../../../Augmentation/Augmentation";
|
||||||
import { Augmentations } from "../../../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations";
|
||||||
import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames";
|
||||||
import { CONSTANTS } from "../../../Constants";
|
import { CONSTANTS } from "../../../Constants";
|
||||||
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
|
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
|
||||||
@ -54,7 +54,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
|||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const router = use.Router();
|
const router = use.Router();
|
||||||
|
|
||||||
for (const aug of Object.values(Augmentations)) {
|
for (const aug of Object.values(StaticAugmentations)) {
|
||||||
const name = aug.name;
|
const name = aug.name;
|
||||||
const graftableAug = new GraftableAugmentation(aug);
|
const graftableAug = new GraftableAugmentation(aug);
|
||||||
GraftableAugmentations[name] = graftableAug;
|
GraftableAugmentations[name] = graftableAug;
|
||||||
@ -62,6 +62,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
|||||||
|
|
||||||
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs(player)[0]);
|
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs(player)[0]);
|
||||||
const [graftOpen, setGraftOpen] = useState(false);
|
const [graftOpen, setGraftOpen] = useState(false);
|
||||||
|
const selectedAugmentation = StaticAugmentations[selectedAug];
|
||||||
|
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
@ -148,22 +149,26 @@ export const GraftingRoot = (): React.ReactElement => {
|
|||||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{Augmentations[selectedAug].prereqs.length > 0 && (
|
{selectedAugmentation.prereqs.length > 0 && (
|
||||||
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
|
<AugPreReqsChecklist player={player} aug={selectedAugmentation} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
{(() => {
|
{(() => {
|
||||||
const aug = Augmentations[selectedAug];
|
const info =
|
||||||
|
typeof selectedAugmentation.info === "string" ? (
|
||||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
<span>{selectedAugmentation.info}</span>
|
||||||
|
) : (
|
||||||
|
selectedAugmentation.info
|
||||||
|
);
|
||||||
const tooltip = (
|
const tooltip = (
|
||||||
<>
|
<>
|
||||||
{info}
|
{info}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{aug.stats}
|
{selectedAugmentation.stats}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return tooltip;
|
return tooltip;
|
||||||
|
@ -248,7 +248,7 @@ export interface IPlayer {
|
|||||||
queryStatFromString(str: string): number;
|
queryStatFromString(str: string): number;
|
||||||
getIntelligenceBonus(weight: number): number;
|
getIntelligenceBonus(weight: number): number;
|
||||||
getCasinoWinnings(): number;
|
getCasinoWinnings(): number;
|
||||||
quitJob(company: string): void;
|
quitJob(company: string, sing?: boolean): void;
|
||||||
hasJob(): boolean;
|
hasJob(): boolean;
|
||||||
createHacknetServer(): HacknetServer;
|
createHacknetServer(): HacknetServer;
|
||||||
startCreateProgramWork(programName: string, time: number, reqLevel: number): void;
|
startCreateProgramWork(programName: string, time: number, reqLevel: number): void;
|
||||||
@ -291,6 +291,6 @@ export interface IPlayer {
|
|||||||
sourceFileLvl(n: number): number;
|
sourceFileLvl(n: number): number;
|
||||||
startGraftAugmentationWork(augmentationName: string, time: number): void;
|
startGraftAugmentationWork(augmentationName: string, time: number): void;
|
||||||
graftAugmentationWork(numCycles: number): boolean;
|
graftAugmentationWork(numCycles: number): boolean;
|
||||||
finishGraftAugmentationWork(cancelled: boolean): string;
|
finishGraftAugmentationWork(cancelled: boolean, singularity?: boolean): string;
|
||||||
applyEntropy(stacks?: number): void;
|
applyEntropy(stacks?: number): void;
|
||||||
}
|
}
|
||||||
|
@ -258,7 +258,7 @@ export class PlayerObject implements IPlayer {
|
|||||||
queryStatFromString: (str: string) => number;
|
queryStatFromString: (str: string) => number;
|
||||||
getIntelligenceBonus: (weight: number) => number;
|
getIntelligenceBonus: (weight: number) => number;
|
||||||
getCasinoWinnings: () => number;
|
getCasinoWinnings: () => number;
|
||||||
quitJob: (company: string) => void;
|
quitJob: (company: string, sing?: boolean) => void;
|
||||||
hasJob: () => boolean;
|
hasJob: () => boolean;
|
||||||
process: (router: IRouter, numCycles?: number) => void;
|
process: (router: IRouter, numCycles?: number) => void;
|
||||||
createHacknetServer: () => HacknetServer;
|
createHacknetServer: () => HacknetServer;
|
||||||
@ -302,7 +302,7 @@ export class PlayerObject implements IPlayer {
|
|||||||
sourceFileLvl: (n: number) => number;
|
sourceFileLvl: (n: number) => number;
|
||||||
startGraftAugmentationWork: (augmentationName: string, time: number) => void;
|
startGraftAugmentationWork: (augmentationName: string, time: number) => void;
|
||||||
graftAugmentationWork: (numCycles: number) => boolean;
|
graftAugmentationWork: (numCycles: number) => boolean;
|
||||||
finishGraftAugmentationWork: (cancelled: boolean) => string;
|
finishGraftAugmentationWork: (cancelled: boolean, singularity?: boolean) => string;
|
||||||
applyEntropy: (stacks?: number) => void;
|
applyEntropy: (stacks?: number) => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -463,12 +463,12 @@ export class PlayerObject implements IPlayer {
|
|||||||
this.bladeburner = null;
|
this.bladeburner = null;
|
||||||
this.bladeburner_max_stamina_mult = 1;
|
this.bladeburner_max_stamina_mult = 1;
|
||||||
this.bladeburner_stamina_gain_mult = 1;
|
this.bladeburner_stamina_gain_mult = 1;
|
||||||
this.bladeburner_analysis_mult = 1; //Field Analysis Onl;
|
this.bladeburner_analysis_mult = 1; //Field Analysis Only
|
||||||
this.bladeburner_success_chance_mult = 1;
|
this.bladeburner_success_chance_mult = 1;
|
||||||
|
|
||||||
// Sleeves & Re-sleeving
|
// Sleeves & Re-sleeving
|
||||||
this.sleeves = [];
|
this.sleeves = [];
|
||||||
this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenan;
|
this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenant
|
||||||
//bitnode
|
//bitnode
|
||||||
this.bitNodeN = 1;
|
this.bitNodeN = 1;
|
||||||
|
|
||||||
@ -483,8 +483,8 @@ export class PlayerObject implements IPlayer {
|
|||||||
this.playtimeSinceLastBitnode = 0;
|
this.playtimeSinceLastBitnode = 0;
|
||||||
|
|
||||||
// Keep track of where money comes from
|
// Keep track of where money comes from
|
||||||
this.moneySourceA = new MoneySourceTracker(); // Where money comes from since last-installed Augmentatio;
|
this.moneySourceA = new MoneySourceTracker(); // Where money comes from since last-installed Augmentation
|
||||||
this.moneySourceB = new MoneySourceTracker(); // Where money comes from for this entire BitNode ru;
|
this.moneySourceB = new MoneySourceTracker(); // Where money comes from for this entire BitNode run
|
||||||
// Production since last Augmentation installation
|
// Production since last Augmentation installation
|
||||||
this.scriptProdSinceLastAug = 0;
|
this.scriptProdSinceLastAug = 0;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { IPlayer } from "../IPlayer";
|
import { IPlayer } from "../IPlayer";
|
||||||
import { PlayerObject } from "./PlayerObject";
|
import { PlayerObject } from "./PlayerObject";
|
||||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
|
||||||
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
|
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
|
||||||
import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
|
import { PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
@ -1364,10 +1363,10 @@ export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean): string {
|
export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean, singularity = false): string {
|
||||||
const augName = this.graftAugmentationName;
|
const augName = this.graftAugmentationName;
|
||||||
if (cancelled === false) {
|
if (cancelled === false) {
|
||||||
applyAugmentation(Augmentations[augName]);
|
applyAugmentation({ name: augName, level: 1 });
|
||||||
|
|
||||||
if (!this.hasAugmentation(AugmentationNames.CongruityImplant)) {
|
if (!this.hasAugmentation(AugmentationNames.CongruityImplant)) {
|
||||||
this.entropy += 1;
|
this.entropy += 1;
|
||||||
@ -1378,7 +1377,7 @@ export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean):
|
|||||||
`You've finished grafting ${augName}.<br>The augmentation has been applied to your body` +
|
`You've finished grafting ${augName}.<br>The augmentation has been applied to your body` +
|
||||||
(this.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."),
|
(this.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."),
|
||||||
);
|
);
|
||||||
} else {
|
} else if (cancelled && singularity === false) {
|
||||||
dialogBoxCreate(`You cancelled the grafting of ${augName}.<br>Your money was not returned to you.`);
|
dialogBoxCreate(`You cancelled the grafting of ${augName}.<br>Your money was not returned to you.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1700,6 +1699,9 @@ export function singularityStopWork(this: IPlayer): string {
|
|||||||
case CONSTANTS.WorkTypeCrime:
|
case CONSTANTS.WorkTypeCrime:
|
||||||
res = this.finishCrime(true);
|
res = this.finishCrime(true);
|
||||||
break;
|
break;
|
||||||
|
case CONSTANTS.WorkTypeGraftAugmentation:
|
||||||
|
res = this.finishGraftAugmentationWork(true, true);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(`Unrecognized work type (${this.workType})`);
|
console.error(`Unrecognized work type (${this.workType})`);
|
||||||
return "";
|
return "";
|
||||||
@ -1748,14 +1750,6 @@ export function hospitalize(this: IPlayer): number {
|
|||||||
//The 'sing' argument designates whether or not this is being called from
|
//The 'sing' argument designates whether or not this is being called from
|
||||||
//the applyToCompany() Netscript Singularity function
|
//the applyToCompany() Netscript Singularity function
|
||||||
export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing = false): boolean {
|
export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing = false): boolean {
|
||||||
// Get current company and job
|
|
||||||
let currCompany = null;
|
|
||||||
if (this.companyName !== "") {
|
|
||||||
currCompany = Companies[this.companyName];
|
|
||||||
}
|
|
||||||
const currPositionName = this.jobs[this.companyName];
|
|
||||||
|
|
||||||
// Get company that's being applied to
|
|
||||||
const company = Companies[this.location]; //Company being applied to
|
const company = Companies[this.location]; //Company being applied to
|
||||||
if (!(company instanceof Company)) {
|
if (!(company instanceof Company)) {
|
||||||
console.error(`Could not find company that matches the location: ${this.location}. Player.applyToCompany() failed`);
|
console.error(`Could not find company that matches the location: ${this.location}. Player.applyToCompany() failed`);
|
||||||
@ -1765,66 +1759,44 @@ export function applyForJob(this: IPlayer, entryPosType: CompanyPosition, sing =
|
|||||||
let pos = entryPosType;
|
let pos = entryPosType;
|
||||||
|
|
||||||
if (!this.isQualified(company, pos)) {
|
if (!this.isQualified(company, pos)) {
|
||||||
const reqText = getJobRequirementText(company, pos);
|
|
||||||
if (!sing) {
|
if (!sing) {
|
||||||
dialogBoxCreate("Unfortunately, you do not qualify for this position<br>" + reqText);
|
dialogBoxCreate("Unfortunately, you do not qualify for this position<br>" + getJobRequirementText(company, pos));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this company has the position
|
|
||||||
if (!company.hasPosition(pos)) {
|
if (!company.hasPosition(pos)) {
|
||||||
|
console.error(`Company ${company.name} does not have position ${pos}. Player.applyToCompany() failed`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const newPos = getNextCompanyPositionHelper(pos);
|
const nextPos = getNextCompanyPositionHelper(pos);
|
||||||
if (newPos == null) {
|
if (nextPos == null) break;
|
||||||
break;
|
if (company.hasPosition(nextPos) && this.isQualified(company, nextPos)) {
|
||||||
}
|
pos = nextPos;
|
||||||
|
} else break;
|
||||||
//Check if this company has this position
|
|
||||||
if (company.hasPosition(newPos)) {
|
|
||||||
if (!this.isQualified(company, newPos)) {
|
|
||||||
//If player not qualified for next job, break loop so player will be given current job
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos = newPos;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if the determined job is the same as the player's current job
|
//Check if player already has the assigned job
|
||||||
if (currCompany != null) {
|
if (this.jobs[company.name] === pos.name) {
|
||||||
if (currCompany.name == company.name && pos.name == currPositionName) {
|
if (!sing) {
|
||||||
const nextPos = getNextCompanyPositionHelper(pos);
|
const nextPos = getNextCompanyPositionHelper(pos);
|
||||||
if (nextPos == null) {
|
if (nextPos == null || !company.hasPosition(nextPos)) {
|
||||||
if (!sing) {
|
dialogBoxCreate("You are already at the highest position for your field! No promotion available");
|
||||||
dialogBoxCreate("You are already at the highest position for your field! No promotion available");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else if (company.hasPosition(nextPos)) {
|
|
||||||
if (!sing) {
|
|
||||||
const reqText = getJobRequirementText(company, nextPos);
|
|
||||||
dialogBoxCreate("Unfortunately, you do not qualify for a promotion<br>" + reqText);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
if (!sing) {
|
const reqText = getJobRequirementText(company, nextPos);
|
||||||
dialogBoxCreate("You are already at the highest position for your field! No promotion available");
|
dialogBoxCreate("Unfortunately, you do not qualify for a promotion<br>" + reqText);
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.jobs[company.name] = pos.name;
|
this.jobs[company.name] = pos.name;
|
||||||
if (!this.focus && this.isWorking && this.companyName !== this.location) this.resetWorkStatus();
|
if (!this.isWorking || !this.workType.includes("Working for Company")) this.companyName = company.name;
|
||||||
this.companyName = this.location;
|
|
||||||
|
|
||||||
if (!sing) {
|
if (!sing) {
|
||||||
dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!");
|
dialogBoxCreate("Congratulations! You were offered a new job at " + company.name + " as a " + pos.name + "!");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1869,7 +1841,7 @@ export function getNextCompanyPosition(
|
|||||||
return entryPosType;
|
return entryPosType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function quitJob(this: IPlayer, company: string): void {
|
export function quitJob(this: IPlayer, company: string, _sing = false): void {
|
||||||
if (this.isWorking == true && this.workType.includes("Working for Company") && this.companyName == company) {
|
if (this.isWorking == true && this.workType.includes("Working for Company") && this.companyName == company) {
|
||||||
this.finishWork(true);
|
this.finishWork(true);
|
||||||
}
|
}
|
||||||
|
@ -686,7 +686,7 @@ export class Sleeve extends Person {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean {
|
tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean {
|
||||||
if (!p.canAfford(aug.startingCost)) {
|
if (!p.canAfford(aug.baseCost)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,7 +695,7 @@ export class Sleeve extends Person {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.loseMoney(aug.startingCost, "sleeves");
|
p.loseMoney(aug.baseCost, "sleeves");
|
||||||
this.installAugmentation(aug);
|
this.installAugmentation(aug);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { Sleeve } from "./Sleeve";
|
|||||||
import { IPlayer } from "../IPlayer";
|
import { IPlayer } from "../IPlayer";
|
||||||
|
|
||||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
|
||||||
import { Faction } from "../../Faction/Faction";
|
import { Faction } from "../../Faction/Faction";
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
|
|
||||||
@ -64,13 +64,13 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
|
|||||||
if (p.inGang()) {
|
if (p.inGang()) {
|
||||||
const fac = p.getGangFaction();
|
const fac = p.getGangFaction();
|
||||||
|
|
||||||
for (const augName of Object.keys(Augmentations)) {
|
for (const augName of Object.keys(StaticAugmentations)) {
|
||||||
const aug = Augmentations[augName];
|
const aug = StaticAugmentations[augName];
|
||||||
if (!isAvailableForSleeve(aug)) {
|
if (!isAvailableForSleeve(aug)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fac.playerReputation > aug.baseRepRequirement) {
|
if (fac.playerReputation > aug.getCost(p).repCost) {
|
||||||
availableAugs.push(aug);
|
availableAugs.push(aug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,12 +89,12 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const augName of fac.augmentations) {
|
for (const augName of fac.augmentations) {
|
||||||
const aug: Augmentation = Augmentations[augName];
|
const aug: Augmentation = StaticAugmentations[augName];
|
||||||
if (!isAvailableForSleeve(aug)) {
|
if (!isAvailableForSleeve(aug)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fac.playerReputation > aug.baseRepRequirement) {
|
if (fac.playerReputation > aug.getCost(p).repCost) {
|
||||||
availableAugs.push(aug);
|
availableAugs.push(aug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
|
|||||||
ownedAugNames={ownedAugNames}
|
ownedAugNames={ownedAugNames}
|
||||||
player={player}
|
player={player}
|
||||||
canPurchase={(player, aug) => {
|
canPurchase={(player, aug) => {
|
||||||
return player.money > aug.startingCost;
|
return player.money > aug.baseCost;
|
||||||
}}
|
}}
|
||||||
purchaseAugmentation={(player, aug, _showModal) => {
|
purchaseAugmentation={(player, aug, _showModal) => {
|
||||||
props.sleeve.tryBuyAugmentation(player, aug);
|
props.sleeve.tryBuyAugmentation(player, aug);
|
||||||
|
@ -77,7 +77,9 @@ function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return factions.filter((faction) => {
|
return factions.filter((faction) => {
|
||||||
const facInfo = Factions[faction].getInfo();
|
const factionObj = Factions[faction];
|
||||||
|
if (!factionObj) return false;
|
||||||
|
const facInfo = factionObj.getInfo();
|
||||||
return facInfo.offerHackingWork || facInfo.offerFieldWork || facInfo.offerSecurityWork;
|
return facInfo.offerHackingWork || facInfo.offerFieldWork || facInfo.offerSecurityWork;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FactionNames } from "./Faction/data/FactionNames";
|
import { FactionNames } from "./Faction/data/FactionNames";
|
||||||
import { CityName } from "./Locations/data/CityNames";
|
import { CityName } from "./Locations/data/CityNames";
|
||||||
import { Augmentations } from "./Augmentation/Augmentations";
|
import { StaticAugmentations } from "./Augmentation/StaticAugmentations";
|
||||||
import { augmentationExists, initAugmentations } from "./Augmentation/AugmentationHelpers";
|
import { augmentationExists, initAugmentations } from "./Augmentation/AugmentationHelpers";
|
||||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||||
import { initBitNodeMultipliers } from "./BitNode/BitNode";
|
import { initBitNodeMultipliers } from "./BitNode/BitNode";
|
||||||
@ -225,9 +225,9 @@ export function prestigeSourceFile(flume: boolean): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete all Augmentations
|
// Delete all Augmentations
|
||||||
for (const name of Object.keys(Augmentations)) {
|
for (const name of Object.keys(StaticAugmentations)) {
|
||||||
if (Augmentations.hasOwnProperty(name)) {
|
if (StaticAugmentations.hasOwnProperty(name)) {
|
||||||
delete Augmentations[name];
|
delete StaticAugmentations[name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
42
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -1800,6 +1800,18 @@ export interface Singularity {
|
|||||||
*/
|
*/
|
||||||
workForCompany(companyName?: string, focus?: boolean): boolean;
|
workForCompany(companyName?: string, focus?: boolean): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quit jobs by company.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 3 GB * 16/4/1
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This function will finish work with the company provided and quit any jobs.
|
||||||
|
*
|
||||||
|
* @param companyName - Name of the company.
|
||||||
|
*/
|
||||||
|
quitJob(companyName?: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply for a job at a company.
|
* Apply for a job at a company.
|
||||||
* @remarks
|
* @remarks
|
||||||
@ -2340,13 +2352,13 @@ export interface Singularity {
|
|||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* // NS1
|
* // NS1
|
||||||
* getDarkwebProgramsAvailable();
|
* getDarkwebPrograms();
|
||||||
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
||||||
* ```
|
* ```
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* // NS2
|
* // NS2
|
||||||
* ns.getDarkwebProgramsAvailable();
|
* ns.getDarkwebPrograms();
|
||||||
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
||||||
* ```
|
* ```
|
||||||
* @returns - a list of programs available for purchase on the dark web, or [] if Tor has not
|
* @returns - a list of programs available for purchase on the dark web, or [] if Tor has not
|
||||||
@ -5311,9 +5323,10 @@ export interface NS {
|
|||||||
* If no host is defined, it will kill all scripts, where the script is running.
|
* If no host is defined, it will kill all scripts, where the script is running.
|
||||||
*
|
*
|
||||||
* @param host - IP or hostname of the server on which to kill all scripts.
|
* @param host - IP or hostname of the server on which to kill all scripts.
|
||||||
|
* @param safetyguard - Skips the script that calls this function
|
||||||
* @returns True if any scripts were killed, and false otherwise.
|
* @returns True if any scripts were killed, and false otherwise.
|
||||||
*/
|
*/
|
||||||
killall(host?: string): boolean;
|
killall(host?: string, safetyguard?: boolean): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminates the current script immediately.
|
* Terminates the current script immediately.
|
||||||
@ -6126,7 +6139,7 @@ export interface NS {
|
|||||||
* Returns 0 if the script does not exist.
|
* Returns 0 if the script does not exist.
|
||||||
*
|
*
|
||||||
* @param script - Filename of script. This is case-sensitive.
|
* @param script - Filename of script. This is case-sensitive.
|
||||||
* @param host - Host of target server the script is located on. This is optional, If it is not specified then the function will se the current server as the target server.
|
* @param host - Host of target server the script is located on. This is optional, if it is not specified then the function will use the current server as the target server.
|
||||||
* @returns Amount of RAM (in GB) required to run the specified script on the target server, and 0 if the script does not exist.
|
* @returns Amount of RAM (in GB) required to run the specified script on the target server, and 0 if the script does not exist.
|
||||||
*/
|
*/
|
||||||
getScriptRam(script: string, host?: string): number;
|
getScriptRam(script: string, host?: string): number;
|
||||||
@ -7044,22 +7057,27 @@ interface CorporationInfo {
|
|||||||
interface Employee {
|
interface Employee {
|
||||||
/** Name of the employee */
|
/** Name of the employee */
|
||||||
name: string;
|
name: string;
|
||||||
/** Morale */
|
/** Morale of the employee */
|
||||||
mor: number;
|
mor: number;
|
||||||
/** Happiness */
|
/** Happiness of the employee */
|
||||||
hap: number;
|
hap: number;
|
||||||
/** Energy */
|
/** Energy of the employee */
|
||||||
ene: number;
|
ene: number;
|
||||||
|
/** Intelligence of the employee */
|
||||||
int: number;
|
int: number;
|
||||||
|
/** Charisma of the employee */
|
||||||
cha: number;
|
cha: number;
|
||||||
|
/** Experience of the employee */
|
||||||
exp: number;
|
exp: number;
|
||||||
|
/** Creativity of the employee */
|
||||||
cre: number;
|
cre: number;
|
||||||
|
/** Efficiency of the employee */
|
||||||
eff: number;
|
eff: number;
|
||||||
/** Salary */
|
/** Salary of the employee */
|
||||||
sal: number;
|
sal: number;
|
||||||
/** City */
|
/** Current Location (city) */
|
||||||
loc: string;
|
loc: string;
|
||||||
/** Current job */
|
/** Current job position */
|
||||||
pos: string;
|
pos: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7111,6 +7129,8 @@ interface Material {
|
|||||||
sell: number;
|
sell: number;
|
||||||
/** cost to buy material */
|
/** cost to buy material */
|
||||||
cost: number;
|
cost: number;
|
||||||
|
/** Sell cost, can be "MP+5" */
|
||||||
|
sCost: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7202,6 +7222,8 @@ interface Division {
|
|||||||
cities: string[];
|
cities: string[];
|
||||||
/** Products developed by this division */
|
/** Products developed by this division */
|
||||||
products: string[];
|
products: string[];
|
||||||
|
/** Whether the industry this division is in is capable of making products */
|
||||||
|
makesProducts: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -544,11 +544,14 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
// this is duplicate code with saving later.
|
// this is duplicate code with saving later.
|
||||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||||
//Make sure filename + code properly follow tutorial
|
//Make sure filename + code properly follow tutorial
|
||||||
if (currentScript.fileName !== "n00dles.script") {
|
if (currentScript.fileName !== "n00dles.script" && currentScript.fileName !== "n00dles.js") {
|
||||||
dialogBoxCreate("Leave the script name as 'n00dles.script'!");
|
dialogBoxCreate("Don't change the script name for now.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentScript.code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
const cleanCode = currentScript.code.replace(/\s/g, "");
|
||||||
|
const ns1 = "while(true){hack('n00dles');}";
|
||||||
|
const ns2 = `exportasyncfunctionmain(ns){while(true){awaitns.hack('n00dles');}}`;
|
||||||
|
if (cleanCode.indexOf(ns1) == -1 && cleanCode.indexOf(ns2) == -1) {
|
||||||
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -692,7 +695,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
const serverScriptIndex = server.scripts.findIndex((script) => script.filename === closingScript.fileName);
|
const serverScriptIndex = server.scripts.findIndex((script) => script.filename === closingScript.fileName);
|
||||||
if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
|
if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
|
||||||
PromptEvent.emit({
|
PromptEvent.emit({
|
||||||
txt: "Do you want to save changes to " + closingScript.fileName + "?",
|
txt: `Do you want to save changes to ${closingScript.fileName} on ${closingScript.hostname}?`,
|
||||||
resolve: (result: boolean | string) => {
|
resolve: (result: boolean | string) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
// Save changes
|
// Save changes
|
||||||
@ -855,22 +858,31 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{filteredOpenScripts.map(({ fileName, hostname }, index) => {
|
{filteredOpenScripts.map(({ fileName, hostname }, index) => {
|
||||||
|
const editingCurrentScript =
|
||||||
|
currentScript?.fileName === filteredOpenScripts[index].fileName &&
|
||||||
|
currentScript?.hostname === filteredOpenScripts[index].hostname;
|
||||||
|
const externalScript = hostname !== "home";
|
||||||
|
const colorProps = editingCurrentScript
|
||||||
|
? {
|
||||||
|
background: Settings.theme.button,
|
||||||
|
borderColor: Settings.theme.button,
|
||||||
|
color: Settings.theme.primary,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
background: Settings.theme.backgroundsecondary,
|
||||||
|
borderColor: Settings.theme.backgroundsecondary,
|
||||||
|
color: Settings.theme.secondary,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (externalScript) {
|
||||||
|
colorProps.color = Settings.theme.info;
|
||||||
|
}
|
||||||
const iconButtonStyle = {
|
const iconButtonStyle = {
|
||||||
maxWidth: `${tabIconWidth}px`,
|
maxWidth: `${tabIconWidth}px`,
|
||||||
minWidth: `${tabIconWidth}px`,
|
minWidth: `${tabIconWidth}px`,
|
||||||
minHeight: "38.5px",
|
minHeight: "38.5px",
|
||||||
maxHeight: "38.5px",
|
maxHeight: "38.5px",
|
||||||
...(currentScript?.fileName === filteredOpenScripts[index].fileName
|
...colorProps,
|
||||||
? {
|
|
||||||
background: Settings.theme.button,
|
|
||||||
borderColor: Settings.theme.button,
|
|
||||||
color: Settings.theme.primary,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
background: Settings.theme.backgroundsecondary,
|
|
||||||
borderColor: Settings.theme.backgroundsecondary,
|
|
||||||
color: Settings.theme.secondary,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`;
|
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`;
|
||||||
@ -905,17 +917,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
maxWidth: `${tabTextWidth}px`,
|
maxWidth: `${tabTextWidth}px`,
|
||||||
minHeight: "38.5px",
|
minHeight: "38.5px",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
...(currentScript?.fileName === filteredOpenScripts[index].fileName
|
...colorProps,
|
||||||
? {
|
|
||||||
background: Settings.theme.button,
|
|
||||||
borderColor: Settings.theme.button,
|
|
||||||
color: Settings.theme.primary,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
background: Settings.theme.backgroundsecondary,
|
|
||||||
borderColor: Settings.theme.backgroundsecondary,
|
|
||||||
color: Settings.theme.secondary,
|
|
||||||
}),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ overflow: "hidden", direction: "rtl", textOverflow: "ellipsis" }}>
|
<span style={{ overflow: "hidden", direction: "rtl", textOverflow: "ellipsis" }}>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { KEY } from "../../utils/helpers/keyCodes";
|
import { KEYCODE } from "../../utils/helpers/keyCodes";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
||||||
import createStyles from "@mui/styles/createStyles";
|
import createStyles from "@mui/styles/createStyles";
|
||||||
@ -275,57 +275,53 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
||||||
if (Settings.DisableHotkeys) return;
|
if (Settings.DisableHotkeys) return;
|
||||||
if ((props.player.isWorking && props.player.focus) || props.router.page() === Page.BitVerse) return;
|
if ((props.player.isWorking && props.player.focus) || props.router.page() === Page.BitVerse) return;
|
||||||
if (event.key === KEY.T && event.altKey) {
|
if (event.code === KEYCODE.T && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTerminal();
|
clickTerminal();
|
||||||
} else if (event.key === KEY.C && event.altKey) {
|
} else if (event.code === KEYCODE.C && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickStats();
|
clickStats();
|
||||||
} else if (event.key === KEY.E && event.altKey) {
|
} else if (event.code === KEYCODE.E && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickScriptEditor();
|
clickScriptEditor();
|
||||||
} else if (event.key === KEY.S && event.altKey) {
|
} else if (event.code === KEYCODE.S && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickActiveScripts();
|
clickActiveScripts();
|
||||||
} else if (event.key === KEY.H && event.altKey) {
|
} else if (event.code === KEYCODE.H && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickHacknet();
|
clickHacknet();
|
||||||
} else if (event.key === KEY.W && event.altKey) {
|
} else if (event.code === KEYCODE.W && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickCity();
|
clickCity();
|
||||||
} else if (event.key === KEY.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) {
|
} else if (event.code === KEYCODE.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) {
|
||||||
// ctrl/cmd + alt + j is shortcut to open Chrome dev tools
|
// ctrl/cmd + alt + j is shortcut to open Chrome dev tools
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickJob();
|
clickJob();
|
||||||
} else if (event.key === KEY.R && event.altKey) {
|
} else if (event.code === KEYCODE.R && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTravel();
|
clickTravel();
|
||||||
} else if (event.key === KEY.P && event.altKey) {
|
} else if (event.code === KEYCODE.P && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickCreateProgram();
|
clickCreateProgram();
|
||||||
} else if (event.key === KEY.F && event.altKey) {
|
} else if (event.code === KEYCODE.F && event.altKey) {
|
||||||
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
|
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickFactions();
|
clickFactions();
|
||||||
} else if (event.key === KEY.A && event.altKey) {
|
} else if (event.code === KEYCODE.A && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickAugmentations();
|
clickAugmentations();
|
||||||
} else if (event.key === KEY.U && event.altKey) {
|
} else if (event.code === KEYCODE.U && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTutorial();
|
clickTutorial();
|
||||||
} else if (event.key === KEY.B && event.altKey && props.player.bladeburner) {
|
} else if (event.code === KEYCODE.B && event.altKey && props.player.bladeburner) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickBladeburner();
|
clickBladeburner();
|
||||||
} else if (event.key === KEY.G && event.altKey && props.player.gang) {
|
} else if (event.code === KEYCODE.G && event.altKey && props.player.gang) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickGang();
|
clickGang();
|
||||||
}
|
}
|
||||||
// if (event.key === KEY.O && event.altKey) {
|
|
||||||
// event.preventDefault();
|
|
||||||
// gameOptionsBoxOpen();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", handleShortcuts);
|
document.addEventListener("keydown", handleShortcuts);
|
||||||
|
@ -718,7 +718,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalCreateScript:
|
case iTutorialSteps.TerminalCreateScript:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "nano" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
@ -734,7 +738,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.TerminalRunScript:
|
case iTutorialSteps.TerminalRunScript:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "run" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
@ -742,7 +750,11 @@ export class Terminal implements ITerminal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case iTutorialSteps.ActiveScriptsToTerminal:
|
case iTutorialSteps.ActiveScriptsToTerminal:
|
||||||
if (commandArray.length == 2 && commandArray[0] == "tail" && commandArray[1] == "n00dles.script") {
|
if (
|
||||||
|
commandArray.length == 2 &&
|
||||||
|
commandArray[0] == "tail" &&
|
||||||
|
(commandArray[1] == "n00dles.script" || commandArray[1] == "n00dles.js")
|
||||||
|
) {
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
} else {
|
} else {
|
||||||
this.error("Bad command. Please follow the tutorial");
|
this.error("Bad command. Please follow the tutorial");
|
||||||
|
@ -7,7 +7,7 @@ import Paper from "@mui/material/Paper";
|
|||||||
import Popper from "@mui/material/Popper";
|
import Popper from "@mui/material/Popper";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
import { KEY } from "../../utils/helpers/keyCodes";
|
import { KEY, KEYCODE } from "../../utils/helpers/keyCodes";
|
||||||
import { ITerminal } from "../ITerminal";
|
import { ITerminal } from "../ITerminal";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
@ -318,57 +318,57 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
|
|
||||||
// Extra Bash Emulation Hotkeys, must be enabled through options
|
// Extra Bash Emulation Hotkeys, must be enabled through options
|
||||||
if (Settings.EnableBashHotkeys) {
|
if (Settings.EnableBashHotkeys) {
|
||||||
if (event.key === KEY.A && event.ctrlKey) {
|
if (event.code === KEYCODE.A && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("home");
|
moveTextCursor("home");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.E && event.ctrlKey) {
|
if (event.code === KEYCODE.E && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("end");
|
moveTextCursor("end");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.B && event.ctrlKey) {
|
if (event.code === KEYCODE.B && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("prevchar");
|
moveTextCursor("prevchar");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.B && event.altKey) {
|
if (event.code === KEYCODE.B && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("prevword");
|
moveTextCursor("prevword");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.F && event.ctrlKey) {
|
if (event.code === KEYCODE.F && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("nextchar");
|
moveTextCursor("nextchar");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.F && event.altKey) {
|
if (event.code === KEYCODE.F && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("nextword");
|
moveTextCursor("nextword");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.key === KEY.H || event.key === KEY.D) && event.ctrlKey) {
|
if ((event.code === KEYCODE.H || event.code === KEYCODE.D) && event.ctrlKey) {
|
||||||
modifyInput("backspace");
|
modifyInput("backspace");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.W && event.ctrlKey) {
|
if (event.code === KEYCODE.W && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("deletewordbefore");
|
modifyInput("deletewordbefore");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.D && event.altKey) {
|
if (event.code === KEYCODE.D && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("deletewordafter");
|
modifyInput("deletewordafter");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.U && event.ctrlKey) {
|
if (event.code === KEYCODE.U && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("clearbefore");
|
modifyInput("clearbefore");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === KEY.K && event.ctrlKey) {
|
if (event.code === KEYCODE.K && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("clearafter");
|
modifyInput("clearafter");
|
||||||
}
|
}
|
||||||
|
@ -134,20 +134,191 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function lineClass(s: string): string {
|
function lineClass(s: string): string {
|
||||||
if (s === "error") {
|
const lineClassMap: Record<string, string> = {
|
||||||
return classes.error;
|
error: classes.error,
|
||||||
}
|
success: classes.success,
|
||||||
if (s === "success") {
|
info: classes.info,
|
||||||
return classes.success;
|
warn: classes.warning,
|
||||||
}
|
};
|
||||||
if (s === "info") {
|
return lineClassMap[s] || classes.primary;
|
||||||
return classes.info;
|
}
|
||||||
}
|
|
||||||
if (s === "warn") {
|
function ansiCodeStyle(code: string | null): Record<string, any> {
|
||||||
return classes.warning;
|
// The ANSI colors actually have the dark color set as default and require extra work to get
|
||||||
|
// bright colors. But these are rarely used or, if they are, are often re-mapped by the
|
||||||
|
// terminal emulator to brighter colors. So for foreground colors we use the bright color set
|
||||||
|
// and for background colors we use the dark color set. Of course, all colors are available
|
||||||
|
// via the longer ESC[n8;5;c] sequence (n={3,4}, c=color). Ideally, these 8-bit maps could
|
||||||
|
// be managed in the user preferences/theme.
|
||||||
|
const COLOR_MAP_BRIGHT: Record<string | number, string> = {
|
||||||
|
0: "#404040",
|
||||||
|
1: "#ff0000",
|
||||||
|
2: "#00ff00",
|
||||||
|
3: "#ffff00",
|
||||||
|
4: "#0000ff",
|
||||||
|
5: "#ff00ff",
|
||||||
|
6: "#00ffff",
|
||||||
|
7: "#ffffff",
|
||||||
|
};
|
||||||
|
const COLOR_MAP_DARK: Record<string | number, string> = {
|
||||||
|
0: "#000000",
|
||||||
|
1: "#800000",
|
||||||
|
2: "#008000",
|
||||||
|
3: "#808000",
|
||||||
|
4: "#000080",
|
||||||
|
5: "#800080",
|
||||||
|
6: "#008080",
|
||||||
|
7: "#c0c0c0",
|
||||||
|
};
|
||||||
|
|
||||||
|
const ansi2rgb = (code: number): string => {
|
||||||
|
/* eslint-disable yoda */
|
||||||
|
if (0 <= code && code < 8) {
|
||||||
|
// x8 RGB
|
||||||
|
return COLOR_MAP_BRIGHT[code];
|
||||||
|
}
|
||||||
|
if (8 <= code && code < 16) {
|
||||||
|
// x8 RGB - "High Intensity" (but here, actually the dark set)
|
||||||
|
return COLOR_MAP_DARK[code];
|
||||||
|
}
|
||||||
|
if (16 <= code && code < 232) {
|
||||||
|
// x216 RGB
|
||||||
|
const base = code - 16;
|
||||||
|
const ir = Math.floor(base / 36);
|
||||||
|
const ig = Math.floor((base % 36) / 6);
|
||||||
|
const ib = Math.floor((base % 6) / 1);
|
||||||
|
const r = ir <= 0 ? 0 : 55 + ir * 40;
|
||||||
|
const g = ig <= 0 ? 0 : 55 + ig * 40;
|
||||||
|
const b = ib <= 0 ? 0 : 55 + ib * 40;
|
||||||
|
return `rgb(${r}, ${g}, ${b})`;
|
||||||
|
}
|
||||||
|
if (232 <= code && code < 256) {
|
||||||
|
// x32 greyscale
|
||||||
|
const base = code - 232;
|
||||||
|
const grey = base * 10 + 8;
|
||||||
|
return `rgb(${grey}, ${grey}, ${grey})`;
|
||||||
|
}
|
||||||
|
// shouldn't get here (under normal circumstances), but just in case
|
||||||
|
return "initial";
|
||||||
|
};
|
||||||
|
|
||||||
|
type styleKey = "fontWeight" | "textDecoration" | "color" | "backgroundColor" | "display";
|
||||||
|
const style: {
|
||||||
|
fontWeight?: string;
|
||||||
|
textDecoration?: string;
|
||||||
|
color?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
display?: string;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
if (code === null || code === "0") {
|
||||||
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
return classes.primary;
|
const codeParts = code
|
||||||
|
.split(";")
|
||||||
|
.map((p) => parseInt(p))
|
||||||
|
.filter(
|
||||||
|
(p, i, arr) =>
|
||||||
|
// If the sequence is 38;5 (x256 foreground color) or 48;5 (x256 background color),
|
||||||
|
// filter out the 5 so the next codePart after {38,48} is the color code.
|
||||||
|
p != 5 || i == 0 || (arr[i - 1] != 38 && arr[i - 1] != 48),
|
||||||
|
);
|
||||||
|
|
||||||
|
let nextStyleKey: styleKey | null = null;
|
||||||
|
codeParts.forEach((codePart) => {
|
||||||
|
/* eslint-disable yoda */
|
||||||
|
if (nextStyleKey !== null) {
|
||||||
|
style[nextStyleKey] = ansi2rgb(codePart);
|
||||||
|
nextStyleKey = null;
|
||||||
|
}
|
||||||
|
// Decorations
|
||||||
|
else if (codePart == 1) {
|
||||||
|
style.fontWeight = "bold";
|
||||||
|
} else if (codePart == 4) {
|
||||||
|
style.textDecoration = "underline";
|
||||||
|
}
|
||||||
|
// Forground Color (x8)
|
||||||
|
else if (30 <= codePart && codePart < 38) {
|
||||||
|
if (COLOR_MAP_BRIGHT[codePart % 10]) {
|
||||||
|
style.color = COLOR_MAP_BRIGHT[codePart % 10];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Background Color (x8)
|
||||||
|
else if (40 <= codePart && codePart < 48) {
|
||||||
|
if (COLOR_MAP_DARK[codePart % 10]) {
|
||||||
|
style.backgroundColor = COLOR_MAP_DARK[codePart % 10];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Forground Color (x256)
|
||||||
|
else if (codePart == 38) {
|
||||||
|
nextStyleKey = "color";
|
||||||
|
}
|
||||||
|
// Background Color (x256)
|
||||||
|
else if (codePart == 48) {
|
||||||
|
nextStyleKey = "backgroundColor";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// If a background color is set, render this as an inline block element (instead of inline)
|
||||||
|
// so that the background between lines (at least those that don't wrap) is uninterrupted.
|
||||||
|
if (style.backgroundColor !== undefined) {
|
||||||
|
style.display = "inline-block";
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOutputText(item: Output): React.ReactElement {
|
||||||
|
const parts = [];
|
||||||
|
// Some things, oddly, can cause item.text to not be a string (e.g. expr from the CLI), which
|
||||||
|
// causes .matchAll to throw. Ensure it's a string immediately
|
||||||
|
if (typeof item.text === "string") {
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const ANSI_ESCAPE = new RegExp("\u{001b}\\[(?<code>.*?)m", "ug");
|
||||||
|
// Build a look-alike regex match to place at the front of the matches list
|
||||||
|
const INITIAL = {
|
||||||
|
0: "",
|
||||||
|
index: 0,
|
||||||
|
groups: { code: null },
|
||||||
|
};
|
||||||
|
const matches = [INITIAL, ...item.text.matchAll(ANSI_ESCAPE), null];
|
||||||
|
if (matches.length > 2) {
|
||||||
|
matches.slice(0, -1).forEach((m, i) => {
|
||||||
|
const n = matches[i + 1];
|
||||||
|
if (!m || m.index === undefined || m.groups === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const startIndex = m.index + m[0].length;
|
||||||
|
const stopIndex = n ? n.index : item.text.length;
|
||||||
|
const partText = item.text.slice(startIndex, stopIndex);
|
||||||
|
if (startIndex !== stopIndex) {
|
||||||
|
// Don't generate "empty" spans
|
||||||
|
parts.push({ code: m.groups.code, text: partText });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parts.length === 0) {
|
||||||
|
// For example, if the string was empty or there were no escape sequence matches
|
||||||
|
parts.push({ code: null, text: item.text });
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Typography classes={{ root: lineClass(item.color) }} paragraph={false}>
|
||||||
|
{parts.map((part, index) => {
|
||||||
|
const spanStyle = ansiCodeStyle(part.code);
|
||||||
|
if (!_.isEmpty(spanStyle)) {
|
||||||
|
// Only wrap parts with spans if the calculated spanStyle is non-empty...
|
||||||
|
return (
|
||||||
|
<Typography key={index} paragraph={false} component="span" sx={spanStyle}>
|
||||||
|
{part.text}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// ... otherwise yield the unmodified, unwrapped part.
|
||||||
|
return part.text;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@ -160,7 +331,7 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
|
|||||||
return (
|
return (
|
||||||
<ListItem key={i} classes={{ root: classes.nopadding }}>
|
<ListItem key={i} classes={{ root: classes.nopadding }}>
|
||||||
<Typography classes={{ root: lineClass(item.color) }} paragraph={false}>
|
<Typography classes={{ root: lineClass(item.color) }} paragraph={false}>
|
||||||
{item.text}
|
{parseOutputText(item)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
|
@ -1532,7 +1532,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
length += 2;
|
length += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ans.length === length;
|
|
||||||
|
return ans.length <= length;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1555,12 +1556,12 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
"You are given the following LZ-encoded string:\n",
|
"You are given the following LZ-encoded string:\n",
|
||||||
` ${compressed}\n`,
|
` ${compressed}\n`,
|
||||||
"Decode it and output the original string.\n\n",
|
"Decode it and output the original string.\n\n",
|
||||||
"Example: decoding '5aaabc340533bca' chunk-by-chunk\n",
|
"Example: decoding '5aaabb450723abb' chunk-by-chunk\n",
|
||||||
" 5aaabc -> aaabc\n",
|
" 5aaabb -> aaabb\n",
|
||||||
" 5aaabc34 -> aaabcaab\n",
|
" 5aaabb45 -> aaabbaaab\n",
|
||||||
" 5aaabc340 -> aaabcaab\n",
|
" 5aaabb450 -> aaabbaaab\n",
|
||||||
" 5aaabc34053 -> aaabcaabaabaa\n",
|
" 5aaabb45072 -> aaabbaaababababa\n",
|
||||||
" 5aaabc340533bca -> aaabcaabaabaabca",
|
" 5aaabb450723abb -> aaabbaaababababaabb",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
},
|
},
|
||||||
gen: (): string => {
|
gen: (): string => {
|
||||||
@ -1591,21 +1592,21 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
` ${plaintext}\n`,
|
` ${plaintext}\n`,
|
||||||
"Encode it using Lempel-Ziv encoding with the minimum possible output length.\n\n",
|
"Encode it using Lempel-Ziv encoding with the minimum possible output length.\n\n",
|
||||||
"Examples (some have other possible encodings of minimal length):\n",
|
"Examples (some have other possible encodings of minimal length):\n",
|
||||||
" abracadabra -> 7abracad47\n",
|
" abracadabra -> 7abracad47\n",
|
||||||
" mississippi -> 4miss433ppi\n",
|
" mississippi -> 4miss433ppi\n",
|
||||||
" aAAaAAaAaAA -> 3aAA53035\n",
|
" aAAaAAaAaAA -> 3aAA53035\n",
|
||||||
" 2718281828 -> 627182844\n",
|
" 2718281828 -> 627182844\n",
|
||||||
" abcdefghijk -> 9abcdefghi02jk\n",
|
" abcdefghijk -> 9abcdefghi02jk\n",
|
||||||
" aaaaaaaaaaa -> 1a911a\n",
|
" aaaaaaaaaaaa -> 3aaa91\n",
|
||||||
" aaaaaaaaaaaa -> 1a912aa\n",
|
" aaaaaaaaaaaaa -> 1a91031\n",
|
||||||
" aaaaaaaaaaaaa -> 1a91031",
|
" aaaaaaaaaaaaaa -> 1a91041",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
},
|
},
|
||||||
gen: (): string => {
|
gen: (): string => {
|
||||||
return comprLZGenerate();
|
return comprLZGenerate();
|
||||||
},
|
},
|
||||||
solver: (plain: string, ans: string): boolean => {
|
solver: (plain: string, ans: string): boolean => {
|
||||||
return comprLZDecode(ans) === plain && ans.length === comprLZEncode(plain).length;
|
return comprLZDecode(ans) === plain && ans.length <= comprLZEncode(plain).length;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -4,7 +4,6 @@ import ReactDOM from "react-dom";
|
|||||||
import { TTheme as Theme, ThemeEvents, refreshTheme } from "./Themes/ui/Theme";
|
import { TTheme as Theme, ThemeEvents, refreshTheme } from "./Themes/ui/Theme";
|
||||||
import { LoadingScreen } from "./ui/LoadingScreen";
|
import { LoadingScreen } from "./ui/LoadingScreen";
|
||||||
import { initElectron } from "./Electron";
|
import { initElectron } from "./Electron";
|
||||||
import { AlertEvents } from "./ui/React/AlertManager";
|
|
||||||
initElectron();
|
initElectron();
|
||||||
globalThis["React"] = React;
|
globalThis["React"] = React;
|
||||||
globalThis["ReactDOM"] = ReactDOM;
|
globalThis["ReactDOM"] = ReactDOM;
|
||||||
|
@ -9,6 +9,7 @@ import ArrowBackIos from "@mui/icons-material/ArrowBackIos";
|
|||||||
import { ITutorialEvents } from "./ITutorialEvents";
|
import { ITutorialEvents } from "./ITutorialEvents";
|
||||||
import { CopyableText } from "../React/CopyableText";
|
import { CopyableText } from "../React/CopyableText";
|
||||||
|
|
||||||
|
import List from "@mui/material/List";
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from "@mui/material/ListItem";
|
||||||
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
||||||
import LastPageIcon from "@mui/icons-material/LastPage";
|
import LastPageIcon from "@mui/icons-material/LastPage";
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
iTutorialSteps,
|
iTutorialSteps,
|
||||||
iTutorialEnd,
|
iTutorialEnd,
|
||||||
} from "../../InteractiveTutorial";
|
} from "../../InteractiveTutorial";
|
||||||
|
import { NSSelection } from "./NSSelection";
|
||||||
|
|
||||||
interface IContent {
|
interface IContent {
|
||||||
content: React.ReactElement;
|
content: React.ReactElement;
|
||||||
@ -45,9 +47,23 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
enum Language {
|
||||||
|
None,
|
||||||
|
NS1,
|
||||||
|
NS2,
|
||||||
|
}
|
||||||
|
|
||||||
export function InteractiveTutorialRoot(): React.ReactElement {
|
export function InteractiveTutorialRoot(): React.ReactElement {
|
||||||
|
const [nsSelectionOpen, setNSSelectionOpen] = useState(false);
|
||||||
|
const [language, setLanguage] = useState(Language.None);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const tutorialScriptName = {
|
||||||
|
[Language.None]: "n00dles.script",
|
||||||
|
[Language.NS1]: "n00dles.script",
|
||||||
|
[Language.NS2]: "n00dles.js",
|
||||||
|
}[language];
|
||||||
|
|
||||||
const contents: { [number: string]: IContent | undefined } = {
|
const contents: { [number: string]: IContent | undefined } = {
|
||||||
[iTutorialSteps.Start as number]: {
|
[iTutorialSteps.Start as number]: {
|
||||||
content: (
|
content: (
|
||||||
@ -66,6 +82,47 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
),
|
),
|
||||||
canNext: true,
|
canNext: true,
|
||||||
},
|
},
|
||||||
|
[iTutorialSteps.NSSelection as number]: {
|
||||||
|
content: (
|
||||||
|
<>
|
||||||
|
<Typography>The tutorial will adjust to your programming ability.</Typography>
|
||||||
|
<Typography>Bitburner has 2 types of scripts:</Typography>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<Typography>NS1: Javascript from 2009, very simple. Recommended for beginners to programming.</Typography>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<Typography>
|
||||||
|
NS2: Native, modern Javascript. Recommended if you know any programming language or are serious about
|
||||||
|
learning programming.
|
||||||
|
</Typography>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
<Typography>
|
||||||
|
Both are available at all time and interchangeably. This choice is only for the tutorial.
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setLanguage(Language.NS1);
|
||||||
|
iTutorialNextStep();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use NS1
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setLanguage(Language.NS2);
|
||||||
|
iTutorialNextStep();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use NS2
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setNSSelectionOpen(true)}>More info</Button>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
canNext: false,
|
||||||
|
},
|
||||||
[iTutorialSteps.GoToCharacterPage as number]: {
|
[iTutorialSteps.GoToCharacterPage as number]: {
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
@ -321,7 +378,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography>
|
||||||
|
|
||||||
<Typography>Scripts must end with the .script extension. Let's make a script now by entering </Typography>
|
<Typography>Scripts must end with the .script extension. Let's make a script now by entering </Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> nano ${tutorialScriptName}`}</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
after the hack command finishes running (Sidenote: Pressing ctrl + c will end a command like hack early)
|
after the hack command finishes running (Sidenote: Pressing ctrl + c will end a command like hack early)
|
||||||
@ -334,16 +391,28 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<Typography>
|
<Typography>
|
||||||
This is the script editor. You can use it to program your scripts. Scripts are written in a simplified
|
This is the script editor. You can use it to program your scripts.{" "}
|
||||||
version of javascript. Copy and paste the following code into the script editor: <br />
|
{language !== Language.NS2 && <>Scripts are written in a simplified version of javascript.</>} Copy and
|
||||||
|
paste the following code into the script editor: <br />
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography classes={{ root: classes.code }}>
|
<Typography classes={{ root: classes.code }}>
|
||||||
<CopyableText
|
{language !== Language.NS2 && (
|
||||||
value={`while(true) {
|
<CopyableText
|
||||||
|
value={`while(true) {
|
||||||
hack('n00dles');
|
hack('n00dles');
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{language === Language.NS2 && (
|
||||||
|
<CopyableText
|
||||||
|
value={`export async function main(ns) {
|
||||||
|
while(true) {
|
||||||
|
await ns.hack('n00dles');
|
||||||
|
}
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
For anyone with basic programming experience, this code should be straightforward. This script will
|
For anyone with basic programming experience, this code should be straightforward. This script will
|
||||||
@ -378,7 +447,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
<Typography>
|
<Typography>
|
||||||
We have 8GB of free RAM on this machine, which is enough to run our script. Let's run our script using
|
We have 8GB of free RAM on this machine, which is enough to run our script. Let's run our script using
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> run n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> run ${tutorialScriptName}`}</Typography>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
canNext: false,
|
canNext: false,
|
||||||
@ -425,7 +494,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
One last thing about scripts, each active script contains logs that detail what it's doing. We can check
|
One last thing about scripts, each active script contains logs that detail what it's doing. We can check
|
||||||
these logs using the tail command. Do that now for the script we just ran by typing{" "}
|
these logs using the tail command. Do that now for the script we just ran by typing{" "}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> tail n00dles.script"}</Typography>
|
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> tail ${tutorialScriptName}`}</Typography>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
canNext: false,
|
canNext: false,
|
||||||
@ -447,14 +516,6 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
in the main navigation menu to look at the documentation.
|
in the main navigation menu to look at the documentation.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
If you know even a little bit of programming it is highly recommended you use NS2 instead. You will enjoy
|
|
||||||
the game much more. NS1 files end with .script and are a subset of javascript. NS2 files end with .js and
|
|
||||||
are full speed native javascript.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
You can learn more about the difference between them later in the documentation.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
For now, let's move on to something else!
|
For now, let's move on to something else!
|
||||||
</Typography>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
@ -549,25 +610,30 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
|||||||
const content = contents[step];
|
const content = contents[step];
|
||||||
if (content === undefined) throw new Error("error in the tutorial");
|
if (content === undefined) throw new Error("error in the tutorial");
|
||||||
return (
|
return (
|
||||||
<Paper square sx={{ maxWidth: "70vw", p: 2 }}>
|
<>
|
||||||
{content.content}
|
<NSSelection open={nsSelectionOpen} onClose={() => setNSSelectionOpen(false)} />
|
||||||
{step !== iTutorialSteps.TutorialPageInfo && (
|
<Paper square sx={{ maxWidth: "70vw", p: 2 }}>
|
||||||
<>
|
{content.content}
|
||||||
<IconButton onClick={iTutorialPrevStep} aria-label="previous">
|
{step !== iTutorialSteps.TutorialPageInfo && (
|
||||||
<ArrowBackIos />
|
<>
|
||||||
</IconButton>
|
{step !== iTutorialSteps.Start && (
|
||||||
{content.canNext && (
|
<IconButton onClick={iTutorialPrevStep} aria-label="previous">
|
||||||
<IconButton onClick={iTutorialNextStep} aria-label="next">
|
<ArrowBackIos />
|
||||||
<ArrowForwardIos />
|
</IconButton>
|
||||||
</IconButton>
|
)}
|
||||||
)}
|
{content.canNext && (
|
||||||
</>
|
<IconButton onClick={iTutorialNextStep} aria-label="next">
|
||||||
)}
|
<ArrowForwardIos />
|
||||||
<br />
|
</IconButton>
|
||||||
<br />
|
)}
|
||||||
<Button onClick={iTutorialEnd}>
|
</>
|
||||||
{step !== iTutorialSteps.TutorialPageInfo ? "SKIP TUTORIAL" : "FINISH TUTORIAL"}
|
)}
|
||||||
</Button>
|
<br />
|
||||||
</Paper>
|
<br />
|
||||||
|
<Button onClick={iTutorialEnd}>
|
||||||
|
{step !== iTutorialSteps.TutorialPageInfo ? "SKIP TUTORIAL" : "FINISH TUTORIAL"}
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
82
src/ui/InteractiveTutorial/NSSelection.tsx
Normal file
82
src/ui/InteractiveTutorial/NSSelection.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import Editor from "@monaco-editor/react";
|
||||||
|
import { Tab, Tabs, Typography } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Modal } from "../React/Modal";
|
||||||
|
|
||||||
|
import * as monaco from "monaco-editor";
|
||||||
|
|
||||||
|
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ns1Example = `while(true) {
|
||||||
|
hack('n00dles');
|
||||||
|
}`;
|
||||||
|
const ns2Example = `/** @param {NS} ns */
|
||||||
|
export async function main(ns) {
|
||||||
|
while(true) {
|
||||||
|
await ns.hack('n00dles');
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export function NSSelection(props: IProps): React.ReactElement {
|
||||||
|
const [value, setValue] = React.useState(0);
|
||||||
|
|
||||||
|
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||||
|
setValue(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMount(editor: IStandaloneCodeEditor): void {
|
||||||
|
editor.updateOptions({ readOnly: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||||
|
<Tab label="NS1" />
|
||||||
|
<Tab label="NS2" />
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
{value === 0 && (
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
These scripts end with '.script'. Using a very old interpreted version of javascript. It is perfect for
|
||||||
|
beginner to programming.
|
||||||
|
</Typography>
|
||||||
|
<Typography>Example script using NS1:</Typography>
|
||||||
|
<Editor
|
||||||
|
loading={<></>}
|
||||||
|
defaultLanguage="javascript"
|
||||||
|
defaultValue={ns1Example}
|
||||||
|
height={`${300}px`}
|
||||||
|
theme={"vs-dark"}
|
||||||
|
onMount={onMount}
|
||||||
|
options={{ fontSize: 30 }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{value === 1 && (
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
These scripts end with '.js'. Scripts using ns2 are running natively along the game. If you know any
|
||||||
|
programming language you should be using this. However if you're unfamiliar with javascript Promises you
|
||||||
|
might want to read up on them a little bit before diving in.
|
||||||
|
</Typography>
|
||||||
|
<Typography>Example script using NS2:</Typography>
|
||||||
|
<Editor
|
||||||
|
loading={<></>}
|
||||||
|
defaultLanguage="javascript"
|
||||||
|
defaultValue={ns2Example}
|
||||||
|
height={`${300}px`}
|
||||||
|
theme={"vs-dark"}
|
||||||
|
options={{ fontSize: 30 }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -12,6 +12,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
zIndex: 999999,
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
|
@ -105,6 +105,13 @@ export function comprLZEncode(plain: string): string {
|
|||||||
|
|
||||||
// start new literal
|
// start new literal
|
||||||
set(new_state, 0, 1, string + length + offset);
|
set(new_state, 0, 1, string + length + offset);
|
||||||
|
|
||||||
|
// end current backreference and start new backreference
|
||||||
|
for (let new_offset = 1; new_offset <= Math.min(9, i); ++new_offset) {
|
||||||
|
if (plain[i - new_offset] === c) {
|
||||||
|
set(new_state, new_offset, 1, string + length + offset + "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ export function exceptionAlert(e: IError | string): void {
|
|||||||
"This is a bug, please report to game developer with this " +
|
"This is a bug, please report to game developer with this " +
|
||||||
"message as well as details about how to reproduce the bug.<br><br>" +
|
"message as well as details about how to reproduce the bug.<br><br>" +
|
||||||
"If you want to be safe, I suggest refreshing the game WITHOUT saving so that your " +
|
"If you want to be safe, I suggest refreshing the game WITHOUT saving so that your " +
|
||||||
"safe doesn't get corrupted",
|
"save doesn't get corrupted",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Keyboard key codes
|
* Keyboard key codes as returned by event.key
|
||||||
*/
|
*/
|
||||||
export enum KEY {
|
export enum KEY {
|
||||||
//SHIFT: 16, // Check by `&& event.shiftKey`
|
//SHIFT: 16, // Check by `&& event.shiftKey`
|
||||||
@ -70,3 +70,69 @@ export enum KEY {
|
|||||||
Y = "y",
|
Y = "y",
|
||||||
Z = "z",
|
Z = "z",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard key codes as returned by event.code
|
||||||
|
*/
|
||||||
|
export enum KEYCODE {
|
||||||
|
//SHIFT: 16, // Check by `&& event.shiftKey`
|
||||||
|
//CTRL: 17, // Check by `&& event.ctrlKey`
|
||||||
|
//ALT: 18, // Check by `&& event.altKey`
|
||||||
|
ENTER = "Enter",
|
||||||
|
ESC = "Escape",
|
||||||
|
TAB = "Tab",
|
||||||
|
SPACE = "Space",
|
||||||
|
BACKSPACE = "Backspace",
|
||||||
|
UP_ARROW = "ArrowUp",
|
||||||
|
DOWN_ARROW = "ArrowDown",
|
||||||
|
LEFT_ARROW = "ArrowLeft",
|
||||||
|
RIGHT_ARROW = "ArrowRight",
|
||||||
|
|
||||||
|
BACKWARD_SLASH = "Backslash",
|
||||||
|
BACKQUOTE = "Backquote",
|
||||||
|
COMMA = "Comma",
|
||||||
|
DOT = "Period",
|
||||||
|
EQUAL = "Equal",
|
||||||
|
FORWARD_SLASH = "Slash",
|
||||||
|
HYPHEN = "Minus",
|
||||||
|
SEMICOLON = "Semicolon",
|
||||||
|
QUOTE = "Quote",
|
||||||
|
|
||||||
|
k0 = "Digit0",
|
||||||
|
k1 = "Digit1",
|
||||||
|
k2 = "Digit2",
|
||||||
|
k3 = "Digit3",
|
||||||
|
k4 = "Digit4",
|
||||||
|
k5 = "Digit5",
|
||||||
|
k6 = "Digit6",
|
||||||
|
k7 = "Digit7",
|
||||||
|
k8 = "Digit8",
|
||||||
|
k9 = "Digit9",
|
||||||
|
|
||||||
|
A = "KeyA",
|
||||||
|
B = "KeyB",
|
||||||
|
C = "KeyC",
|
||||||
|
D = "KeyD",
|
||||||
|
E = "KeyE",
|
||||||
|
F = "KeyF",
|
||||||
|
G = "KeyG",
|
||||||
|
H = "KeyH",
|
||||||
|
I = "KeyI",
|
||||||
|
J = "KeyJ",
|
||||||
|
K = "KeyK",
|
||||||
|
L = "KeyL",
|
||||||
|
M = "KeyM",
|
||||||
|
N = "KeyN",
|
||||||
|
O = "KeyO",
|
||||||
|
P = "KeyP",
|
||||||
|
Q = "KeyQ",
|
||||||
|
R = "KeyR",
|
||||||
|
S = "KeyS",
|
||||||
|
T = "KeyT",
|
||||||
|
U = "KeyU",
|
||||||
|
V = "KeyV",
|
||||||
|
W = "KeyW",
|
||||||
|
X = "KeyX",
|
||||||
|
Y = "KeyY",
|
||||||
|
Z = "KeyZ",
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user