mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-29 12:27:07 +02:00
merge latest dev
This commit is contained in:
Vendored
+6
@@ -1,2 +1,8 @@
|
||||
// Defined by webpack on startup or compilation
|
||||
declare let __COMMIT_HASH__: string;
|
||||
|
||||
// When using file-loader, we'll get a path to the resource
|
||||
declare module "*.png" {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ export const achievements: IMap<Achievement> = {
|
||||
},
|
||||
TRAVEL: {
|
||||
...achievementData["TRAVEL"],
|
||||
Icon: "travel",
|
||||
Icon: "TRAVEL",
|
||||
Condition: () => Player.city !== CityName.Sector12,
|
||||
},
|
||||
WORKOUT: {
|
||||
@@ -553,7 +553,9 @@ export const achievements: IMap<Achievement> = {
|
||||
...achievementData["MAX_CACHE"],
|
||||
Icon: "HASHNETCAP",
|
||||
Visible: () => hasAccessToSF(Player, 9),
|
||||
Condition: () => hasHacknetServers(Player) && Player.hashManager.hashes === Player.hashManager.capacity,
|
||||
Condition: () => hasHacknetServers(Player) &&
|
||||
Player.hashManager.hashes === Player.hashManager.capacity &&
|
||||
Player.hashManager.capacity > 0,
|
||||
},
|
||||
SLEEVE_8: {
|
||||
...achievementData["SLEEVE_8"],
|
||||
|
||||
+2
-2
@@ -22,12 +22,12 @@ export function loadGlobalAliases(saveString: string): void {
|
||||
|
||||
// Prints all aliases to terminal
|
||||
export function printAliases(): void {
|
||||
for (const name in Aliases) {
|
||||
for (const name of Object.keys(Aliases)) {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
Terminal.print("alias " + name + "=" + Aliases[name]);
|
||||
}
|
||||
}
|
||||
for (const name in GlobalAliases) {
|
||||
for (const name of Object.keys(GlobalAliases)) {
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
Terminal.print("global alias " + name + "=" + GlobalAliases[name]);
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ export class Augmentation {
|
||||
|
||||
// Adds this Augmentation to all Factions
|
||||
addToAllFactions(): void {
|
||||
for (const fac in Factions) {
|
||||
for (const fac of Object.keys(Factions)) {
|
||||
if (Factions.hasOwnProperty(fac)) {
|
||||
const facObj: Faction | null = Factions[fac];
|
||||
if (facObj == null) {
|
||||
|
||||
@@ -112,7 +112,7 @@ function getRandomBonus(): any {
|
||||
}
|
||||
|
||||
function initAugmentations(): void {
|
||||
for (const name in Factions) {
|
||||
for (const name of Object.keys(Factions)) {
|
||||
if (Factions.hasOwnProperty(name)) {
|
||||
Factions[name].augmentations = [];
|
||||
}
|
||||
@@ -2050,7 +2050,7 @@ function initAugmentations(): void {
|
||||
info:
|
||||
"A brain implant carefully assembled around the synapses, which " +
|
||||
"micromanages the activity and levels of various neuroreceptor " +
|
||||
"chemicals and modulates electrical acvitiy to optimize concentration, " +
|
||||
"chemicals and modulates electrical activity to optimize concentration, " +
|
||||
"allowing the user to multitask much more effectively.",
|
||||
stats: (
|
||||
<>
|
||||
@@ -2498,7 +2498,7 @@ function initAugmentations(): void {
|
||||
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
|
||||
Player.queuedAugmentations.length,
|
||||
);
|
||||
for (const name in Augmentations) {
|
||||
for (const name of Object.keys(Augmentations)) {
|
||||
if (Augmentations.hasOwnProperty(name)) {
|
||||
Augmentations[name].baseCost *= mult;
|
||||
}
|
||||
@@ -2525,7 +2525,7 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
||||
const augObj = Augmentations[aug.name];
|
||||
|
||||
// Apply multipliers
|
||||
for (const mult in augObj.mults) {
|
||||
for (const mult of Object.keys(augObj.mults)) {
|
||||
const v = Player.getMult(mult) * augObj.mults[mult];
|
||||
Player.setMult(mult, v);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function InstalledAugmentations(): React.ReactElement {
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceAugs.sort((aug1, aug2) => {
|
||||
return aug1.name <= aug2.name ? -1 : 1;
|
||||
return aug1.name.localeCompare(aug2.name);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ function calculateAugmentedStats(): any {
|
||||
const augP: any = {};
|
||||
for (const aug of Player.queuedAugmentations) {
|
||||
const augObj = Augmentations[aug.name];
|
||||
for (const mult in augObj.mults) {
|
||||
for (const mult of Object.keys(augObj.mults)) {
|
||||
const v = augP[mult] ? augP[mult] : 1;
|
||||
augP[mult] = v * augObj.mults[mult];
|
||||
}
|
||||
|
||||
@@ -578,7 +578,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
if (p.bitNodeN == null) {
|
||||
p.bitNodeN = 1;
|
||||
}
|
||||
for (const mult in BitNodeMultipliers) {
|
||||
for (const mult of Object.keys(BitNodeMultipliers)) {
|
||||
if (BitNodeMultipliers.hasOwnProperty(mult)) {
|
||||
BitNodeMultipliers[mult] = 1;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<span onClick={() => setPortalOpen(true)} className={cssClass}>
|
||||
<span onClick={() => setPortalOpen(true)} className={cssClass} aria-label={`enter-bitnode-${bitNode.number.toString()}`}>
|
||||
<b>O</b>
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
@@ -117,7 +117,7 @@ export class Action implements IAction {
|
||||
|
||||
// Check to make sure weights are summed properly
|
||||
let sum = 0;
|
||||
for (const weight in this.weights) {
|
||||
for (const weight of Object.keys(this.weights)) {
|
||||
if (this.weights.hasOwnProperty(weight)) {
|
||||
sum += this.weights[weight];
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export class Action implements IAction {
|
||||
);
|
||||
}
|
||||
|
||||
for (const decay in this.decays) {
|
||||
for (const decay of Object.keys(this.decays)) {
|
||||
if (this.decays.hasOwnProperty(decay)) {
|
||||
if (this.decays[decay] > 1) {
|
||||
throw new Error(
|
||||
@@ -240,7 +240,7 @@ export class Action implements IAction {
|
||||
}
|
||||
let difficulty = this.getDifficulty();
|
||||
let competence = 0;
|
||||
for (const stat in this.weights) {
|
||||
for (const stat of Object.keys(this.weights)) {
|
||||
if (this.weights.hasOwnProperty(stat)) {
|
||||
const playerStatLvl = Player.queryStatFromString(stat);
|
||||
const key = "eff" + stat.charAt(0).toUpperCase() + stat.slice(1);
|
||||
|
||||
@@ -135,7 +135,7 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
// Can't start a BlackOp if you haven't done the one before it
|
||||
const blackops = [];
|
||||
for (const nm in BlackOperations) {
|
||||
for (const nm of Object.keys(BlackOperations)) {
|
||||
if (BlackOperations.hasOwnProperty(nm)) {
|
||||
blackops.push(nm);
|
||||
}
|
||||
@@ -1074,7 +1074,7 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
updateSkillMultipliers(): void {
|
||||
this.resetSkillMultipliers();
|
||||
for (const skillName in this.skills) {
|
||||
for (const skillName of Object.keys(this.skills)) {
|
||||
if (this.skills.hasOwnProperty(skillName)) {
|
||||
const skill = Skills[skillName];
|
||||
if (skill == null) {
|
||||
|
||||
@@ -12,7 +12,7 @@ interface IProps {
|
||||
|
||||
export function BlackOpList(props: IProps): React.ReactElement {
|
||||
let blackops: BlackOperation[] = [];
|
||||
for (const blackopName in BlackOperations) {
|
||||
for (const blackopName of Object.keys(BlackOperations)) {
|
||||
if (BlackOperations.hasOwnProperty(blackopName)) {
|
||||
blackops.push(BlackOperations[blackopName]);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ interface IProps {
|
||||
|
||||
export function GeneralActionList(props: IProps): React.ReactElement {
|
||||
const actions: Action[] = [];
|
||||
for (const name in GeneralActions) {
|
||||
for (const name of Object.keys(GeneralActions)) {
|
||||
if (GeneralActions.hasOwnProperty(name)) {
|
||||
actions.push(GeneralActions[name]);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
const MAX_BET = 100e6;
|
||||
export const DECK_COUNT = 5; // 5-deck multideck
|
||||
|
||||
enum Result {
|
||||
Pending = "",
|
||||
@@ -45,7 +46,7 @@ export class Blackjack extends Game<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.deck = new Deck(5); // 5-deck multideck
|
||||
this.deck = new Deck(DECK_COUNT);
|
||||
|
||||
const initialBet = 1e6;
|
||||
|
||||
|
||||
@@ -40,13 +40,13 @@ const strategies: {
|
||||
} = {
|
||||
Red: {
|
||||
match: (n: number): boolean => {
|
||||
if (n === 0) return false;
|
||||
return redNumbers.includes(n);
|
||||
},
|
||||
payout: 1,
|
||||
},
|
||||
Black: {
|
||||
match: (n: number): boolean => {
|
||||
if (n === 0) return false;
|
||||
return !redNumbers.includes(n);
|
||||
},
|
||||
payout: 1,
|
||||
@@ -118,12 +118,6 @@ export function Roulette(props: IProps): React.ReactElement {
|
||||
const [status, setStatus] = useState<string | JSX.Element>("waiting");
|
||||
const [n, setN] = useState(0);
|
||||
const [lock, setLock] = useState(true);
|
||||
const [strategy, setStrategy] = useState<Strategy>({
|
||||
payout: 0,
|
||||
match: (): boolean => {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const i = window.setInterval(step, 50);
|
||||
@@ -156,13 +150,12 @@ export function Roulette(props: IProps): React.ReactElement {
|
||||
return `${n}${color}`;
|
||||
}
|
||||
|
||||
function play(s: Strategy): void {
|
||||
function play(strategy: Strategy): void {
|
||||
if (reachedLimit(props.p)) return;
|
||||
|
||||
setCanPlay(false);
|
||||
setLock(false);
|
||||
setStatus("playing");
|
||||
setStrategy(s);
|
||||
|
||||
setTimeout(() => {
|
||||
let n = Math.floor(rng.random() * 37);
|
||||
|
||||
@@ -157,20 +157,20 @@ export function SlotMachine(props: IProps): React.ReactElement {
|
||||
function step(): void {
|
||||
let stoppedOne = false;
|
||||
const copy = index.slice();
|
||||
for (const i in copy) {
|
||||
for (let i = 0; i < copy.length; i++) {
|
||||
if (copy[i] === locks[i] && !stoppedOne) continue;
|
||||
copy[i] = (copy[i] + 1) % symbols.length;
|
||||
copy[i] = (copy[i] - 1 >= 0) ? copy[i] - 1 : symbols.length - 1;
|
||||
stoppedOne = true;
|
||||
}
|
||||
|
||||
setIndex(copy);
|
||||
|
||||
if (stoppedOne && copy.every((e, i) => e === locks[i])) {
|
||||
checkWinnings();
|
||||
checkWinnings(getTable(copy, symbols));
|
||||
}
|
||||
}
|
||||
|
||||
function getTable(): string[][] {
|
||||
function getTable(index:number[], symbols:string[]): string[][] {
|
||||
return [
|
||||
[
|
||||
symbols[(index[0] + symbols.length - 1) % symbols.length],
|
||||
@@ -209,8 +209,7 @@ export function SlotMachine(props: IProps): React.ReactElement {
|
||||
]);
|
||||
}
|
||||
|
||||
function checkWinnings(): void {
|
||||
const t = getTable();
|
||||
function checkWinnings(t:string[][]): void {
|
||||
const getPaylineData = function (payline: number[][]): string[] {
|
||||
const data = [];
|
||||
for (const point of payline) {
|
||||
@@ -267,7 +266,7 @@ export function SlotMachine(props: IProps): React.ReactElement {
|
||||
setInvestment(investment);
|
||||
}
|
||||
|
||||
const t = getTable();
|
||||
const t = getTable(index, symbols);
|
||||
// prettier-ignore
|
||||
return (
|
||||
<>
|
||||
@@ -288,7 +287,7 @@ export function SlotMachine(props: IProps): React.ReactElement {
|
||||
disabled={!canPlay}
|
||||
>Spin!</Button>)}}
|
||||
/>
|
||||
|
||||
|
||||
<Typography variant="h4">{status}</Typography>
|
||||
<Typography>Pay lines</Typography>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export function initCompanies(): void {
|
||||
});
|
||||
|
||||
// Reset data
|
||||
for (const companyName in Companies) {
|
||||
for (const companyName of Object.keys(Companies)) {
|
||||
const company = Companies[companyName];
|
||||
const oldCompany = oldCompanies[companyName];
|
||||
if (!(oldCompany instanceof Company)) {
|
||||
|
||||
+11
-78
@@ -111,8 +111,8 @@ export const CONSTANTS: {
|
||||
TotalNumBitNodes: number;
|
||||
LatestUpdate: string;
|
||||
} = {
|
||||
VersionString: "1.3.0",
|
||||
VersionNumber: 9,
|
||||
VersionString: "1.4.0",
|
||||
VersionNumber: 10,
|
||||
|
||||
// Speed (in ms) at which the main loop is updated
|
||||
_idleSpeed: 200,
|
||||
@@ -273,89 +273,22 @@ export const CONSTANTS: {
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
LatestUpdate: `
|
||||
v1.3.0 - 2022-01-04 Cleaning up
|
||||
-------------------------------
|
||||
v1.4.0 - 2022-01-18 Sharing is caring
|
||||
-------------------------------------
|
||||
|
||||
** External IDE integration **
|
||||
** Computer sharing **
|
||||
|
||||
* The Steam version has a webserver that allows integration with external IDEs.
|
||||
A VSCode extension is available on the market place. (The documentation for the ext. isn't
|
||||
written yet)
|
||||
* A new mechanic has been added, it's is invoked by calling the new function 'share'.
|
||||
This mechanic helps you farm reputation faster.
|
||||
|
||||
** Source-Files **
|
||||
|
||||
* SF4 has been reworked.
|
||||
* New SF -1.
|
||||
** gang **
|
||||
|
||||
** UI **
|
||||
* Installing augs means losing a little bit of ascension multipliers.
|
||||
|
||||
* Fix some edge case with skill bat tooltips (@MartinFournier)
|
||||
* Made some background match theme color (@Kejikus)
|
||||
* Fix problem with script editor height not adjusting correctly (@billyvg)
|
||||
* Fix some formatting issues with Bladeburner (@MartinFournier, @nickofolas)
|
||||
* Fix some functions like 'alert' format messages better (@MageKing17)
|
||||
* Many community themes added.
|
||||
* New script editor theme (@Hedrauta, @Dexalt142)
|
||||
* Improvements to tail windows (@theit8514)
|
||||
* Training is more consise (@mikomyazaki)
|
||||
* Fix Investopedia not displaying properly (@JotaroS)
|
||||
* Remove alpha from theme editor (@MartinFournier)
|
||||
* Fix corporation tooltip not displaying properly (@MartinFournier)
|
||||
* Add tooltip on backdoored location names (@MartinFournier)
|
||||
* Allow toasts to be dismissed by clicking them (@nickofolas)
|
||||
* Darkweb item listing now shows what you own. (@hexnaught)
|
||||
|
||||
** Bug fix **
|
||||
|
||||
* Fix unit tests (@MartinFournier)
|
||||
* Fixed issue with 'cat' and 'read' not finding foldered files (@Nick-Colclasure)
|
||||
* Buying on the dark web will remove incomplete exe (@hexnaught)
|
||||
* Fix bug that would cause the game to crash trying to go to a job without a job (@hexnaught)
|
||||
* purchaseServer validation (@nickofolas)
|
||||
* Script Editor focuses code when changing tab (@MartinFournier)
|
||||
* Fix script editor for .txt files (@65-7a)
|
||||
* Fix 'buy' command not displaying correctly. (@hexnaught)
|
||||
* Fix hackAnalyzeThread returning NaN (@mikomyazaki)
|
||||
* Electron handles exceptions better (@MageKing17)
|
||||
* Electron will handle 'unresponsive' event and present the opportunity to reload the game with no scripts (@MartinFournier)
|
||||
* Fix 'cp' between folders (@theit8514)
|
||||
* Fix throwing null/undefined errors (@nickofolas)
|
||||
* Allow shortcuts to work when unfocused (@MageKing17)
|
||||
* Fix some dependency issue (@locriacyber)
|
||||
* Fix corporation state returning an object instead of a string (@antonvmironov)
|
||||
* Fix 'mv' overwriting files (@theit8514)
|
||||
* Fix joesguns not being influenced by hack/grow (@dou867, @MartinFournier)
|
||||
* Added warning when opening external links. (@MartinFournier)
|
||||
* Prevent applying for positions that aren't offered (@TheMas3212)
|
||||
* Import has validation (@MartinFournier)
|
||||
** There's more but I'm going to write it later. **
|
||||
|
||||
** Misc. **
|
||||
|
||||
* Added vim mode to script editor (@billyvg)
|
||||
* Clean up script editor code (@Rez855)
|
||||
* 'cat' works on scripts (@65-7a)
|
||||
* Add wordWrap for Monaco (@MartinFournier)
|
||||
* Include map bundles in electron for easier debugging (@MartinFournier)
|
||||
* Fix importing very large files (@MartinFournier)
|
||||
* Cache program blob, reducing ram usage of the game (@theit8514)
|
||||
* Dev menu can set server to $0 (@mikomyazaki)
|
||||
* 'backdoor' allows direct connect (@mikomyazaki)
|
||||
* Github workflow work (@MartinFournier)
|
||||
* workForFaction / workForCompany have a new parameter (@theit8514)
|
||||
* Alias accept single quotes (@sporkwitch, @FaintSpeaker)
|
||||
* Add grep options to 'ps' (@maxtimum)
|
||||
* Added buy all option to 'buy' (@anthonydroberts)
|
||||
* Added more shortcuts to terminal input (@Frank-py)
|
||||
* Refactor some port code (@ErzengelLichtes)
|
||||
* Settings to control GiB vs GB (@ErzengelLichtes)
|
||||
* Add electron option to export save game (@MartinFournier)
|
||||
* Electron improvements (@MartinFournier)
|
||||
* Expose some notifications functions to electron (@MartinFournier)
|
||||
* Documentation (@MartinFournier, @cyn, @millennIumAMbiguity, @2PacIsAlive,
|
||||
@TheCoderJT, @hexnaught, @sschmidTU, @FOLLGAD, @Hedrauta, @Xynrati,
|
||||
@mikomyazaki, @Icehawk78, @aaronransley, @TheMas3212, @Hedrauta, @alkemann,
|
||||
@ReeseJones, @amclark42, @thadguidry, @jasonhaxstuff, @pan-kuleczka, @jhollowe,
|
||||
@ApatheticsAnonymous, @erplsf, @daanflore, @nickofolas, @Kebap, @smolgumball,
|
||||
@woody-lam-cwl)
|
||||
* Nerf noodle bar.
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -307,7 +307,7 @@ export class Corporation {
|
||||
if (upgN === 1) {
|
||||
for (let i = 0; i < this.divisions.length; ++i) {
|
||||
const industry = this.divisions[i];
|
||||
for (const city in industry.warehouses) {
|
||||
for (const city of Object.keys(industry.warehouses)) {
|
||||
const warehouse = industry.warehouses[city];
|
||||
if (warehouse === 0) continue;
|
||||
if (industry.warehouses.hasOwnProperty(city) && warehouse instanceof Warehouse) {
|
||||
|
||||
+28
-28
@@ -378,7 +378,7 @@ export class Industry implements IIndustry {
|
||||
updateWarehouseSizeUsed(warehouse: Warehouse): void {
|
||||
warehouse.updateMaterialSizeUsed();
|
||||
|
||||
for (const prodName in this.products) {
|
||||
for (const prodName of Object.keys(this.products)) {
|
||||
if (this.products.hasOwnProperty(prodName)) {
|
||||
const prod = this.products[prodName];
|
||||
if (prod === undefined) continue;
|
||||
@@ -414,7 +414,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// Process offices (and the employees in them)
|
||||
let employeeSalary = 0;
|
||||
for (const officeLoc in this.offices) {
|
||||
for (const officeLoc of Object.keys(this.offices)) {
|
||||
const office = this.offices[officeLoc];
|
||||
if (office === 0) continue;
|
||||
if (office instanceof OfficeSpace) {
|
||||
@@ -473,7 +473,7 @@ export class Industry implements IIndustry {
|
||||
if (this.warehouses[CorporationConstants.Cities[i]] instanceof Warehouse) {
|
||||
const wh = this.warehouses[CorporationConstants.Cities[i]];
|
||||
if (wh === 0) continue;
|
||||
for (const name in reqMats) {
|
||||
for (const name of Object.keys(reqMats)) {
|
||||
if (reqMats.hasOwnProperty(name)) {
|
||||
wh.materials[name].processMarket();
|
||||
}
|
||||
@@ -496,7 +496,7 @@ export class Industry implements IIndustry {
|
||||
// Process change in demand and competition for this industry's products
|
||||
processProductMarket(marketCycles = 1): void {
|
||||
// Demand gradually decreases, and competition gradually increases
|
||||
for (const name in this.products) {
|
||||
for (const name of Object.keys(this.products)) {
|
||||
if (this.products.hasOwnProperty(name)) {
|
||||
const product = this.products[name];
|
||||
if (product === undefined) continue;
|
||||
@@ -534,7 +534,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
const warehouse = this.warehouses[city];
|
||||
if (warehouse === 0) continue;
|
||||
for (const matName in warehouse.materials) {
|
||||
for (const matName of Object.keys(warehouse.materials)) {
|
||||
if (warehouse.materials.hasOwnProperty(matName)) {
|
||||
const mat = warehouse.materials[matName];
|
||||
mat.imp = 0;
|
||||
@@ -555,7 +555,7 @@ export class Industry implements IIndustry {
|
||||
switch (this.state) {
|
||||
case "PURCHASE": {
|
||||
/* Process purchase of materials */
|
||||
for (const matName in warehouse.materials) {
|
||||
for (const matName of Object.keys(warehouse.materials)) {
|
||||
if (!warehouse.materials.hasOwnProperty(matName)) continue;
|
||||
const mat = warehouse.materials[matName];
|
||||
let buyAmt = 0;
|
||||
@@ -577,7 +577,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// smart supply
|
||||
const smartBuy: { [key: string]: number | undefined } = {};
|
||||
for (const matName in warehouse.materials) {
|
||||
for (const matName of Object.keys(warehouse.materials)) {
|
||||
if (!warehouse.materials.hasOwnProperty(matName)) continue;
|
||||
if (!warehouse.smartSupplyEnabled || !Object.keys(this.reqMats).includes(matName)) continue;
|
||||
const mat = warehouse.materials[matName];
|
||||
@@ -594,7 +594,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// Find which material were trying to create the least amount of product with.
|
||||
let worseAmt = 1e99;
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
const buyAmt = smartBuy[matName];
|
||||
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
|
||||
const reqMat = this.reqMats[matName];
|
||||
@@ -604,7 +604,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
|
||||
// Align all the materials to the smallest amount.
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
const reqMat = this.reqMats[matName];
|
||||
if (reqMat === undefined) throw new Error(`reqMat "${matName}" is undefined`);
|
||||
smartBuy[matName] = worseAmt * reqMat;
|
||||
@@ -612,7 +612,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// Calculate the total size of all things were trying to buy
|
||||
let totalSize = 0;
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
const buyAmt = smartBuy[matName];
|
||||
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
|
||||
totalSize += buyAmt * MaterialSizes[matName];
|
||||
@@ -621,7 +621,7 @@ export class Industry implements IIndustry {
|
||||
// Shrink to the size of available space.
|
||||
const freeSpace = warehouse.size - warehouse.sizeUsed;
|
||||
if (totalSize > freeSpace) {
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
const buyAmt = smartBuy[matName];
|
||||
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
|
||||
smartBuy[matName] = Math.floor((buyAmt * freeSpace) / totalSize);
|
||||
@@ -629,7 +629,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
|
||||
// Use the materials already in the warehouse if the option is on.
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
if (!warehouse.smartSupplyUseLeftovers[matName]) continue;
|
||||
const mat = warehouse.materials[matName];
|
||||
const buyAmt = smartBuy[matName];
|
||||
@@ -638,7 +638,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
|
||||
// buy them
|
||||
for (const matName in smartBuy) {
|
||||
for (const matName of Object.keys(smartBuy)) {
|
||||
const mat = warehouse.materials[matName];
|
||||
const buyAmt = smartBuy[matName];
|
||||
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
|
||||
@@ -675,7 +675,7 @@ export class Industry implements IIndustry {
|
||||
for (let tmp = 0; tmp < this.prodMats.length; ++tmp) {
|
||||
totalMatSize += MaterialSizes[this.prodMats[tmp]];
|
||||
}
|
||||
for (const reqMatName in this.reqMats) {
|
||||
for (const reqMatName of Object.keys(this.reqMats)) {
|
||||
const normQty = this.reqMats[reqMatName];
|
||||
if (normQty === undefined) continue;
|
||||
totalMatSize -= MaterialSizes[reqMatName] * normQty;
|
||||
@@ -695,7 +695,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// Make sure we have enough resource to make our materials
|
||||
let producableFrac = 1;
|
||||
for (const reqMatName in this.reqMats) {
|
||||
for (const reqMatName of Object.keys(this.reqMats)) {
|
||||
if (this.reqMats.hasOwnProperty(reqMatName)) {
|
||||
const reqMat = this.reqMats[reqMatName];
|
||||
if (reqMat === undefined) continue;
|
||||
@@ -712,7 +712,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
// Make our materials if they are producable
|
||||
if (producableFrac > 0 && prod > 0) {
|
||||
for (const reqMatName in this.reqMats) {
|
||||
for (const reqMatName of Object.keys(this.reqMats)) {
|
||||
const reqMat = this.reqMats[reqMatName];
|
||||
if (reqMat === undefined) continue;
|
||||
const reqMatQtyNeeded = reqMat * prod * producableFrac;
|
||||
@@ -729,7 +729,7 @@ export class Industry implements IIndustry {
|
||||
Math.pow(warehouse.materials["AICores"].qty, this.aiFac) / 10e3;
|
||||
}
|
||||
} else {
|
||||
for (const reqMatName in this.reqMats) {
|
||||
for (const reqMatName of Object.keys(this.reqMats)) {
|
||||
if (this.reqMats.hasOwnProperty(reqMatName)) {
|
||||
warehouse.materials[reqMatName].prd = 0;
|
||||
}
|
||||
@@ -745,7 +745,7 @@ export class Industry implements IIndustry {
|
||||
//If this doesn't produce any materials, then it only creates
|
||||
//Products. Creating products will consume materials. The
|
||||
//Production of all consumed materials must be set to 0
|
||||
for (const reqMatName in this.reqMats) {
|
||||
for (const reqMatName of Object.keys(this.reqMats)) {
|
||||
warehouse.materials[reqMatName].prd = 0;
|
||||
}
|
||||
}
|
||||
@@ -753,7 +753,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
case "SALE":
|
||||
/* Process sale of materials */
|
||||
for (const matName in warehouse.materials) {
|
||||
for (const matName of Object.keys(warehouse.materials)) {
|
||||
if (warehouse.materials.hasOwnProperty(matName)) {
|
||||
const mat = warehouse.materials[matName];
|
||||
if (mat.sCost < 0 || mat.sllman[0] === false) {
|
||||
@@ -884,7 +884,7 @@ export class Industry implements IIndustry {
|
||||
break;
|
||||
|
||||
case "EXPORT":
|
||||
for (const matName in warehouse.materials) {
|
||||
for (const matName of Object.keys(warehouse.materials)) {
|
||||
if (warehouse.materials.hasOwnProperty(matName)) {
|
||||
const mat = warehouse.materials[matName];
|
||||
mat.totalExp = 0; //Reset export
|
||||
@@ -996,7 +996,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
//Create products
|
||||
if (this.state === "PRODUCTION") {
|
||||
for (const prodName in this.products) {
|
||||
for (const prodName of Object.keys(this.products)) {
|
||||
const prod = this.products[prodName];
|
||||
if (prod === undefined) continue;
|
||||
if (!prod.fin) {
|
||||
@@ -1028,7 +1028,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
|
||||
//Produce Products
|
||||
for (const prodName in this.products) {
|
||||
for (const prodName of Object.keys(this.products)) {
|
||||
if (this.products.hasOwnProperty(prodName)) {
|
||||
const prod = this.products[prodName];
|
||||
if (prod instanceof Product && prod.fin) {
|
||||
@@ -1070,7 +1070,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
//Calculate net change in warehouse storage making the Products will cost
|
||||
let netStorageSize = product.siz;
|
||||
for (const reqMatName in product.reqMats) {
|
||||
for (const reqMatName of Object.keys(product.reqMats)) {
|
||||
if (product.reqMats.hasOwnProperty(reqMatName)) {
|
||||
const normQty = product.reqMats[reqMatName];
|
||||
netStorageSize -= MaterialSizes[reqMatName] * normQty;
|
||||
@@ -1087,7 +1087,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
//Make sure we have enough resources to make our Products
|
||||
let producableFrac = 1;
|
||||
for (const reqMatName in product.reqMats) {
|
||||
for (const reqMatName of Object.keys(product.reqMats)) {
|
||||
if (product.reqMats.hasOwnProperty(reqMatName)) {
|
||||
const req = product.reqMats[reqMatName] * prod;
|
||||
if (warehouse.materials[reqMatName].qty < req) {
|
||||
@@ -1098,7 +1098,7 @@ export class Industry implements IIndustry {
|
||||
|
||||
//Make our Products if they are producable
|
||||
if (producableFrac > 0 && prod > 0) {
|
||||
for (const reqMatName in product.reqMats) {
|
||||
for (const reqMatName of Object.keys(product.reqMats)) {
|
||||
if (product.reqMats.hasOwnProperty(reqMatName)) {
|
||||
const reqMatQtyNeeded = product.reqMats[reqMatName] * prod * producableFrac;
|
||||
warehouse.materials[reqMatName].qty -= reqMatQtyNeeded;
|
||||
@@ -1117,7 +1117,7 @@ export class Industry implements IIndustry {
|
||||
case "SALE": {
|
||||
//Process sale of Products
|
||||
product.pCost = 0; //Estimated production cost
|
||||
for (const reqMatName in product.reqMats) {
|
||||
for (const reqMatName of Object.keys(product.reqMats)) {
|
||||
if (product.reqMats.hasOwnProperty(reqMatName)) {
|
||||
product.pCost += product.reqMats[reqMatName] * warehouse.materials[reqMatName].bCost;
|
||||
}
|
||||
@@ -1253,7 +1253,7 @@ export class Industry implements IIndustry {
|
||||
}
|
||||
|
||||
discontinueProduct(product: Product): void {
|
||||
for (const productName in this.products) {
|
||||
for (const productName of Object.keys(this.products)) {
|
||||
if (this.products.hasOwnProperty(productName)) {
|
||||
if (product === this.products[productName]) {
|
||||
delete this.products[productName];
|
||||
@@ -1358,7 +1358,7 @@ export class Industry implements IIndustry {
|
||||
// Since ResearchTree data isnt saved, we'll update the Research Tree data
|
||||
// based on the stored 'researched' property in the Industry object
|
||||
if (Object.keys(researchTree.researched).length !== Object.keys(this.researched).length) {
|
||||
for (const research in this.researched) {
|
||||
for (const research of Object.keys(this.researched)) {
|
||||
researchTree.research(research);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export class OfficeSpace {
|
||||
|
||||
calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void {
|
||||
//Reset
|
||||
for (const name in this.employeeProd) {
|
||||
for (const name of Object.keys(this.employeeProd)) {
|
||||
this.employeeProd[name] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ export class Product {
|
||||
|
||||
//Calculate the product's required materials
|
||||
//For now, just set it to be the same as the requirements to make materials
|
||||
for (const matName in industry.reqMats) {
|
||||
for (const matName of Object.keys(industry.reqMats)) {
|
||||
if (industry.reqMats.hasOwnProperty(matName)) {
|
||||
const reqMat = industry.reqMats[matName];
|
||||
if (reqMat === undefined) continue;
|
||||
@@ -209,7 +209,7 @@ export class Product {
|
||||
//Calculate the product's size
|
||||
//For now, just set it to be the same size as the requirements to make materials
|
||||
this.siz = 0;
|
||||
for (const matName in industry.reqMats) {
|
||||
for (const matName of Object.keys(industry.reqMats)) {
|
||||
const reqMat = industry.reqMats[matName];
|
||||
if (reqMat === undefined) continue;
|
||||
this.siz += MaterialSizes[matName] * reqMat;
|
||||
|
||||
@@ -85,7 +85,7 @@ export class Warehouse {
|
||||
// Re-calculate how much space is being used by this Warehouse
|
||||
updateMaterialSizeUsed(): void {
|
||||
this.sizeUsed = 0;
|
||||
for (const matName in this.materials) {
|
||||
for (const matName of Object.keys(this.materials)) {
|
||||
const mat = this.materials[matName];
|
||||
if (MaterialSizes.hasOwnProperty(matName)) {
|
||||
this.sizeUsed += mat.qty * MaterialSizes[matName];
|
||||
|
||||
@@ -26,6 +26,7 @@ import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import { TableCell } from "../../ui/React/Table";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
interface IProps {
|
||||
office: OfficeSpace;
|
||||
@@ -430,51 +431,46 @@ export function IndustryOffice(props: IProps): React.ReactElement {
|
||||
<Typography>
|
||||
Size: {props.office.employees.length} / {props.office.size} employees
|
||||
</Typography>
|
||||
<Tooltip title={<Typography>Automatically hires an employee and gives him/her a random name</Typography>}>
|
||||
<span>
|
||||
<Button disabled={props.office.atCapacity()} onClick={autohireEmployeeButtonOnClick}>
|
||||
Hire Employee
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<br />
|
||||
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
|
||||
<span>
|
||||
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}>
|
||||
Upgrade size
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<UpgradeOfficeSizeModal
|
||||
rerender={props.rerender}
|
||||
office={props.office}
|
||||
open={upgradeOfficeSizeOpen}
|
||||
onClose={() => setUpgradeOfficeSizeOpen(false)}
|
||||
/>
|
||||
|
||||
{!division.hasResearch("AutoPartyManager") && (
|
||||
<>
|
||||
<Tooltip
|
||||
title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
|
||||
>
|
||||
<span>
|
||||
<Button disabled={corp.funds < 0} onClick={() => setThrowPartyOpen(true)}>
|
||||
Throw Party
|
||||
</Button>
|
||||
</span>
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr', width: 'fit-content' }}>
|
||||
<Box sx={{ gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
||||
<Tooltip title={<Typography>Automatically hires an employee and gives him/her a random name</Typography>}>
|
||||
<Button disabled={props.office.atCapacity()} onClick={autohireEmployeeButtonOnClick}>
|
||||
Hire Employee
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ThrowPartyModal
|
||||
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
|
||||
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}>
|
||||
Upgrade size
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<UpgradeOfficeSizeModal
|
||||
rerender={props.rerender}
|
||||
office={props.office}
|
||||
open={throwPartyOpen}
|
||||
onClose={() => setThrowPartyOpen(false)}
|
||||
open={upgradeOfficeSizeOpen}
|
||||
onClose={() => setUpgradeOfficeSizeOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<br />
|
||||
{!division.hasResearch("AutoPartyManager") && (
|
||||
<>
|
||||
<Tooltip
|
||||
title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
|
||||
>
|
||||
<Button disabled={corp.funds < 0} onClick={() => setThrowPartyOpen(true)}>
|
||||
Throw Party
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<ThrowPartyModal
|
||||
rerender={props.rerender}
|
||||
office={props.office}
|
||||
open={throwPartyOpen}
|
||||
onClose={() => setThrowPartyOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<SwitchButton manualMode={employeeManualAssignMode} switchMode={setEmployeeManualAssignMode} />
|
||||
</Box>
|
||||
<SwitchButton manualMode={employeeManualAssignMode} switchMode={setEmployeeManualAssignMode} />
|
||||
</Box>
|
||||
{employeeManualAssignMode ? (
|
||||
<ManualManagement rerender={props.rerender} office={props.office} />
|
||||
) : (
|
||||
|
||||
@@ -215,7 +215,7 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
|
||||
const corp = useCorporation();
|
||||
const division = useDivision();
|
||||
const upgrades = [];
|
||||
for (const index in IndustryUpgrades) {
|
||||
for (const index of Object.keys(IndustryUpgrades)) {
|
||||
const upgrade = IndustryUpgrades[index];
|
||||
|
||||
// AutoBrew research disables the Coffee upgrade
|
||||
|
||||
@@ -81,7 +81,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
|
||||
// Create React components for materials
|
||||
const mats = [];
|
||||
for (const matName in props.warehouse.materials) {
|
||||
for (const matName of Object.keys(props.warehouse.materials)) {
|
||||
if (!(props.warehouse.materials[matName] instanceof Material)) continue;
|
||||
// Only create UI for materials that are relevant for the industry
|
||||
if (!isRelevantMaterial(matName, division)) continue;
|
||||
@@ -99,7 +99,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
// Create React components for products
|
||||
const products = [];
|
||||
if (division.makesProducts && Object.keys(division.products).length > 0) {
|
||||
for (const productName in division.products) {
|
||||
for (const productName of Object.keys(division.products)) {
|
||||
const product = division.products[productName];
|
||||
if (!(product instanceof Product)) continue;
|
||||
products.push(
|
||||
@@ -109,14 +109,14 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
const breakdownItems: JSX.Element[] = [];
|
||||
for (const matName in props.warehouse.materials) {
|
||||
for (const matName of Object.keys(props.warehouse.materials)) {
|
||||
const mat = props.warehouse.materials[matName];
|
||||
if (!MaterialSizes.hasOwnProperty(matName)) continue;
|
||||
if (mat.qty === 0) continue;
|
||||
breakdownItems.push(<>{matName}: {numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0.0")}</>);
|
||||
}
|
||||
|
||||
for (const prodName in division.products) {
|
||||
for (const prodName of Object.keys(division.products)) {
|
||||
const prod = division.products[prodName];
|
||||
if (prod === undefined) continue;
|
||||
breakdownItems.push(<>{prodName}: {numeralWrapper.format(prod.data[props.warehouse.loc][0] * prod.siz, "0,0.0")}</>);
|
||||
@@ -139,13 +139,13 @@ function WarehouseRoot(props: IProps): React.ReactElement {
|
||||
{numeralWrapper.formatBigNumber(props.warehouse.size)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
|
||||
<Button disabled={!canAffordUpgrade} onClick={upgradeWarehouseOnClick}>
|
||||
Upgrade Warehouse Size -
|
||||
<MoneyCost money={sizeUpgradeCost} corp={corp} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Button disabled={!canAffordUpgrade} onClick={upgradeWarehouseOnClick}>
|
||||
Upgrade Warehouse Size -
|
||||
<MoneyCost money={sizeUpgradeCost} corp={corp} />
|
||||
</Button>
|
||||
|
||||
<Typography>This industry uses the following equation for its production: </Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
|
||||
@@ -112,7 +112,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<Box display="flex">
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '2fr 1fr', m: '5px' }}>
|
||||
<Box>
|
||||
<Tooltip
|
||||
title={
|
||||
@@ -149,11 +149,10 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Box sx={{ "& button": { width: '100%' } }}>
|
||||
<Tooltip
|
||||
title={tutorial ? <Typography>Purchase your required materials to get production started!</Typography> : ""}
|
||||
>
|
||||
<span>
|
||||
<Button
|
||||
color={tutorial ? "error" : "primary"}
|
||||
onClick={() => setPurchaseMaterialOpen(true)}
|
||||
@@ -161,7 +160,6 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
>
|
||||
{purchaseButtonText}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<PurchaseMaterialModal
|
||||
mat={mat}
|
||||
@@ -177,7 +175,6 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
|
||||
<ExportModal mat={mat} open={exportOpen} onClose={() => setExportOpen(false)} />
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
|
||||
<Button
|
||||
color={division.prodMats.includes(props.mat.name) && !mat.sllman[0] ? "error" : "primary"}
|
||||
|
||||
@@ -89,19 +89,21 @@ export function Overview({ rerender }: IProps): React.ReactElement {
|
||||
<StatsTable rows={multRows} />
|
||||
<br />
|
||||
<BonusTime />
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
|
||||
that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
|
||||
helping you get started with managing it.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
|
||||
</Tooltip>
|
||||
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
|
||||
<BribeButton />
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', width: 'fit-content' }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
|
||||
that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
|
||||
helping you get started with managing it.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
|
||||
</Tooltip>
|
||||
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
|
||||
<BribeButton />
|
||||
</Box>
|
||||
<br />
|
||||
<Upgrades rerender={rerender} />
|
||||
</>
|
||||
@@ -125,11 +127,9 @@ function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement
|
||||
return (
|
||||
<>
|
||||
<Tooltip title={<Typography>{findInvestorsTooltip}</Typography>}>
|
||||
<span>
|
||||
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
|
||||
Find Investors
|
||||
</Button>
|
||||
</span>
|
||||
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
|
||||
Find Investors
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={
|
||||
@@ -143,7 +143,6 @@ function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement
|
||||
</Tooltip>
|
||||
<FindInvestorsModal open={findInvestorsopen} onClose={() => setFindInvestorsopen(false)} rerender={rerender} />
|
||||
<GoPublicModal open={goPublicopen} onClose={() => setGoPublicopen(false)} rerender={rerender} />
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -201,8 +200,8 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
|
||||
const sellSharesTooltip = sellSharesOnCd
|
||||
? "Cannot sell shares for " + corp.convertCooldownToString(corp.shareSaleCooldown)
|
||||
: "Sell your shares in the company. The money earned from selling your " +
|
||||
"shares goes into your personal account, not the Corporation's. " +
|
||||
"This is one of the only ways to profit from your business venture.";
|
||||
"shares goes into your personal account, not the Corporation's. " +
|
||||
"This is one of the only ways to profit from your business venture.";
|
||||
|
||||
const issueNewSharesOnCd = corp.issueNewSharesCooldown > 0;
|
||||
const issueNewSharesTooltip = issueNewSharesOnCd
|
||||
@@ -212,28 +211,21 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Tooltip title={<Typography>{sellSharesTooltip}</Typography>}>
|
||||
<span>
|
||||
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
|
||||
Sell Shares
|
||||
</Button>
|
||||
</span>
|
||||
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
|
||||
Sell Shares
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
|
||||
<Tooltip title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}>
|
||||
<span>
|
||||
<Button disabled={corp.issuedShares < 1} onClick={() => setBuybackSharesOpen(true)}>
|
||||
Buyback shares
|
||||
</Button>
|
||||
</span>
|
||||
<Button disabled={corp.issuedShares < 1} onClick={() => setBuybackSharesOpen(true)}>
|
||||
Buyback shares
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
|
||||
<br />
|
||||
<Tooltip title={<Typography>{issueNewSharesTooltip}</Typography>}>
|
||||
<span>
|
||||
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
|
||||
Issue New Shares
|
||||
</Button>
|
||||
</span>
|
||||
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
|
||||
Issue New Shares
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
|
||||
<Tooltip
|
||||
@@ -242,7 +234,6 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
|
||||
<Button onClick={() => setIssueDividendsOpen(true)}>Issue Dividends</Button>
|
||||
</Tooltip>
|
||||
<IssueDividendsModal open={issueDividendsOpen} onClose={() => setIssueDividendsOpen(false)} />
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -269,11 +260,9 @@ function BribeButton(): React.ReactElement {
|
||||
: "Your Corporation is not powerful enough to bribe Faction leaders"
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Button disabled={!canBribe} onClick={openBribe}>
|
||||
Bribe Factions
|
||||
</Button>
|
||||
</span>
|
||||
<Button disabled={!canBribe} onClick={openBribe}>
|
||||
Bribe Factions
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<BribeFactionModal open={open} onClose={() => setOpen(false)} />
|
||||
</>
|
||||
|
||||
@@ -81,7 +81,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
|
||||
);
|
||||
} else if (product.sCost) {
|
||||
if (isString(product.sCost)) {
|
||||
const sCost = (product.sCost as string).replace(/MP/g, product.pCost + "");
|
||||
const sCost = (product.sCost as string).replace(/MP/g, product.pCost + product.rat / product.mku + "");
|
||||
sellButtonText = (
|
||||
<>
|
||||
{sellButtonText} @ <Money money={eval(sCost)} />
|
||||
|
||||
@@ -6,17 +6,18 @@ import { IIndustry } from "../IIndustry";
|
||||
import { Research } from "../Actions";
|
||||
import { Node } from "../ResearchTree";
|
||||
import { ResearchMap } from "../ResearchMap";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
|
||||
interface INodeProps {
|
||||
n: Node | null;
|
||||
division: IIndustry;
|
||||
@@ -42,8 +43,8 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
|
||||
|
||||
dialogBoxCreate(
|
||||
`Researched ${n.text}. It may take a market cycle ` +
|
||||
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`,
|
||||
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,8 +53,8 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
|
||||
color = "info";
|
||||
}
|
||||
|
||||
const but = (
|
||||
<Box>
|
||||
const wrapInTooltip = (ele: React.ReactElement): React.ReactElement => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
@@ -63,12 +64,22 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
{ele}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const but = (
|
||||
<Box>
|
||||
{wrapInTooltip(
|
||||
<span>
|
||||
<Button color={color} disabled={disabled && !n.researched} onClick={research}>
|
||||
{n.text}
|
||||
<Button color={color} disabled={disabled && !n.researched} onClick={research}
|
||||
style={{ width: '100%', textAlign: 'left', justifyContent: 'unset' }}
|
||||
>
|
||||
{n.researched && (<CheckIcon sx={{ mr: 1 }} />)}{n.text}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -76,15 +87,25 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box display="flex">
|
||||
{but}
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText />
|
||||
<Box display="flex" sx={{ border: '1px solid ' + Settings.theme.well }}>
|
||||
{wrapInTooltip(
|
||||
<span style={{ width: '100%' }}>
|
||||
<Button color={color} disabled={disabled && !n.researched} onClick={research} sx={{
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
justifyContent: 'unset',
|
||||
borderColor: Settings.theme.button
|
||||
}}>
|
||||
{n.researched && (<CheckIcon sx={{ mr: 1 }} />)}{n.text}
|
||||
</Button>
|
||||
</span>
|
||||
)}
|
||||
<Button onClick={() => setOpen((old) => !old)} sx={{ borderColor: Settings.theme.button, minWidth: 'fit-content' }}>
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
</Button>
|
||||
</Box>
|
||||
<Collapse in={open} unmountOnExit>
|
||||
<Box m={4}>
|
||||
<Box m={1}>
|
||||
{n.children.map((m) => (
|
||||
<Upgrade key={m.text} division={division} n={m} />
|
||||
))}
|
||||
@@ -108,7 +129,7 @@ export function ResearchModal(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Upgrade division={props.industry} n={researchTree.root} />
|
||||
<Typography>
|
||||
<Typography sx={{ mt: 1 }}>
|
||||
Research points: {props.industry.sciResearch.qty.toFixed(3)}
|
||||
<br />
|
||||
Multipliers from research:
|
||||
|
||||
@@ -61,7 +61,7 @@ export function SmartSupplyModal(props: IProps): React.ReactElement {
|
||||
|
||||
// Create React components for materials
|
||||
const mats = [];
|
||||
for (const matName in props.warehouse.materials) {
|
||||
for (const matName of Object.keys(props.warehouse.materials)) {
|
||||
if (!(props.warehouse.materials[matName] instanceof Material)) continue;
|
||||
if (!Object.keys(division.reqMats).includes(matName)) continue;
|
||||
mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
export function determineCrimeSuccess(p: IPlayer, type: string): boolean {
|
||||
let chance = 0;
|
||||
let found = false;
|
||||
for (const i in Crimes) {
|
||||
for (const i of Object.keys(Crimes)) {
|
||||
const crime = Crimes[i];
|
||||
if (crime.type == type) {
|
||||
chance = crime.successRate(p);
|
||||
@@ -44,7 +44,7 @@ export function findCrime(roughName: string): Crime | null {
|
||||
return Crimes.DealDrugs;
|
||||
} else if (roughName.includes("bond") && roughName.includes("forge")) {
|
||||
return Crimes.BondForgery;
|
||||
} else if (roughName.includes("traffick") && roughName.includes("arms")) {
|
||||
} else if ((roughName.includes("traffic") || roughName.includes("illegal")) && roughName.includes("arms")) {
|
||||
return Crimes.TraffickArms;
|
||||
} else if (roughName.includes("homicide")) {
|
||||
return Crimes.Homicide;
|
||||
@@ -52,7 +52,7 @@ export function findCrime(roughName: string): Crime | null {
|
||||
return Crimes.GrandTheftAuto;
|
||||
} else if (roughName.includes("kidnap")) {
|
||||
return Crimes.Kidnap;
|
||||
} else if (roughName.includes("assassinate") || roughName.includes("assassination")) {
|
||||
} else if (roughName.includes("assassin")) {
|
||||
return Crimes.Assassination;
|
||||
} else if (roughName.includes("heist")) {
|
||||
return Crimes.Heist;
|
||||
|
||||
@@ -21,7 +21,7 @@ export function checkIfConnectedToDarkweb(): void {
|
||||
}
|
||||
|
||||
export function listAllDarkwebItems(): void {
|
||||
for (const key in DarkWebItems) {
|
||||
for (const key of Object.keys(DarkWebItems)) {
|
||||
const item = DarkWebItems[key];
|
||||
|
||||
const cost = Player.getHomeComputer().programs.includes(item.program) ? (
|
||||
@@ -44,7 +44,7 @@ export function buyDarkwebItem(itemName: string): void {
|
||||
// find the program that matches, if any
|
||||
let item: DarkWebItem | null = null;
|
||||
|
||||
for (const key in DarkWebItems) {
|
||||
for (const key of Object.keys(DarkWebItems)) {
|
||||
const i = DarkWebItems[key];
|
||||
if (i.program.toLowerCase() == itemName) {
|
||||
item = i;
|
||||
@@ -93,7 +93,7 @@ export function buyAllDarkwebItems(): void {
|
||||
const itemsToBuy: DarkWebItem[] = [];
|
||||
let cost = 0;
|
||||
|
||||
for (const key in DarkWebItems) {
|
||||
for (const key of Object.keys(DarkWebItems)) {
|
||||
const item = DarkWebItems[key];
|
||||
if (!Player.hasProgram(item.program)) {
|
||||
itemsToBuy.push(item);
|
||||
|
||||
@@ -46,25 +46,25 @@ export function Companies(): React.ReactElement {
|
||||
}
|
||||
|
||||
function tonsOfRepCompanies(): void {
|
||||
for (const c in AllCompanies) {
|
||||
for (const c of Object.keys(AllCompanies)) {
|
||||
AllCompanies[c].playerReputation = bigNumber;
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllRepCompanies(): void {
|
||||
for (const c in AllCompanies) {
|
||||
for (const c of Object.keys(AllCompanies)) {
|
||||
AllCompanies[c].playerReputation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function tonsOfFavorCompanies(): void {
|
||||
for (const c in AllCompanies) {
|
||||
for (const c of Object.keys(AllCompanies)) {
|
||||
AllCompanies[c].favor = bigNumber;
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllFavorCompanies(): void {
|
||||
for (const c in AllCompanies) {
|
||||
for (const c of Object.keys(AllCompanies)) {
|
||||
AllCompanies[c].favor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export function Factions(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function receiveAllInvites(): void {
|
||||
for (const i in AllFaction) {
|
||||
for (const i of Object.keys(AllFaction)) {
|
||||
props.player.receiveInvite(AllFaction[i].name);
|
||||
}
|
||||
}
|
||||
@@ -74,25 +74,25 @@ export function Factions(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function tonsOfRep(): void {
|
||||
for (const i in AllFaction) {
|
||||
for (const i of Object.keys(AllFaction)) {
|
||||
AllFaction[i].playerReputation = bigNumber;
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllRep(): void {
|
||||
for (const i in AllFaction) {
|
||||
for (const i of Object.keys(AllFaction)) {
|
||||
AllFaction[i].playerReputation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function tonsOfFactionFavor(): void {
|
||||
for (const i in AllFaction) {
|
||||
for (const i of Object.keys(AllFaction)) {
|
||||
AllFaction[i].favor = bigNumber;
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllFactionFavor(): void {
|
||||
for (const i in AllFaction) {
|
||||
for (const i of Object.keys(AllFaction)) {
|
||||
AllFaction[i].favor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
@@ -17,6 +17,8 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function General(props: IProps): React.ReactElement {
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
function addMoney(n: number) {
|
||||
return function () {
|
||||
props.player.gainMoney(n, "other");
|
||||
@@ -43,6 +45,10 @@ export function General(props: IProps): React.ReactElement {
|
||||
props.router.toBitVerse(false, false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (error) throw new ReferenceError('Manually thrown error');
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
@@ -81,6 +87,7 @@ export function General(props: IProps): React.ReactElement {
|
||||
<Button onClick={b1tflum3}>Run b1t_flum3.exe</Button>
|
||||
<Button onClick={quickHackW0r1dD43m0n}>Quick w0rld_d34m0n</Button>
|
||||
<Button onClick={hackW0r1dD43m0n}>Hack w0rld_d34m0n</Button>
|
||||
<Button onClick={() => setError(true)}>Throw Error</Button>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ export function Programs(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
function addAllPrograms(): void {
|
||||
for (const i in AllPrograms) {
|
||||
for (const i of Object.keys(AllPrograms)) {
|
||||
if (!props.player.hasProgram(AllPrograms[i].name)) {
|
||||
props.player.getHomeComputer().programs.push(AllPrograms[i].name);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Adjuster } from "./Adjuster";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
@@ -38,6 +39,12 @@ export function Sleeves(props: IProps): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
function sleeveSetStoredCycles(cycles: number): void {
|
||||
for (let i = 0; i < props.player.sleeves.length; ++i) {
|
||||
props.player.sleeves[i].storedCycles = cycles;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
@@ -68,6 +75,18 @@ export function Sleeves(props: IProps): React.ReactElement {
|
||||
<Button onClick={sleeveSyncClearAll}>Clear all</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={3}>
|
||||
<Adjuster
|
||||
label="Stored Cycles"
|
||||
placeholder="cycles"
|
||||
tons={() => sleeveSetStoredCycles(10000000)}
|
||||
add={sleeveSetStoredCycles}
|
||||
subtract={sleeveSetStoredCycles}
|
||||
reset={() => sleeveSetStoredCycles(0)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</AccordionDetails>
|
||||
|
||||
@@ -38,7 +38,7 @@ export function StockMarket(): React.ReactElement {
|
||||
};
|
||||
}
|
||||
|
||||
for (const name in SM) {
|
||||
for (const name of Object.keys(SM)) {
|
||||
if (SM.hasOwnProperty(name)) {
|
||||
const stock = SM[name];
|
||||
if (stock instanceof Stock && match(stock.symbol)) {
|
||||
|
||||
+190
-21
@@ -1,11 +1,18 @@
|
||||
import { Player } from "./Player";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
import { isScriptFilename } from "./Script/isScriptFilename";
|
||||
import { Script } from "./Script/Script";
|
||||
import { removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { IMap } from "./types";
|
||||
import { IMap, IReturnStatus } from "./types";
|
||||
import { GetServer } from "./Server/AllServers";
|
||||
import { resolve } from "cypress/types/bluebird";
|
||||
import { ImportPlayerData, SaveData, saveObject } from "./SaveObject";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { exportScripts } from "./Terminal/commands/download";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { hash } from "./hash/hash";
|
||||
|
||||
export function initElectron(): void {
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
@@ -14,36 +21,81 @@ export function initElectron(): void {
|
||||
(document as any).achievements = [];
|
||||
initWebserver();
|
||||
initAppNotifier();
|
||||
initSaveFunctions();
|
||||
initElectronBridge();
|
||||
}
|
||||
}
|
||||
|
||||
function initWebserver(): void {
|
||||
(document as any).saveFile = function (filename: string, code: string): string {
|
||||
interface IReturnWebStatus extends IReturnStatus {
|
||||
data?: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
}
|
||||
function normalizeFileName(filename: string): string {
|
||||
filename = filename.replace(/\/\/+/g, "/");
|
||||
filename = removeLeadingSlash(filename);
|
||||
if (filename.includes("/")) {
|
||||
filename = "/" + removeLeadingSlash(filename);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
(document as any).getFiles = function (): IReturnWebStatus {
|
||||
const home = GetServer("home");
|
||||
if (home === null) {
|
||||
return {
|
||||
res: false,
|
||||
msg: "Home server does not exist.",
|
||||
};
|
||||
}
|
||||
return {
|
||||
res: true,
|
||||
data: {
|
||||
files: home.scripts.map((script) => ({
|
||||
filename: script.filename,
|
||||
code: script.code,
|
||||
ramUsage: script.ramUsage,
|
||||
})),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
(document as any).deleteFile = function (filename: string): IReturnWebStatus {
|
||||
filename = normalizeFileName(filename);
|
||||
const home = GetServer("home");
|
||||
if (home === null) {
|
||||
return {
|
||||
res: false,
|
||||
msg: "Home server does not exist.",
|
||||
};
|
||||
}
|
||||
return home.removeFile(filename);
|
||||
};
|
||||
|
||||
(document as any).saveFile = function (filename: string, code: string): IReturnWebStatus {
|
||||
filename = normalizeFileName(filename);
|
||||
|
||||
code = Buffer.from(code, "base64").toString();
|
||||
const home = GetServer("home");
|
||||
if (home === null) return "'home' server not found.";
|
||||
if (isScriptFilename(filename)) {
|
||||
//If the current script already exists on the server, overwrite it
|
||||
for (let i = 0; i < home.scripts.length; i++) {
|
||||
if (filename == home.scripts[i].filename) {
|
||||
home.scripts[i].saveScript(Player, filename, code, "home", home.scripts);
|
||||
return "written";
|
||||
}
|
||||
}
|
||||
|
||||
//If the current script does NOT exist, create a new one
|
||||
const script = new Script();
|
||||
script.saveScript(Player, filename, code, "home", home.scripts);
|
||||
home.scripts.push(script);
|
||||
return "written";
|
||||
if (home === null) {
|
||||
return {
|
||||
res: false,
|
||||
msg: "Home server does not exist.",
|
||||
};
|
||||
}
|
||||
|
||||
return "not a script file";
|
||||
const { success, overwritten } = home.writeToScriptFile(Player, filename, code);
|
||||
let script;
|
||||
if (success) {
|
||||
script = home.getScript(filename);
|
||||
}
|
||||
return {
|
||||
res: success,
|
||||
data: {
|
||||
overwritten,
|
||||
ramUsage: script?.ramUsage,
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,6 +119,123 @@ function initAppNotifier(): void {
|
||||
};
|
||||
|
||||
// Will be consumud by the electron wrapper.
|
||||
// @ts-ignore
|
||||
window.appNotifier = funcs;
|
||||
(window as any).appNotifier = funcs;
|
||||
}
|
||||
|
||||
function initSaveFunctions(): void {
|
||||
const funcs = {
|
||||
triggerSave: (): Promise<void> => saveObject.saveGame(true),
|
||||
triggerGameExport: (): void => {
|
||||
try {
|
||||
saveObject.exportGame();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export game.", "error", 2000);
|
||||
}
|
||||
},
|
||||
triggerScriptsExport: (): void => exportScripts("*", Player.getHomeComputer()),
|
||||
getSaveData: (): { save: string; fileName: string } => {
|
||||
return {
|
||||
save: saveObject.getSaveString(Settings.ExcludeRunningScriptsFromSave),
|
||||
fileName: saveObject.getSaveFileName(),
|
||||
};
|
||||
},
|
||||
getSaveInfo: async (base64save: string): Promise<ImportPlayerData | undefined> => {
|
||||
try {
|
||||
const data = await saveObject.getImportDataFromString(base64save);
|
||||
return data.playerData;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
},
|
||||
pushSaveData: (base64save: string, automatic = false): void => Router.toImportSave(base64save, automatic),
|
||||
};
|
||||
|
||||
// Will be consumud by the electron wrapper.
|
||||
(window as any).appSaveFns = funcs;
|
||||
}
|
||||
|
||||
function initElectronBridge(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.receive("get-save-data-request", () => {
|
||||
const data = (window as any).appSaveFns.getSaveData();
|
||||
bridge.send("get-save-data-response", data);
|
||||
});
|
||||
bridge.receive("get-save-info-request", async (save: string) => {
|
||||
const data = await (window as any).appSaveFns.getSaveInfo(save);
|
||||
bridge.send("get-save-info-response", data);
|
||||
});
|
||||
bridge.receive("push-save-request", ({ save, automatic = false }: { save: string; automatic: boolean }) => {
|
||||
(window as any).appSaveFns.pushSaveData(save, automatic);
|
||||
});
|
||||
bridge.receive("trigger-save", () => {
|
||||
return (window as any).appSaveFns
|
||||
.triggerSave()
|
||||
.then(() => {
|
||||
bridge.send("save-completed");
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not save game.", "error", 2000);
|
||||
});
|
||||
});
|
||||
bridge.receive("trigger-game-export", () => {
|
||||
try {
|
||||
(window as any).appSaveFns.triggerGameExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export game.", "error", 2000);
|
||||
}
|
||||
});
|
||||
bridge.receive("trigger-scripts-export", () => {
|
||||
try {
|
||||
(window as any).appSaveFns.triggerScriptsExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export scripts.", "error", 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function pushGameSaved(data: SaveData): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-game-saved", data);
|
||||
}
|
||||
|
||||
export function pushGameReady(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
if (!bridge) return;
|
||||
|
||||
// Send basic information to the electron wrapper
|
||||
bridge.send("push-game-ready", {
|
||||
player: {
|
||||
identifier: Player.identifier,
|
||||
playtime: Player.totalPlaytime,
|
||||
lastSave: Player.lastSave,
|
||||
},
|
||||
game: {
|
||||
version: CONSTANTS.VersionString,
|
||||
hash: hash(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function pushImportResult(wasImported: boolean): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-import-result", { wasImported });
|
||||
pushDisableRestore();
|
||||
}
|
||||
|
||||
export function pushDisableRestore(): void {
|
||||
const bridge = (window as any).electronBridge as any;
|
||||
if (!bridge) return;
|
||||
|
||||
bridge.send("push-disable-restore", { duration: 1000 * 60 });
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ export function joinFaction(faction: Faction): void {
|
||||
const factionInfo = faction.getInfo();
|
||||
|
||||
//Determine what factions you are banned from now that you have joined this faction
|
||||
for (const i in factionInfo.enemies) {
|
||||
const enemy = factionInfo.enemies[i];
|
||||
for (const enemy of factionInfo.enemies) {
|
||||
if (Factions[enemy] instanceof Faction) {
|
||||
Factions[enemy].isBanned = true;
|
||||
}
|
||||
@@ -121,7 +120,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
||||
}
|
||||
}
|
||||
|
||||
for (const name in Augmentations) {
|
||||
for (const name of Object.keys(Augmentations)) {
|
||||
if (Augmentations.hasOwnProperty(name)) {
|
||||
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]];
|
||||
}
|
||||
@@ -170,7 +169,7 @@ export function getNextNeurofluxLevel(): number {
|
||||
}
|
||||
|
||||
export function processPassiveFactionRepGain(numCycles: number): void {
|
||||
for (const name in Factions) {
|
||||
for (const name of Object.keys(Factions)) {
|
||||
if (name === Player.currentWorkFactionName) continue;
|
||||
if (!Factions.hasOwnProperty(name)) continue;
|
||||
const faction = Factions[name];
|
||||
|
||||
@@ -34,7 +34,7 @@ export function factionExists(name: string): boolean {
|
||||
}
|
||||
|
||||
export function initFactions(): void {
|
||||
for (const name in FactionInfos) {
|
||||
for (const name of Object.keys(FactionInfos)) {
|
||||
resetFaction(new Faction(name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
function getAugs(): string[] {
|
||||
if (isPlayersGang) {
|
||||
const augs: string[] = [];
|
||||
for (const augName in Augmentations) {
|
||||
for (const augName of Object.keys(Augmentations)) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
|
||||
if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue;
|
||||
const aug = Augmentations[augName];
|
||||
|
||||
@@ -77,7 +77,7 @@ export function DonateOption(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper sx={{ my: 1, p: 1, width: "100%" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Status />
|
||||
{props.disabled ? (
|
||||
<Typography>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Container from "@mui/material/Container";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Table, TableCell } from "../../ui/React/Table";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { Factions } from "../Factions";
|
||||
import { Faction } from "../Faction";
|
||||
import { joinFaction } from "../FactionHelpers";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import Link from "@mui/material/Link";
|
||||
import Button from "@mui/material/Button";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { Table, TableCell } from "../../ui/React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import { Factions } from "../Factions";
|
||||
|
||||
export const InvitationsSeen: string[] = [];
|
||||
|
||||
@@ -48,42 +48,67 @@ export function FactionsRoot(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container disableGutters maxWidth="md" sx={{ mx: 0, mb: 10 }}>
|
||||
<Typography variant="h4">Factions</Typography>
|
||||
<Typography>Lists all factions you have joined</Typography>
|
||||
<br />
|
||||
<Box display="flex" flexDirection="column">
|
||||
{props.player.factions.map((faction: string) => (
|
||||
<Link key={faction} variant="h6" onClick={() => openFaction(Factions[faction])}>
|
||||
{faction}
|
||||
</Link>
|
||||
))}
|
||||
</Box>
|
||||
<br />
|
||||
{props.player.factionInvitations.length > 0 && (
|
||||
<>
|
||||
<Typography variant="h5" color="primary">
|
||||
Outstanding Faction Invitations
|
||||
</Typography>
|
||||
<Typography>
|
||||
Lists factions you have been invited to. You can accept these faction invitations at any time.
|
||||
</Typography>
|
||||
<Table size="small" padding="none">
|
||||
<Typography mb={4}>
|
||||
Throughout the game you may receive invitations from factions. There are many different factions, and each
|
||||
faction has different criteria for determining its potential members. Joining a faction and furthering its cause
|
||||
is crucial to progressing in the game and unlocking endgame content.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h5" color="primary" mt={2} mb={1}>
|
||||
Factions you have joined:
|
||||
</Typography>
|
||||
{(props.player.factions.length > 0 && (
|
||||
<Paper sx={{ my: 1, p: 1, pb: 0, display: "inline-block" }}>
|
||||
<Table padding="none">
|
||||
<TableBody>
|
||||
{props.player.factionInvitations.map((faction: string) => (
|
||||
{props.player.factions.map((faction: string) => (
|
||||
<TableRow key={faction}>
|
||||
<TableCell>
|
||||
<Typography noWrap>{faction}</Typography>
|
||||
<Typography noWrap mb={1}>
|
||||
{faction}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Button onClick={(e) => acceptInvitation(e, faction)}>Join!</Button>
|
||||
<Box ml={1} mb={1}>
|
||||
<Button onClick={() => openFaction(Factions[faction])}>Details</Button>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</Paper>
|
||||
)) || <Typography>You haven't joined any factions.</Typography>}
|
||||
<Typography variant="h5" color="primary" mt={4} mb={1}>
|
||||
Outstanding Faction Invitations
|
||||
</Typography>
|
||||
<Typography mb={1}>
|
||||
Factions you have been invited to. You can accept these faction invitations at any time:
|
||||
</Typography>
|
||||
{(props.player.factionInvitations.length > 0 && (
|
||||
<Paper sx={{ my: 1, mb: 4, p: 1, pb: 0, display: "inline-block" }}>
|
||||
<Table padding="none">
|
||||
<TableBody>
|
||||
{props.player.factionInvitations.map((faction: string) => (
|
||||
<TableRow key={faction}>
|
||||
<TableCell>
|
||||
<Typography noWrap mb={1}>
|
||||
{faction}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Box ml={1} mb={1}>
|
||||
<Button onClick={(e) => acceptInvitation(e, faction)}>Join!</Button>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
)) || <Typography>You have no outstanding faction invites.</Typography>}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ type IProps = {
|
||||
export function Option(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ my: 1, p: 1, width: "100%" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Button onClick={props.onClick}>{props.buttonText}</Button>
|
||||
<Typography>{props.infoText}</Typography>
|
||||
</Paper>
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ export class Gang implements IGang {
|
||||
|
||||
// Process power first
|
||||
const gangName = this.facName;
|
||||
for (const name in AllGangs) {
|
||||
for (const name of Object.keys(AllGangs)) {
|
||||
if (AllGangs.hasOwnProperty(name)) {
|
||||
if (name == gangName) {
|
||||
AllGangs[name].power += this.calculatePower();
|
||||
|
||||
@@ -31,7 +31,7 @@ export function AscensionModal(props: IProps): React.ReactElement {
|
||||
props.onAscend();
|
||||
const res = gang.ascendMember(props.member);
|
||||
dialogBoxCreate(
|
||||
<Typography>
|
||||
<>
|
||||
You ascended {props.member.name}!<br />
|
||||
<br />
|
||||
Your gang lost {numeralWrapper.formatRespect(res.respect)} respect.
|
||||
@@ -51,7 +51,7 @@ export function AscensionModal(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
Charisma: x{numeralWrapper.format(res.cha, "0.000")}
|
||||
<br />
|
||||
</Typography>,
|
||||
</>
|
||||
);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
@@ -2,20 +2,27 @@
|
||||
* React Component for the popup that manages gang members upgrades
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
||||
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { useGang } from "./Context";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { UpgradeType } from "../data/upgrades";
|
||||
import { use } from "../../ui/Context";
|
||||
import { generateTableRow } from "./GangMemberStats";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { MenuItem, Table, TableBody, TextField } from "@mui/material";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMemberUpgrades } from "../GangMemberUpgrades";
|
||||
import { GangMemberUpgrade } from "../GangMemberUpgrade";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { UpgradeType } from "../data/upgrades";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { characterOverviewStyles as useStyles } from "../../ui/React/CharacterOverview";
|
||||
|
||||
interface INextRevealProps {
|
||||
upgrades: string[];
|
||||
@@ -46,12 +53,10 @@ function NextReveal(props: INextRevealProps): React.ReactElement {
|
||||
function PurchasedUpgrade({ upgName }: { upgName: string }): React.ReactElement {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
return (
|
||||
<Paper sx={{ mx: 1, p: 1 }}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: upg.desc }} />}>
|
||||
<Typography>{upg.name}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: upg.desc }} />}>
|
||||
<Typography>{upg.name}</Typography>
|
||||
</Tooltip>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -72,8 +77,8 @@ function UpgradeButton(props: IUpgradeButtonProps): React.ReactElement {
|
||||
return (
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: props.upg.desc }} />}>
|
||||
<span>
|
||||
<Typography>{props.upg.name}</Typography>
|
||||
<Button onClick={onClick}>
|
||||
<Button onClick={onClick} sx={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%' }}>
|
||||
<Typography sx={{ display: 'block' }}>{props.upg.name}</Typography>
|
||||
<Money money={gang.getUpgradeCost(props.upg)} />
|
||||
</Button>
|
||||
</span>
|
||||
@@ -86,12 +91,16 @@ interface IPanelProps {
|
||||
}
|
||||
|
||||
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const gang = useGang();
|
||||
const player = use.Player();
|
||||
const setRerender = useState(false)[1];
|
||||
const [currentCategory, setCurrentCategory] = useState("Weapons");
|
||||
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
|
||||
return Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
@@ -103,12 +112,26 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
}
|
||||
|
||||
const onChange = (event: SelectChangeEvent<string>): void => {
|
||||
setCurrentCategory(event.target.value);
|
||||
rerender()
|
||||
}
|
||||
|
||||
const weaponUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Weapon);
|
||||
const armorUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Armor);
|
||||
const vehicleUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Vehicle);
|
||||
const rootkitUpgrades = filterUpgrades(props.member.upgrades, UpgradeType.Rootkit);
|
||||
const augUpgrades = filterUpgrades(props.member.augmentations, UpgradeType.Augmentation);
|
||||
|
||||
const categories: { [key: string]: (GangMemberUpgrade[] | UpgradeType)[] } = {
|
||||
'Weapons': [weaponUpgrades, UpgradeType.Weapon],
|
||||
'Armor': [armorUpgrades, UpgradeType.Armor],
|
||||
'Vehicles': [vehicleUpgrades, UpgradeType.Vehicle],
|
||||
'Rootkits': [rootkitUpgrades, UpgradeType.Rootkit],
|
||||
'Augmentations': [augUpgrades, UpgradeType.Augmentation]
|
||||
};
|
||||
|
||||
const asc = {
|
||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||
str: props.member.calculateAscensionMult(props.member.str_asc_points),
|
||||
@@ -119,26 +142,89 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
};
|
||||
return (
|
||||
<Paper>
|
||||
<Typography variant="h5" color="primary">
|
||||
{props.member.name} ({props.member.task})
|
||||
</Typography>
|
||||
<Typography>
|
||||
Hack: {props.member.hack} (x
|
||||
{formatNumber(props.member.hack_mult * asc.hack, 2)})<br />
|
||||
Str: {props.member.str} (x
|
||||
{formatNumber(props.member.str_mult * asc.str, 2)})<br />
|
||||
Def: {props.member.def} (x
|
||||
{formatNumber(props.member.def_mult * asc.def, 2)})<br />
|
||||
Dex: {props.member.dex} (x
|
||||
{formatNumber(props.member.dex_mult * asc.dex, 2)})<br />
|
||||
Agi: {props.member.agi} (x
|
||||
{formatNumber(props.member.agi_mult * asc.agi, 2)})<br />
|
||||
Cha: {props.member.cha} (x
|
||||
{formatNumber(props.member.cha_mult * asc.cha, 2)})
|
||||
</Typography>
|
||||
<Box display="flex" flexWrap="wrap">
|
||||
<Typography>Purchased Upgrades: </Typography>
|
||||
<br />
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', m: 1, gap: 1 }}>
|
||||
<span>
|
||||
<Typography variant="h5" color="primary">
|
||||
{props.member.name} ({props.member.task})
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Hk: x{numeralWrapper.formatMultiplier(props.member.hack_mult * asc.hack)}(x
|
||||
{numeralWrapper.formatMultiplier(props.member.hack_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.hack)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
St: x{numeralWrapper.formatMultiplier(props.member.str_mult * asc.str)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.str_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.str)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Df: x{numeralWrapper.formatMultiplier(props.member.def_mult * asc.def)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.def_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.def)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Dx: x{numeralWrapper.formatMultiplier(props.member.dex_mult * asc.dex)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.dex_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.dex)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Ag: x{numeralWrapper.formatMultiplier(props.member.agi_mult * asc.agi)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.agi_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.agi)}{" "}
|
||||
Asc)
|
||||
<br />
|
||||
Ch: x{numeralWrapper.formatMultiplier(props.member.cha_mult * asc.cha)}
|
||||
(x{numeralWrapper.formatMultiplier(props.member.cha_mult)} Eq, x{numeralWrapper.formatMultiplier(asc.cha)}{" "}
|
||||
Asc)
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Table>
|
||||
<TableBody>
|
||||
{generateTableRow("Hacking", props.member.hack, props.member.hack_exp, Settings.theme.hack, classes)}
|
||||
{generateTableRow("Strength", props.member.str, props.member.str_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Defense", props.member.def, props.member.def_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Dexterity", props.member.dex, props.member.dex_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Agility", props.member.agi, props.member.agi_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Charisma", props.member.cha, props.member.cha_exp, Settings.theme.cha, classes)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Tooltip>
|
||||
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<Select onChange={onChange} value={currentCategory} sx={{ width: '100%', mb: 1 }}>
|
||||
{Object.keys(categories).map((k, i) => (
|
||||
<MenuItem key={i + 1} value={k}>
|
||||
<Typography variant="h6">{k}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<Box sx={{ width: '100%' }}>
|
||||
{(categories[currentCategory][0] as GangMemberUpgrade[]).length === 0 && (
|
||||
<Typography>
|
||||
All upgrades owned!
|
||||
</Typography>
|
||||
)}
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr' }}>
|
||||
{(categories[currentCategory][0] as GangMemberUpgrade[]).map((upg) => (
|
||||
<UpgradeButton
|
||||
key={upg.name}
|
||||
rerender={rerender}
|
||||
member={props.member}
|
||||
upg={upg}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<NextReveal
|
||||
type={categories[currentCategory][1] as UpgradeType}
|
||||
upgrades={props.member.upgrades}
|
||||
/>
|
||||
</Box>
|
||||
</span>
|
||||
</Box>
|
||||
|
||||
<Typography sx={{ mx: 1 }}>Purchased Upgrades: </Typography>
|
||||
<Box display="grid" sx={{ gridTemplateColumns: 'repeat(4, 1fr)', m: 1 }}>
|
||||
{props.member.upgrades.map((upg: string) => (
|
||||
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||
))}
|
||||
@@ -146,59 +232,22 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
<PurchasedUpgrade key={upg} upgName={upg} />
|
||||
))}
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-around">
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Weapons
|
||||
</Typography>
|
||||
{weaponUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Weapon} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Armor
|
||||
</Typography>
|
||||
{armorUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Armor} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Vehicles
|
||||
</Typography>
|
||||
{vehicleUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Vehicle} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Rootkits
|
||||
</Typography>
|
||||
{rootkitUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Rootkit} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h6" color="primary">
|
||||
Augmentations
|
||||
</Typography>
|
||||
{augUpgrades.map((upg) => (
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
<NextReveal type={UpgradeType.Augmentation} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Paper >
|
||||
);
|
||||
}
|
||||
|
||||
export function EquipmentsSubpage(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
|
||||
const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setFilter(event.target.value.toLowerCase());
|
||||
}
|
||||
|
||||
const members = gang.members
|
||||
.filter((member) => member && member.name.toLowerCase().includes(filter));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
@@ -209,11 +258,26 @@ export function EquipmentsSubpage(): React.ReactElement {
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Discount: -{numeralWrapper.formatPercentage(1 - 1 / gang.getDiscount())}</Typography>
|
||||
<Typography sx={{ m: 1 }}>Discount: -{numeralWrapper.formatPercentage(1 - 1 / gang.getDiscount())}</Typography>
|
||||
</Tooltip>
|
||||
{gang.members.map((member: GangMember) => (
|
||||
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||
))}
|
||||
|
||||
<TextField
|
||||
value={filter}
|
||||
onChange={handleFilterChange}
|
||||
autoFocus
|
||||
InputProps={{
|
||||
startAdornment: <SearchIcon />,
|
||||
spellCheck: false
|
||||
}}
|
||||
placeholder="Filter by member name"
|
||||
sx={{ m: 1, width: '15%' }}
|
||||
/>
|
||||
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: 'fit-content' }}>
|
||||
{members.map((member: GangMember) => (
|
||||
<GangMemberUpgradePanel key={member.name} member={member} />
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* React Component for a gang member on the management subpage.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { GangMemberAccordionContent } from "./GangMemberAccordionContent";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberAccordion(props: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(true);
|
||||
return (
|
||||
<Box component={Paper}>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText primary={<Typography>{props.member.name}</Typography>} />
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
<Collapse in={open} unmountOnExit>
|
||||
<Box sx={{ mx: 4 }}>
|
||||
<GangMemberAccordionContent member={props.member} />
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* React Component for the content of the accordion of gang members on the
|
||||
* management subpage.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { GangMemberStats } from "./GangMemberStats";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
import { TaskDescription } from "./TaskDescription";
|
||||
import { GangMember } from "../GangMember";
|
||||
import Grid from "@mui/material/Grid";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberAccordionContent(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={4}>
|
||||
<GangMemberStats onAscend={() => setRerender((old) => !old)} member={props.member} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TaskSelector onTaskChange={() => setRerender((old) => !old)} member={props.member} />
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<TaskDescription member={props.member} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* React Component for a gang member on the management subpage.
|
||||
*/
|
||||
import React from "react";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { GangMemberCardContent } from "./GangMemberCardContent";
|
||||
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberCard(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Box component={Paper} sx={{ width: 'auto' }}>
|
||||
<Box sx={{ m: 1 }}>
|
||||
<ListItemText primary={<b>{props.member.name}</b>} />
|
||||
<GangMemberCardContent member={props.member} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* React Component for the content of the accordion of gang members on the
|
||||
* management subpage.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { GangMemberStats } from "./GangMemberStats";
|
||||
import { TaskSelector } from "./TaskSelector";
|
||||
import { AscensionModal } from "./AscensionModal";
|
||||
|
||||
import { Box } from "@mui/system";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
|
||||
import { GangMember } from "../GangMember";
|
||||
import { StaticModal } from "../../ui/React/StaticModal";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
}
|
||||
|
||||
export function GangMemberCardContent(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
const [helpOpen, setHelpOpen] = useState(false);
|
||||
const [ascendOpen, setAscendOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.member.canAscend() && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', my: 1 }}>
|
||||
<Button onClick={() => setAscendOpen(true)} style={{ flexGrow: 1, borderRightWidth: 0 }}>Ascend</Button>
|
||||
<AscensionModal
|
||||
open={ascendOpen}
|
||||
onClose={() => setAscendOpen(false)}
|
||||
member={props.member}
|
||||
onAscend={() => setRerender((old) => !old)}
|
||||
/>
|
||||
<Button onClick={() => setHelpOpen(true)} style={{ width: 'fit-content', borderLeftWidth: 0 }}>
|
||||
<HelpIcon />
|
||||
</Button>
|
||||
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
|
||||
<Typography>
|
||||
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their
|
||||
stat multipliers.
|
||||
<br />
|
||||
<br />
|
||||
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp
|
||||
they have.
|
||||
<br />
|
||||
<br />
|
||||
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect
|
||||
equal to the total respect earned by the member.
|
||||
</Typography>
|
||||
</StaticModal>
|
||||
</Box>
|
||||
)}
|
||||
<Box display="grid" sx={{ gridTemplateColumns: '1fr 1fr', width: '100%', gap: 1 }}>
|
||||
<GangMemberStats member={props.member} />
|
||||
<TaskSelector onTaskChange={() => setRerender((old) => !old)} member={props.member} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,23 +2,63 @@
|
||||
* React Component for the list of gang members on the management subpage.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { GangMemberAccordion } from "./GangMemberAccordion";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { GangMemberCard } from "./GangMemberCard";
|
||||
import { RecruitButton } from "./RecruitButton";
|
||||
import { useGang } from "./Context";
|
||||
|
||||
import { Box, TextField } from "@mui/material";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
import { GangMember } from "../GangMember";
|
||||
import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||
|
||||
export function GangMemberList(): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const setRerender = useState(false)[1];
|
||||
const [filter, setFilter] = useState("");
|
||||
const [ascendOnly, setAscendOnly] = useState(false);
|
||||
|
||||
const handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setFilter(event.target.value.toLowerCase());
|
||||
}
|
||||
|
||||
const members = gang.members
|
||||
.filter((member) => member && member.name.toLowerCase().includes(filter))
|
||||
.filter((member) => {
|
||||
if (ascendOnly) return member.canAscend();
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<RecruitButton onRecruit={() => setRerender((old) => !old)} />
|
||||
<ul>
|
||||
{gang.members.map((member: GangMember) => (
|
||||
<GangMemberAccordion key={member.name} member={member} />
|
||||
<TextField
|
||||
value={filter}
|
||||
onChange={handleFilterChange}
|
||||
autoFocus
|
||||
InputProps={{
|
||||
startAdornment: <SearchIcon />,
|
||||
spellCheck: false
|
||||
}}
|
||||
placeholder="Filter by member name"
|
||||
sx={{ m: 1, width: '15%' }}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={ascendOnly}
|
||||
onChange={(newValue) => (setAscendOnly(newValue))}
|
||||
text="Show only ascendable"
|
||||
tooltip={
|
||||
<>
|
||||
Filter the members list by whether or not the member
|
||||
can be ascended.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Box display="grid" sx={{ gridTemplateColumns: 'repeat(2, 1fr)' }}>
|
||||
{members.map((member: GangMember) => (
|
||||
<GangMemberCard key={member.name} member={member} />
|
||||
))}
|
||||
</ul>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,26 +2,53 @@
|
||||
* React Component for the first part of a gang member details.
|
||||
* Contains skills and exp.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { AscensionModal } from "./AscensionModal";
|
||||
import React from "react";
|
||||
import { useGang } from "./Context";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Button from "@mui/material/Button";
|
||||
import { StaticModal } from "../../ui/React/StaticModal";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from "@mui/material";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { characterOverviewStyles as useStyles } from "../../ui/React/CharacterOverview";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
onAscend: () => void;
|
||||
}
|
||||
|
||||
export const generateTableRow = (
|
||||
name: string,
|
||||
level: number,
|
||||
exp: number,
|
||||
color: string,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
classes: any
|
||||
): React.ReactElement => {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell classes={{ root: classes.cellNone }}>
|
||||
<Typography style={{ color: color }}>{name}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography style={{ color: color }}>
|
||||
{formatNumber(level, 0)} ({numeralWrapper.formatExp(exp)} exp)
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export function GangMemberStats(props: IProps): React.ReactElement {
|
||||
const [helpOpen, setHelpOpen] = useState(false);
|
||||
const [ascendOpen, setAscendOpen] = useState(false);
|
||||
const classes = useStyles();
|
||||
|
||||
const asc = {
|
||||
hack: props.member.calculateAscensionMult(props.member.hack_asc_points),
|
||||
@@ -32,6 +59,16 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
||||
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
||||
};
|
||||
|
||||
|
||||
|
||||
const gang = useGang();
|
||||
const data = [
|
||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
||||
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(gang))} / sec`],
|
||||
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(gang))} / sec`],
|
||||
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
@@ -63,50 +100,32 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Hacking: {formatNumber(props.member.hack, 0)} ({numeralWrapper.formatExp(props.member.hack_exp)} exp)
|
||||
<br />
|
||||
Strength: {formatNumber(props.member.str, 0)} ({numeralWrapper.formatExp(props.member.str_exp)} exp)
|
||||
<br />
|
||||
Defense: {formatNumber(props.member.def, 0)} ({numeralWrapper.formatExp(props.member.def_exp)} exp)
|
||||
<br />
|
||||
Dexterity: {formatNumber(props.member.dex, 0)} ({numeralWrapper.formatExp(props.member.dex_exp)} exp)
|
||||
<br />
|
||||
Agility: {formatNumber(props.member.agi, 0)} ({numeralWrapper.formatExp(props.member.agi_exp)} exp)
|
||||
<br />
|
||||
Charisma: {formatNumber(props.member.cha, 0)} ({numeralWrapper.formatExp(props.member.cha_exp)} exp)
|
||||
<br />
|
||||
</Typography>
|
||||
<Table sx={{ display: 'table', mb: 1, width: '100%' }}>
|
||||
<TableBody>
|
||||
{generateTableRow("Hacking", props.member.hack, props.member.hack_exp, Settings.theme.hack, classes)}
|
||||
{generateTableRow("Strength", props.member.str, props.member.str_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Defense", props.member.def, props.member.def_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Dexterity", props.member.dex, props.member.dex_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Agility", props.member.agi, props.member.agi_exp, Settings.theme.combat, classes)}
|
||||
{generateTableRow("Charisma", props.member.cha, props.member.cha_exp, Settings.theme.cha, classes)}
|
||||
<TableRow>
|
||||
<TableCell classes={{ root: classes.cellNone }}>
|
||||
<br />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{data.map(([a, b]) => (
|
||||
<TableRow key={a.toString() + b.toString()}>
|
||||
<TableCell classes={{ root: classes.cellNone }}>
|
||||
<Typography>{a}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography>{b}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Tooltip>
|
||||
<br />
|
||||
{props.member.canAscend() && (
|
||||
<>
|
||||
<Button onClick={() => setAscendOpen(true)}>Ascend</Button>
|
||||
<AscensionModal
|
||||
open={ascendOpen}
|
||||
onClose={() => setAscendOpen(false)}
|
||||
member={props.member}
|
||||
onAscend={props.onAscend}
|
||||
/>
|
||||
<IconButton onClick={() => setHelpOpen(true)}>
|
||||
<HelpIcon />
|
||||
</IconButton>
|
||||
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
|
||||
<Typography>
|
||||
Ascending a Gang Member resets the member's progress and stats in exchange for a permanent boost to their
|
||||
stat multipliers.
|
||||
<br />
|
||||
<br />
|
||||
The additional stat multiplier that the Gang Member gains upon ascension is based on the amount of exp
|
||||
they have.
|
||||
<br />
|
||||
<br />
|
||||
Upon ascension, the member will lose all of its non-Augmentation Equipment and your gang will lose respect
|
||||
equal to the total respect earned by the member.
|
||||
</Typography>
|
||||
</StaticModal>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React, { useState } from "react";
|
||||
import { RecruitModal } from "./RecruitModal";
|
||||
import { GangConstants } from "../data/Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { useGang } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
@@ -24,18 +24,20 @@ export function RecruitButton(props: IProps): React.ReactElement {
|
||||
if (!gang.canRecruitMember()) {
|
||||
const respect = gang.getRespectNeededToRecruitMember();
|
||||
return (
|
||||
<Box display="flex" alignItems="center">
|
||||
<Button sx={{ mx: 1 }} disabled>
|
||||
<Box display="flex" alignItems="center" sx={{ mx: 1 }}>
|
||||
<Button disabled>
|
||||
Recruit Gang Member
|
||||
</Button>
|
||||
<Typography>{formatNumber(respect, 2)} respect needed to recruit next member</Typography>
|
||||
<Typography sx={{ ml: 1 }}>{numeralWrapper.formatRespect(respect)} respect needed to recruit next member</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
|
||||
<Box sx={{ mx: 1 }}>
|
||||
<Button onClick={() => setOpen(true)}>Recruit Gang Member</Button>
|
||||
</Box>
|
||||
<RecruitModal open={open} onClose={() => setOpen(false)} onRecruit={props.onRecruit} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
* the task selector as well as some stats.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { useGang } from "./Context";
|
||||
import { GangMember } from "../GangMember";
|
||||
import { TaskDescription } from "./TaskDescription";
|
||||
|
||||
import { Box } from "@mui/material";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
|
||||
import { GangMember } from "../GangMember";
|
||||
|
||||
interface IProps {
|
||||
member: GangMember;
|
||||
onTaskChange: () => void;
|
||||
@@ -29,16 +30,9 @@ export function TaskSelector(props: IProps): React.ReactElement {
|
||||
|
||||
const tasks = gang.getAllTaskNames();
|
||||
|
||||
const data = [
|
||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
||||
[`Respect:`, `${numeralWrapper.formatRespect(5 * props.member.calculateRespectGain(gang))} / sec`],
|
||||
[`Wanted Level:`, `${numeralWrapper.formatWanted(5 * props.member.calculateWantedLevelGain(gang))} / sec`],
|
||||
[`Total Respect:`, `${numeralWrapper.formatRespect(props.member.earnedRespect)}`],
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Select onChange={onChange} value={currentTask}>
|
||||
<Box>
|
||||
<Select onChange={onChange} value={currentTask} sx={{ width: '100%' }}>
|
||||
<MenuItem key={0} value={"Unassigned"}>
|
||||
Unassigned
|
||||
</MenuItem>
|
||||
@@ -48,8 +42,7 @@ export function TaskSelector(props: IProps): React.ReactElement {
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<StatsTable rows={data} />
|
||||
</>
|
||||
<TaskDescription member={props.member} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,9 +34,9 @@ export function calculateHackingExpGain(server: Server, player: IPlayer): number
|
||||
server.baseDifficulty = server.hackDifficulty;
|
||||
}
|
||||
let expGain = baseExpGain;
|
||||
expGain += server.baseDifficulty * player.hacking_exp_mult * diffFactor;
|
||||
expGain += server.baseDifficulty * diffFactor;
|
||||
|
||||
return expGain * BitNodeMultipliers.HackExpGain;
|
||||
return expGain * player.hacking_exp_mult * BitNodeMultipliers.HackExpGain;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ export class HashManager {
|
||||
upgrades: IMap<number> = {};
|
||||
|
||||
constructor() {
|
||||
for (const name in HashUpgrades) {
|
||||
for (const name of Object.keys(HashUpgrades)) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export class HashManager {
|
||||
}
|
||||
|
||||
prestige(): void {
|
||||
for (const name in HashUpgrades) {
|
||||
for (const name of Object.keys(HashUpgrades)) {
|
||||
this.upgrades[name] = 0;
|
||||
}
|
||||
this.hashes = 0;
|
||||
|
||||
@@ -19,7 +19,7 @@ export const Literatures: IMap<Literature> = {};
|
||||
"money on a server, and grow() increases the amount of money on a server by some percentage (multiplicatively)<br><br>" +
|
||||
"-Because hack() and grow() work by percentages, they are more effective if the target server has a high amount of money. " +
|
||||
"Therefore, you should try to increase the amount of money on a server (using grow()) to a certain amount before hacking it. Two " +
|
||||
"import Netscript functions for this are getServerMoneyAvailable() and getServerMaxMoney()<br><br>" +
|
||||
"important Netscript functions for this are getServerMoneyAvailable() and getServerMaxMoney()<br><br>" +
|
||||
"-Keep security level low. Security level affects everything when hacking. Two important Netscript functions " +
|
||||
"for this are getServerSecurityLevel() and getServerMinSecurityLevel()<br><br>" +
|
||||
"-Purchase additional servers by visiting 'Alpha Enterprises' in the city. They are relatively cheap " +
|
||||
|
||||
@@ -220,7 +220,7 @@ for (const metadata of LocationsMetadata) {
|
||||
const cityName = loc.city;
|
||||
if (cityName === null) {
|
||||
// Generic location, add to all cities
|
||||
for (const city in Cities) {
|
||||
for (const city of Object.keys(Cities)) {
|
||||
Cities[city].addLocation(loc.name);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -39,7 +39,6 @@ export function ApplyToJobButton(props: IProps): React.ReactElement {
|
||||
<Tooltip title={<span dangerouslySetInnerHTML={{ __html: getJobRequirementTooltip() }}></span>}>
|
||||
<Button onClick={props.onClick}>{props.text}</Button>
|
||||
</Tooltip>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import Button from "@mui/material/Button";
|
||||
import { Blackjack } from "../../Casino/Blackjack";
|
||||
import { Blackjack, DECK_COUNT } from "../../Casino/Blackjack";
|
||||
import { CoinFlip } from "../../Casino/CoinFlip";
|
||||
import { Roulette } from "../../Casino/Roulette";
|
||||
import { SlotMachine } from "../../Casino/SlotMachine";
|
||||
@@ -38,7 +38,7 @@ export function CasinoLocation(props: IProps): React.ReactElement {
|
||||
<Button onClick={() => updateGame(GameType.Coin)}>Play coin flip</Button>
|
||||
<Button onClick={() => updateGame(GameType.Slots)}>Play slots</Button>
|
||||
<Button onClick={() => updateGame(GameType.Roulette)}>Play roulette</Button>
|
||||
<Button onClick={() => updateGame(GameType.Blackjack)}>Play blackjack</Button>
|
||||
<Button onClick={() => updateGame(GameType.Blackjack)}>Play blackjack ({DECK_COUNT} decks)</Button>
|
||||
</Box>
|
||||
)}
|
||||
{game !== GameType.None && (
|
||||
|
||||
@@ -132,10 +132,10 @@ function ASCIICity(props: IProps): React.ReactElement {
|
||||
|
||||
const elems: JSX.Element[] = [];
|
||||
const lines = props.city.asciiArt.split("\n");
|
||||
for (const i in lines) {
|
||||
for (const line of lines) {
|
||||
elems.push(
|
||||
<Typography key={i} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
||||
{lineElems(lines[i])}
|
||||
<Typography key={line} sx={{ lineHeight: "1em", whiteSpace: "pre" }}>
|
||||
{lineElems(line)}
|
||||
</Typography>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ export function GymLocation(props: IProps): React.ReactElement {
|
||||
|
||||
function train(stat: string): void {
|
||||
const loc = props.loc;
|
||||
props.p.startClass(props.router, calculateCost(), loc.expMult, stat);
|
||||
props.p.startClass(calculateCost(), loc.expMult, stat);
|
||||
props.p.startFocusing();
|
||||
props.router.toWork();
|
||||
}
|
||||
|
||||
function trainStrength(): void {
|
||||
@@ -61,15 +63,12 @@ export function GymLocation(props: IProps): React.ReactElement {
|
||||
<Button onClick={trainStrength}>
|
||||
Train Strength (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<br />
|
||||
<Button onClick={trainDefense}>
|
||||
Train Defense (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<br />
|
||||
<Button onClick={trainDexterity}>
|
||||
Train Dexterity (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
<br />
|
||||
<Button onClick={trainAgility}>
|
||||
Train Agility (<Money money={cost} player={props.p} /> / sec)
|
||||
</Button>
|
||||
|
||||
@@ -42,7 +42,6 @@ function ServerButton(props: IServerProps): React.ReactElement {
|
||||
cost={cost}
|
||||
rerender={props.rerender}
|
||||
/>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,9 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
||||
|
||||
function take(stat: string): void {
|
||||
const loc = props.loc;
|
||||
player.startClass(router, calculateCost(), loc.expMult, stat);
|
||||
player.startClass(calculateCost(), loc.expMult, stat);
|
||||
player.startFocusing();
|
||||
router.toWork();
|
||||
}
|
||||
|
||||
function study(): void {
|
||||
|
||||
@@ -106,12 +106,15 @@ export const RamCosts: IMap<any> = {
|
||||
hackAnalyzeSecurity: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyzeChance: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
sleep: 0,
|
||||
share: 2.4,
|
||||
getSharePower: 0.2,
|
||||
grow: RamCostConstants.ScriptGrowRamCost,
|
||||
growthAnalyze: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
growthAnalyzeSecurity: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
weaken: RamCostConstants.ScriptWeakenRamCost,
|
||||
weakenAnalyze: RamCostConstants.ScriptWeakenAnalyzeRamCost,
|
||||
print: 0,
|
||||
printf: 0,
|
||||
tprint: 0,
|
||||
clearLog: 0,
|
||||
disableLog: 0,
|
||||
@@ -368,6 +371,7 @@ export const RamCosts: IMap<any> = {
|
||||
getStyles: 0,
|
||||
setStyles: 0,
|
||||
resetStyles: 0,
|
||||
getGameInfo: 0,
|
||||
},
|
||||
|
||||
heart: {
|
||||
|
||||
@@ -3,24 +3,20 @@ import { GetServer } from "./Server/AllServers";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
|
||||
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
||||
if (workerScript.delayReject) workerScript.delayReject();
|
||||
return new Promise(function (resolve, reject) {
|
||||
workerScript.delay = window.setTimeout(() => {
|
||||
workerScript.delay = null;
|
||||
workerScript.delayReject = undefined;
|
||||
|
||||
if (workerScript.env.stopFlag)
|
||||
reject(workerScript);
|
||||
else
|
||||
resolve();
|
||||
if (workerScript.env.stopFlag) reject(workerScript);
|
||||
else resolve();
|
||||
}, time);
|
||||
workerScript.delayReject = reject;
|
||||
});
|
||||
}
|
||||
|
||||
export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): string {
|
||||
if ((msg as any) instanceof WorkerScript) {
|
||||
console.error("HERE");
|
||||
}
|
||||
const server = GetServer(workerScript.hostname);
|
||||
if (server == null) {
|
||||
throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.hostname}`);
|
||||
|
||||
+40
-15
@@ -88,6 +88,8 @@ import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
|
||||
import { Flags } from "./NetscriptFunctions/Flags";
|
||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||
|
||||
interface NS extends INS {
|
||||
[key: string]: any;
|
||||
@@ -172,7 +174,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
throw makeRuntimeRejectMsg(
|
||||
workerScript,
|
||||
`Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` +
|
||||
`This is probably a bug. Please report to game developer`,
|
||||
`This is probably a bug. Please report to game developer`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -522,7 +524,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
if (isNaN(hackAmount)) {
|
||||
throw makeRuntimeErrorMsg(
|
||||
"hackAnalyzeThreads",
|
||||
`Invalid growth argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`,
|
||||
`Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -575,9 +577,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
throw makeRuntimeErrorMsg("asleep", "Takes 1 argument.");
|
||||
}
|
||||
workerScript.log("asleep", () => `Sleeping for ${time} milliseconds`);
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
return new Promise((resolve) => setTimeout(resolve, time));
|
||||
},
|
||||
grow: function (hostname: any, { threads: requestedThreads, stock }: any = {}): any {
|
||||
updateDynamicRam("grow", getRamCost(Player, "grow"));
|
||||
@@ -692,7 +692,8 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
workerScript.log(
|
||||
"weaken",
|
||||
() =>
|
||||
`'${server.hostname}' security level weakened to ${server.hackDifficulty
|
||||
`'${server.hostname}' security level weakened to ${
|
||||
server.hackDifficulty
|
||||
}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`,
|
||||
);
|
||||
workerScript.scriptRef.onlineExpGained += expGain;
|
||||
@@ -704,12 +705,29 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const coreBonus = 1 + (cores - 1) / 16;
|
||||
return CONSTANTS.ServerWeakenAmount * threads * coreBonus;
|
||||
},
|
||||
share: function (): Promise<void> {
|
||||
workerScript.log("share", () => "Sharing this computer.");
|
||||
const end = StartSharing(workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.intelligence, 2));
|
||||
return netscriptDelay(10000, workerScript).finally(function () {
|
||||
workerScript.log("share", () => "Finished sharing this computer.");
|
||||
end();
|
||||
});
|
||||
},
|
||||
getSharePower: function (): number {
|
||||
return CalculateShareMult();
|
||||
},
|
||||
print: function (...args: any[]): void {
|
||||
if (args.length === 0) {
|
||||
throw makeRuntimeErrorMsg("print", "Takes at least 1 argument.");
|
||||
}
|
||||
workerScript.print(argsToString(args));
|
||||
},
|
||||
printf: function (format: string, ...args: any[]): void {
|
||||
if (typeof format !== "string") {
|
||||
throw makeRuntimeErrorMsg("printf", "First argument must be string for the format.");
|
||||
}
|
||||
workerScript.print(vsprintf(format, args));
|
||||
},
|
||||
tprint: function (...args: any[]): void {
|
||||
if (args.length === 0) {
|
||||
throw makeRuntimeErrorMsg("tprint", "Takes at least 1 argument.");
|
||||
@@ -762,7 +780,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
},
|
||||
disableLog: function (fn: any): any {
|
||||
if (fn === "ALL") {
|
||||
for (fn in possibleLogs) {
|
||||
for (fn of Object.keys(possibleLogs)) {
|
||||
workerScript.disableLogs[fn] = true;
|
||||
}
|
||||
workerScript.log("disableLog", () => `Disabled logging for all functions`);
|
||||
@@ -775,7 +793,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
},
|
||||
enableLog: function (fn: any): any {
|
||||
if (fn === "ALL") {
|
||||
for (fn in possibleLogs) {
|
||||
for (fn of Object.keys(possibleLogs)) {
|
||||
delete workerScript.disableLogs[fn];
|
||||
}
|
||||
workerScript.log("enableLog", () => `Enabled logging for all functions`);
|
||||
@@ -1302,8 +1320,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
updateDynamicRam("ps", getRamCost(Player, "ps"));
|
||||
const server = safeGetServer(hostname, "ps");
|
||||
const processes = [];
|
||||
for (const i in server.runningScripts) {
|
||||
const script = server.runningScripts[i];
|
||||
for (const script of server.runningScripts) {
|
||||
processes.push({
|
||||
filename: script.filename,
|
||||
threads: script.threads,
|
||||
@@ -1632,7 +1649,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must be a positive power of 2`);
|
||||
if(ram > getPurchaseServerMaxRam()){
|
||||
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`);
|
||||
}else{
|
||||
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must be a positive power of 2`);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -2270,8 +2292,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const source_is_txt = source.endsWith(".txt");
|
||||
const dest_is_txt = destination.endsWith(".txt");
|
||||
|
||||
if (!isScriptFilename(source) && !source_is_txt) throw makeRuntimeErrorMsg("mv", `'mv' can only be used on scripts and text files (.txt)`);
|
||||
if (source_is_txt != dest_is_txt) throw makeRuntimeErrorMsg("mv", `Source and destination files must have the same type`);
|
||||
if (!isScriptFilename(source) && !source_is_txt)
|
||||
throw makeRuntimeErrorMsg("mv", `'mv' can only be used on scripts and text files (.txt)`);
|
||||
if (source_is_txt != dest_is_txt)
|
||||
throw makeRuntimeErrorMsg("mv", `Source and destination files must have the same type`);
|
||||
|
||||
if (source === destination) {
|
||||
return;
|
||||
@@ -2280,7 +2304,8 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
// This will throw if the server is not found, we do not need to validate result.
|
||||
const destServer: BaseServer | null = safeGetServer(host, "mv");
|
||||
|
||||
if (!source_is_txt && destServer.isRunning(source)) throw makeRuntimeErrorMsg("mv", `Cannot use 'mv' on a script that is running`)
|
||||
if (!source_is_txt && destServer.isRunning(source))
|
||||
throw makeRuntimeErrorMsg("mv", `Cannot use 'mv' on a script that is running`);
|
||||
|
||||
interface File {
|
||||
filename: string;
|
||||
@@ -2299,7 +2324,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
}
|
||||
}
|
||||
|
||||
if (source_file == null) throw makeRuntimeErrorMsg("mv", `Source file ${source} does not exist`)
|
||||
if (source_file == null) throw makeRuntimeErrorMsg("mv", `Source file ${source} does not exist`);
|
||||
|
||||
if (dest_file != null) {
|
||||
if (dest_file instanceof TextFile && source_file instanceof TextFile) {
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
BulkPurchase,
|
||||
SellShares,
|
||||
BuyBackShares,
|
||||
SetSmartSupplyUseLeftovers,
|
||||
} from "../Corporation/Actions";
|
||||
import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades";
|
||||
import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades";
|
||||
@@ -414,6 +415,16 @@ export function NetscriptCorporation(
|
||||
const warehouse = getWarehouse(divisionName, cityName);
|
||||
SetSmartSupply(warehouse, enabled);
|
||||
},
|
||||
setSmartSupplyUseLeftovers: function (adivisionName: any, acityName: any, amaterialName: any, aenabled: any): void {
|
||||
checkAccess("setSmartSupplyUseLeftovers", 7);
|
||||
const divisionName = helper.string("setSmartSupply", "divisionName", adivisionName);
|
||||
const cityName = helper.string("sellProduct", "cityName", acityName);
|
||||
const materialName = helper.string("sellProduct", "materialName", amaterialName);
|
||||
const enabled = helper.boolean(aenabled);
|
||||
const warehouse = getWarehouse(divisionName, cityName);
|
||||
const material = getMaterial(divisionName, cityName, materialName);
|
||||
SetSmartSupplyUseLeftovers(warehouse, material, enabled);
|
||||
},
|
||||
buyMaterial: function (adivisionName: any, acityName: any, amaterialName: any, aamt: any): void {
|
||||
checkAccess("buyMaterial", 7);
|
||||
const divisionName = helper.string("buyMaterial", "divisionName", adivisionName);
|
||||
|
||||
@@ -81,41 +81,41 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
|
||||
return {
|
||||
skills: {
|
||||
calculateSkill: function (exp: any, mult: any = 1): any {
|
||||
checkFormulasAccess("basic.calculateSkill");
|
||||
checkFormulasAccess("skills.calculateSkill");
|
||||
return calculateSkill(exp, mult);
|
||||
},
|
||||
calculateExp: function (skill: any, mult: any = 1): any {
|
||||
checkFormulasAccess("basic.calculateExp");
|
||||
checkFormulasAccess("skills.calculateExp");
|
||||
return calculateExp(skill, mult);
|
||||
},
|
||||
},
|
||||
hacking: {
|
||||
hackChance: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.hackChance");
|
||||
checkFormulasAccess("hacking.hackChance");
|
||||
return calculateHackingChance(server, player);
|
||||
},
|
||||
hackExp: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.hackExp");
|
||||
checkFormulasAccess("hacking.hackExp");
|
||||
return calculateHackingExpGain(server, player);
|
||||
},
|
||||
hackPercent: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.hackPercent");
|
||||
checkFormulasAccess("hacking.hackPercent");
|
||||
return calculatePercentMoneyHacked(server, player);
|
||||
},
|
||||
growPercent: function (server: any, threads: any, player: any, cores: any = 1): any {
|
||||
checkFormulasAccess("basic.growPercent");
|
||||
checkFormulasAccess("hacking.growPercent");
|
||||
return calculateServerGrowth(server, threads, player, cores);
|
||||
},
|
||||
hackTime: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.hackTime");
|
||||
checkFormulasAccess("hacking.hackTime");
|
||||
return calculateHackingTime(server, player) * 1000;
|
||||
},
|
||||
growTime: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.growTime");
|
||||
checkFormulasAccess("hacking.growTime");
|
||||
return calculateGrowTime(server, player) * 1000;
|
||||
},
|
||||
weakenTime: function (server: any, player: any): any {
|
||||
checkFormulasAccess("basic.weakenTime");
|
||||
checkFormulasAccess("hacking.weakenTime");
|
||||
return calculateWeakenTime(server, player) * 1000;
|
||||
},
|
||||
},
|
||||
@@ -188,21 +188,27 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
|
||||
},
|
||||
gang: {
|
||||
wantedPenalty(gang: any): number {
|
||||
checkFormulasAccess("gang.wantedPenalty");
|
||||
return calculateWantedPenalty(gang);
|
||||
},
|
||||
respectGain: function (gang: any, member: any, task: any): number {
|
||||
checkFormulasAccess("gang.respectGain");
|
||||
return calculateRespectGain(gang, member, task);
|
||||
},
|
||||
wantedLevelGain: function (gang: any, member: any, task: any): number {
|
||||
checkFormulasAccess("gang.wantedLevelGain");
|
||||
return calculateWantedLevelGain(gang, member, task);
|
||||
},
|
||||
moneyGain: function (gang: any, member: any, task: any): number {
|
||||
checkFormulasAccess("gang.moneyGain");
|
||||
return calculateMoneyGain(gang, member, task);
|
||||
},
|
||||
ascensionPointsGain: function (exp: any): number {
|
||||
checkFormulasAccess("gang.ascensionPointsGain");
|
||||
return calculateAscensionPointsGain(exp);
|
||||
},
|
||||
ascensionMultiplier: function (points: any): number {
|
||||
checkFormulasAccess("gang.ascensionMultiplier");
|
||||
return calculateAscensionMult(points);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -101,7 +101,7 @@ export function NetscriptGang(player: IPlayer, workerScript: WorkerScript, helpe
|
||||
helper.updateDynamicRam("getOtherGangInformation", getRamCost(player, "gang", "getOtherGangInformation"));
|
||||
checkGangApiAccess("getOtherGangInformation");
|
||||
const cpy: any = {};
|
||||
for (const gang in AllGangs) {
|
||||
for (const gang of Object.keys(AllGangs)) {
|
||||
cpy[gang] = Object.assign({}, AllGangs[gang]);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ export function NetscriptSingularity(
|
||||
// If player has a gang with this faction, return all augmentations.
|
||||
if (player.hasGangWith(facname)) {
|
||||
const res = [];
|
||||
for (const augName in Augmentations) {
|
||||
for (const augName of Object.keys(Augmentations)) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
|
||||
if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue;
|
||||
const aug = Augmentations[augName];
|
||||
@@ -165,7 +165,7 @@ export function NetscriptSingularity(
|
||||
|
||||
let augs = [];
|
||||
if (player.hasGangWith(faction)) {
|
||||
for (const augName in Augmentations) {
|
||||
for (const augName of Object.keys(Augmentations)) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) continue;
|
||||
if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue;
|
||||
const tempAug = Augmentations[augName];
|
||||
@@ -264,7 +264,7 @@ export function NetscriptSingularity(
|
||||
return false;
|
||||
}
|
||||
Router.toLocation(location);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 500);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000);
|
||||
return true;
|
||||
},
|
||||
universityCourse: function (universityName: any, className: any, focus = true): any {
|
||||
@@ -343,7 +343,7 @@ export function NetscriptSingularity(
|
||||
workerScript.log("universityCourse", () => `Invalid class name: ${className}.`);
|
||||
return false;
|
||||
}
|
||||
player.startClass(Router, costMult, expMult, task);
|
||||
player.startClass(costMult, expMult, task);
|
||||
if (focus) {
|
||||
player.startFocusing();
|
||||
Router.toWork();
|
||||
@@ -433,19 +433,19 @@ export function NetscriptSingularity(
|
||||
switch (stat.toLowerCase()) {
|
||||
case "strength".toLowerCase():
|
||||
case "str".toLowerCase():
|
||||
player.startClass(Router, costMult, expMult, CONSTANTS.ClassGymStrength);
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymStrength);
|
||||
break;
|
||||
case "defense".toLowerCase():
|
||||
case "def".toLowerCase():
|
||||
player.startClass(Router, costMult, expMult, CONSTANTS.ClassGymDefense);
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymDefense);
|
||||
break;
|
||||
case "dexterity".toLowerCase():
|
||||
case "dex".toLowerCase():
|
||||
player.startClass(Router, costMult, expMult, CONSTANTS.ClassGymDexterity);
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymDexterity);
|
||||
break;
|
||||
case "agility".toLowerCase():
|
||||
case "agi".toLowerCase():
|
||||
player.startClass(Router, costMult, expMult, CONSTANTS.ClassGymAgility);
|
||||
player.startClass(costMult, expMult, CONSTANTS.ClassGymAgility);
|
||||
break;
|
||||
default:
|
||||
workerScript.log("gymWorkout", () => `Invalid stat: ${stat}.`);
|
||||
@@ -474,16 +474,16 @@ export function NetscriptSingularity(
|
||||
case CityName.Ishima:
|
||||
case CityName.Volhaven:
|
||||
if (player.money < CONSTANTS.TravelCost) {
|
||||
throw helper.makeRuntimeErrorMsg("travelToCity", "Not enough money to travel.");
|
||||
workerScript.log("travelToCity", () => "Not enough money to travel.");
|
||||
return false
|
||||
}
|
||||
player.loseMoney(CONSTANTS.TravelCost, "other");
|
||||
player.city = cityname;
|
||||
workerScript.log("travelToCity", () => `Traveled to ${cityname}`);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000);
|
||||
return true;
|
||||
default:
|
||||
workerScript.log("travelToCity", () => `Invalid city name: '${cityname}'.`);
|
||||
return false;
|
||||
throw helper.makeRuntimeErrorMsg("travelToCity", `Invalid city name: '${cityname}'.`);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -515,7 +515,7 @@ export function NetscriptSingularity(
|
||||
|
||||
player.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
|
||||
darkweb.serversOnNetwork.push(player.getHomeComputer().hostname);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 500);
|
||||
workerScript.log("purchaseTor", () => "You have purchased a Tor router!");
|
||||
return true;
|
||||
},
|
||||
@@ -555,7 +555,7 @@ export function NetscriptSingularity(
|
||||
"purchaseProgram",
|
||||
() => `You have purchased the '${item.program}' program. The new program can be found on your home computer.`,
|
||||
);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50);
|
||||
player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 5000);
|
||||
return true;
|
||||
},
|
||||
getCurrentServer: function (): any {
|
||||
@@ -653,7 +653,9 @@ export function NetscriptSingularity(
|
||||
!(
|
||||
player.workType == CONSTANTS.WorkTypeFaction ||
|
||||
player.workType == CONSTANTS.WorkTypeCompany ||
|
||||
player.workType == CONSTANTS.WorkTypeCompanyPartTime
|
||||
player.workType == CONSTANTS.WorkTypeCompanyPartTime ||
|
||||
player.workType == CONSTANTS.WorkTypeCreateProgram ||
|
||||
player.workType == CONSTANTS.WorkTypeStudyClass
|
||||
)
|
||||
) {
|
||||
throw helper.makeRuntimeErrorMsg("setFocus", "Cannot change focus for current job");
|
||||
@@ -1269,7 +1271,7 @@ export function NetscriptSingularity(
|
||||
return false;
|
||||
}
|
||||
|
||||
player.startCreateProgramWork(Router, p.name, create.time, create.level);
|
||||
player.startCreateProgramWork(p.name, create.time, create.level);
|
||||
if (focus) {
|
||||
player.startFocusing();
|
||||
Router.toWork();
|
||||
|
||||
@@ -28,7 +28,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
height: function (): number {
|
||||
return staneksGift.height();
|
||||
},
|
||||
charge: function (arootX: any, arootY: any): Promise<void> {
|
||||
charge: function (arootX: unknown, arootY: unknown): Promise<void> {
|
||||
const rootX = helper.number("stanek.charge", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.charge", "rootY", arootY);
|
||||
|
||||
@@ -63,7 +63,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
workerScript.log("stanek.clear", () => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlace: function (arootX: any, arootY: any, arotation: any, afragmentId: any): boolean {
|
||||
canPlace: function (arootX: unknown, arootY: unknown, arotation: unknown, afragmentId: unknown): boolean {
|
||||
const rootX = helper.number("stanek.canPlace", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.canPlace", "rootY", arootY);
|
||||
const rotation = helper.number("stanek.canPlace", "rotation", arotation);
|
||||
@@ -75,7 +75,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
place: function (arootX: any, arootY: any, arotation: any, afragmentId: any): boolean {
|
||||
place: function (arootX: unknown, arootY: unknown, arotation: unknown, afragmentId: unknown): boolean {
|
||||
const rootX = helper.number("stanek.place", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.place", "rootY", arootY);
|
||||
const rotation = helper.number("stanek.place", "rotation", arotation);
|
||||
@@ -86,7 +86,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
get: function (arootX: any, arootY: any): IActiveFragment | undefined {
|
||||
get: function (arootX: unknown, arootY: unknown): IActiveFragment | undefined {
|
||||
const rootX = helper.number("stanek.get", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.get", "rootY", arootY);
|
||||
helper.updateDynamicRam("get", getRamCost(player, "stanek", "get"));
|
||||
@@ -95,7 +95,7 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
remove: function (arootX: any, arootY: any): boolean {
|
||||
remove: function (arootX: unknown, arootY: unknown): boolean {
|
||||
const rootX = helper.number("stanek.remove", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.remove", "rootY", arootY);
|
||||
helper.updateDynamicRam("remove", getRamCost(player, "stanek", "remove"));
|
||||
|
||||
@@ -273,7 +273,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
|
||||
const orders: any = {};
|
||||
|
||||
const stockMarketOrders = StockMarket["Orders"];
|
||||
for (const symbol in stockMarketOrders) {
|
||||
for (const symbol of Object.keys(stockMarketOrders)) {
|
||||
const orderBook = stockMarketOrders[symbol];
|
||||
if (orderBook.constructor === Array && orderBook.length > 0) {
|
||||
orders[symbol] = [];
|
||||
|
||||
@@ -2,11 +2,13 @@ import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { IStyleSettings, UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { GameInfo, IStyleSettings, UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { ThemeEvents } from "../ui/React/Theme";
|
||||
import { defaultTheme } from "../Settings/Themes";
|
||||
import { defaultStyles } from "../Settings/Styles";
|
||||
import { ThemeEvents } from "../Themes/ui/Theme";
|
||||
import { defaultTheme } from "../Themes/Themes";
|
||||
import { defaultStyles } from "../Themes/Styles";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { hash } from "../hash/hash";
|
||||
|
||||
export function NetscriptUserInterface(
|
||||
player: IPlayer,
|
||||
@@ -84,6 +86,19 @@ export function NetscriptUserInterface(
|
||||
Settings.styles = { ...defaultStyles };
|
||||
ThemeEvents.emit();
|
||||
workerScript.log("ui.resetStyles", () => `Reinitialized styles to default`);
|
||||
},
|
||||
|
||||
getGameInfo: function (): GameInfo {
|
||||
helper.updateDynamicRam("getGameInfo", getRamCost(player, "ui", "getGameInfo"));
|
||||
const version = CONSTANTS.VersionString;
|
||||
const commit = hash();
|
||||
const platform = (navigator.userAgent.toLowerCase().indexOf(" electron/") > -1) ? 'Steam' : 'Browser';
|
||||
|
||||
const gameInfo = {
|
||||
version, commit, platform,
|
||||
}
|
||||
|
||||
return gameInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export function toNative(pseudoObj: any): any {
|
||||
} else {
|
||||
// Object.
|
||||
nativeObj = {};
|
||||
for (const key in pseudoObj.properties) {
|
||||
for (const key of Object.keys(pseudoObj.properties)) {
|
||||
const val = pseudoObj.properties[key];
|
||||
nativeObj[key] = toNative(val);
|
||||
}
|
||||
|
||||
+27
-40
@@ -9,9 +9,6 @@ import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
|
||||
import { ScriptUrl } from "./Script/ScriptUrl";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { Script } from "./Script/Script";
|
||||
import { computeHash } from "./utils/helpers/computeHash";
|
||||
import { BlobCache } from "./utils/BlobCache";
|
||||
import { ImportCache } from "./utils/ImportCache";
|
||||
import { areImportsEquals } from "./Terminal/DirectoryHelpers";
|
||||
import { IPlayer } from "./PersonObjects/IPlayer";
|
||||
|
||||
@@ -82,6 +79,16 @@ export async function executeJSScript(
|
||||
return loadedModule.main(ns);
|
||||
}
|
||||
|
||||
function isDependencyOutOfDate(filename: string, scripts: Script[], scriptModuleSequenceNumber: number): boolean {
|
||||
const depScript = scripts.find((s) => s.filename == filename);
|
||||
|
||||
// If the script is not present on the server, we should recompile, if only to get any necessary
|
||||
// compilation errors.
|
||||
if (!depScript) return true;
|
||||
|
||||
const depIsMoreRecent = depScript.moduleSequenceNumber > scriptModuleSequenceNumber;
|
||||
return depIsMoreRecent;
|
||||
}
|
||||
/** Returns whether we should compile the script parameter.
|
||||
*
|
||||
* @param {Script} script
|
||||
@@ -89,16 +96,7 @@ export async function executeJSScript(
|
||||
*/
|
||||
function shouldCompile(script: Script, scripts: Script[]): boolean {
|
||||
if (script.module === "") return true;
|
||||
return script.dependencies.some((dep) => {
|
||||
const depScript = scripts.find((s) => s.filename == dep.filename);
|
||||
|
||||
// If the script is not present on the server, we should recompile, if only to get any necessary
|
||||
// compilation errors.
|
||||
if (!depScript) return true;
|
||||
|
||||
const depIsMoreRecent = depScript.moduleSequenceNumber > script.moduleSequenceNumber;
|
||||
return depIsMoreRecent;
|
||||
});
|
||||
return script.dependencies.some((dep) => isDependencyOutOfDate(dep.filename, scripts, script.moduleSequenceNumber));
|
||||
}
|
||||
|
||||
// Gets a stack of blob urls, the top/right-most element being
|
||||
@@ -123,8 +121,13 @@ function shouldCompile(script: Script, scripts: Script[]): boolean {
|
||||
// BUG: apparently seen is never consulted. Oops.
|
||||
function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): ScriptUrl[] {
|
||||
// Inspired by: https://stackoverflow.com/a/43834063/91401
|
||||
/** @type {ScriptUrl[]} */
|
||||
const urlStack = [];
|
||||
const urlStack: ScriptUrl[] = [];
|
||||
// Seen contains the dependents of the current script. Make sure we include that in the script dependents.
|
||||
for (const dependent of seen) {
|
||||
if (!script.dependents.some((s) => s.server === dependent.server && s.filename == dependent.filename)) {
|
||||
script.dependents.push({ server: dependent.server, filename: dependent.filename });
|
||||
}
|
||||
}
|
||||
seen.push(script);
|
||||
try {
|
||||
// Replace every import statement with an import to a blob url containing
|
||||
@@ -149,7 +152,7 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
importNodes.push({
|
||||
filename: node.source.value,
|
||||
start: node.source.range[0] + 1,
|
||||
end: node.source.range[1] - 1
|
||||
end: node.source.range[1] - 1,
|
||||
});
|
||||
},
|
||||
ExportNamedDeclaration(node: any) {
|
||||
@@ -157,7 +160,7 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
importNodes.push({
|
||||
filename: node.source.value,
|
||||
start: node.source.range[0] + 1,
|
||||
end: node.source.range[1] - 1
|
||||
end: node.source.range[1] - 1,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -166,10 +169,10 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
importNodes.push({
|
||||
filename: node.source.value,
|
||||
start: node.source.range[0] + 1,
|
||||
end: node.source.range[1] - 1
|
||||
end: node.source.range[1] - 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
// Sort the nodes from last start index to first. This replaces the last import with a blob first,
|
||||
// preventing the ranges for other imports from being shifted.
|
||||
@@ -184,18 +187,12 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
if (matchingScripts.length === 0) continue;
|
||||
|
||||
const [importedScript] = matchingScripts;
|
||||
// Check to see if the urls for this script are stored in the cache by the hash value.
|
||||
let urls = ImportCache.get(importedScript.hash());
|
||||
// If we don't have it in the cache, then we need to generate the urls for it.
|
||||
if (!urls) {
|
||||
// Try to get a URL for the requested script and its dependencies.
|
||||
urls = _getScriptUrls(importedScript, scripts, seen);
|
||||
}
|
||||
|
||||
const urls = _getScriptUrls(importedScript, scripts, seen);
|
||||
|
||||
// The top url in the stack is the replacement import file for this script.
|
||||
urlStack.push(...urls);
|
||||
const blob = urls[urls.length - 1].url;
|
||||
ImportCache.store(importedScript.hash(), urls);
|
||||
|
||||
// Replace the blob inside the import statement.
|
||||
transformedCode = transformedCode.substring(0, node.start) + blob + transformedCode.substring(node.end);
|
||||
@@ -205,23 +202,13 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
// accidental calls to window.print() do not bring up the "print screen" dialog
|
||||
transformedCode += `\n\nfunction print() {throw new Error("Invalid call to window.print(). Did you mean to use Netscript's print()?");}`;
|
||||
|
||||
// If we successfully transformed the code, create a blob url for it
|
||||
// Compute the hash for the transformed code
|
||||
const transformedHash = computeHash(transformedCode);
|
||||
// Check to see if this transformed hash is in our cache
|
||||
let blob = BlobCache.get(transformedHash);
|
||||
if (!blob) {
|
||||
blob = URL.createObjectURL(makeScriptBlob(transformedCode));
|
||||
}
|
||||
// Store this blob in the cache. Any script that transforms the same
|
||||
// (e.g. same scripts on server, same hash value, etc) can use this blob url.
|
||||
BlobCache.store(transformedHash, blob);
|
||||
const blob = URL.createObjectURL(makeScriptBlob(transformedCode));
|
||||
// Push the blob URL onto the top of the stack.
|
||||
urlStack.push(new ScriptUrl(script.filename, blob));
|
||||
urlStack.push(new ScriptUrl(script.filename, blob, script.moduleSequenceNumber));
|
||||
return urlStack;
|
||||
} catch (err) {
|
||||
// If there is an error, we need to clean up the URLs.
|
||||
for (const url in urlStack) URL.revokeObjectURL(url);
|
||||
for (const url of urlStack) URL.revokeObjectURL(url.url);
|
||||
throw err;
|
||||
} finally {
|
||||
seen.pop();
|
||||
|
||||
@@ -116,11 +116,11 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
};
|
||||
}
|
||||
|
||||
for (const prop in workerScript.env.vars) {
|
||||
for (const prop of Object.keys(workerScript.env.vars)) {
|
||||
if (typeof workerScript.env.vars[prop] !== "function") continue;
|
||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
||||
}
|
||||
workerScript.env.vars.stanek.charge = wrap("stanek.prop", workerScript.env.vars.stanek.charge);
|
||||
workerScript.env.vars.stanek.charge = wrap("stanek.charge", workerScript.env.vars.stanek.charge);
|
||||
|
||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||
// to that script, which env.vars does at this point.
|
||||
@@ -131,7 +131,6 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
})
|
||||
.catch((e) => reject(e));
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
if (e instanceof Error) {
|
||||
if (e instanceof SyntaxError) {
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e.message + " (sorry we can't be more helpful)");
|
||||
@@ -175,11 +174,11 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
const interpreterInitialization = function (int: any, scope: any): void {
|
||||
//Add the Netscript environment
|
||||
const ns = NetscriptFunctions(workerScript);
|
||||
for (const name in ns) {
|
||||
for (const name of Object.keys(ns)) {
|
||||
const entry = ns[name];
|
||||
if (typeof entry === "function") {
|
||||
//Async functions need to be wrapped. See JS-Interpreter documentation
|
||||
if (["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "scp", "write"].includes(name)) {
|
||||
if (["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "scp", "write", "share"].includes(name)) {
|
||||
const tempWrapper = function (...args: any[]): void {
|
||||
const fnArgs = [];
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { CalculateShareMult as CSM } from "./formulas/share";
|
||||
|
||||
export let sharePower = 1;
|
||||
|
||||
export function StartSharing(threads: number): () => void {
|
||||
sharePower += threads;
|
||||
return () => (sharePower -= threads);
|
||||
}
|
||||
|
||||
export function CalculateShareMult(): number {
|
||||
return CSM(sharePower);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export function CalculateShareMult(power: number): number {
|
||||
const x = 1 + Math.log(power) / (8 * Math.log(1000));
|
||||
if (isNaN(x) || !isFinite(x)) return 1;
|
||||
return x;
|
||||
}
|
||||
@@ -72,6 +72,7 @@ export interface IPlayer {
|
||||
sourceFiles: IPlayerOwnedSourceFile[];
|
||||
exploits: Exploit[];
|
||||
achievements: PlayerAchievement[];
|
||||
terminalCommandHistory: string[];
|
||||
lastUpdate: number;
|
||||
totalPlaytime: number;
|
||||
|
||||
@@ -215,7 +216,7 @@ export interface IPlayer {
|
||||
singularityStopWork(): string;
|
||||
startBladeburner(p: any): void;
|
||||
startFactionWork(faction: Faction): void;
|
||||
startClass(router: IRouter, costMult: number, expMult: number, className: string): void;
|
||||
startClass(costMult: number, expMult: number, className: string): void;
|
||||
startCorporation(corpName: string, additionalShares?: number): void;
|
||||
startCrime(
|
||||
router: IRouter,
|
||||
@@ -247,7 +248,7 @@ export interface IPlayer {
|
||||
quitJob(company: string): void;
|
||||
hasJob(): boolean;
|
||||
createHacknetServer(): HacknetServer;
|
||||
startCreateProgramWork(router: IRouter, programName: string, time: number, reqLevel: number): void;
|
||||
startCreateProgramWork(programName: string, time: number, reqLevel: number): void;
|
||||
queueAugmentation(augmentationName: string): void;
|
||||
receiveInvite(factionName: string): void;
|
||||
updateSkillLevels(): void;
|
||||
|
||||
@@ -112,7 +112,7 @@ export abstract class Person {
|
||||
* Updates this object's multipliers for the given augmentation
|
||||
*/
|
||||
applyAugmentation(aug: Augmentation): void {
|
||||
for (const mult in aug.mults) {
|
||||
for (const mult of Object.keys(aug.mults)) {
|
||||
if ((this as any)[mult] == null) {
|
||||
console.warn(`Augmentation has unrecognized multiplier property: ${mult}`);
|
||||
} else {
|
||||
|
||||
@@ -35,7 +35,9 @@ import { CityName } from "../../Locations/data/CityNames";
|
||||
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
|
||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
|
||||
import { ISkillProgress } from "../formulas/skill";
|
||||
import { PlayerAchievement } from '../../Achievements/Achievements';
|
||||
import { PlayerAchievement } from "../../Achievements/Achievements";
|
||||
import { cyrb53 } from "../../utils/StringHelperFunctions";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
|
||||
export class PlayerObject implements IPlayer {
|
||||
// Class members
|
||||
@@ -77,7 +79,10 @@ export class PlayerObject implements IPlayer {
|
||||
sourceFiles: IPlayerOwnedSourceFile[];
|
||||
exploits: Exploit[];
|
||||
achievements: PlayerAchievement[];
|
||||
terminalCommandHistory: string[];
|
||||
identifier: string;
|
||||
lastUpdate: number;
|
||||
lastSave: number;
|
||||
totalPlaytime: number;
|
||||
|
||||
// Stats
|
||||
@@ -220,7 +225,7 @@ export class PlayerObject implements IPlayer {
|
||||
singularityStopWork: () => string;
|
||||
startBladeburner: (p: any) => void;
|
||||
startFactionWork: (faction: Faction) => void;
|
||||
startClass: (router: IRouter, costMult: number, expMult: number, className: string) => void;
|
||||
startClass: (costMult: number, expMult: number, className: string) => void;
|
||||
startCorporation: (corpName: string, additionalShares?: number) => void;
|
||||
startCrime: (
|
||||
router: IRouter,
|
||||
@@ -253,7 +258,7 @@ export class PlayerObject implements IPlayer {
|
||||
hasJob: () => boolean;
|
||||
process: (router: IRouter, numCycles?: number) => void;
|
||||
createHacknetServer: () => HacknetServer;
|
||||
startCreateProgramWork: (router: IRouter, programName: string, time: number, reqLevel: number) => void;
|
||||
startCreateProgramWork: (programName: string, time: number, reqLevel: number) => void;
|
||||
queueAugmentation: (augmentationName: string) => void;
|
||||
receiveInvite: (factionName: string) => void;
|
||||
updateSkillLevels: () => void;
|
||||
@@ -459,7 +464,9 @@ export class PlayerObject implements IPlayer {
|
||||
|
||||
//Used to store the last update time.
|
||||
this.lastUpdate = 0;
|
||||
this.lastSave = 0;
|
||||
this.totalPlaytime = 0;
|
||||
|
||||
this.playtimeSinceLastAug = 0;
|
||||
this.playtimeSinceLastBitnode = 0;
|
||||
|
||||
@@ -471,6 +478,17 @@ export class PlayerObject implements IPlayer {
|
||||
|
||||
this.exploits = [];
|
||||
this.achievements = [];
|
||||
this.terminalCommandHistory = [];
|
||||
|
||||
// Let's get a hash of some semi-random stuff so we have something unique.
|
||||
this.identifier = cyrb53(
|
||||
"I-" +
|
||||
new Date().getTime() +
|
||||
navigator.userAgent +
|
||||
window.innerWidth +
|
||||
window.innerHeight +
|
||||
getRandomInt(100, 999),
|
||||
);
|
||||
|
||||
this.init = generalMethods.init;
|
||||
this.prestigeAugmentation = generalMethods.prestigeAugmentation;
|
||||
|
||||
@@ -1253,14 +1253,12 @@ export function getWorkRepGain(this: IPlayer): number {
|
||||
/* Creating a Program */
|
||||
export function startCreateProgramWork(
|
||||
this: IPlayer,
|
||||
router: IRouter,
|
||||
programName: string,
|
||||
time: number,
|
||||
reqLevel: number,
|
||||
): void {
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.focus = true;
|
||||
this.workType = CONSTANTS.WorkTypeCreateProgram;
|
||||
|
||||
//Time needed to complete work affected by hacking skill (linearly based on
|
||||
@@ -1289,7 +1287,6 @@ export function startCreateProgramWork(
|
||||
}
|
||||
|
||||
this.createProgramName = programName;
|
||||
router.toWork();
|
||||
}
|
||||
|
||||
export function createProgramWork(this: IPlayer, numCycles: number): boolean {
|
||||
@@ -1337,10 +1334,9 @@ export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): stri
|
||||
return "You've finished creating " + programName + "! The new program can be found on your home computer.";
|
||||
}
|
||||
/* Studying/Taking Classes */
|
||||
export function startClass(this: IPlayer, router: IRouter, costMult: number, expMult: number, className: string): void {
|
||||
export function startClass(this: IPlayer, costMult: number, expMult: number, className: string): void {
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.focus = true;
|
||||
this.workType = CONSTANTS.WorkTypeStudyClass;
|
||||
this.workCostMult = costMult;
|
||||
this.workExpMult = expMult;
|
||||
@@ -1353,7 +1349,6 @@ export function startClass(this: IPlayer, router: IRouter, costMult: number, exp
|
||||
this.workDexExpGainRate = earnings.workDexExpGainRate;
|
||||
this.workAgiExpGainRate = earnings.workAgiExpGainRate;
|
||||
this.workChaExpGainRate = earnings.workChaExpGainRate;
|
||||
router.toWork();
|
||||
}
|
||||
|
||||
export function takeClass(this: IPlayer, numCycles: number): boolean {
|
||||
@@ -1485,7 +1480,7 @@ export function finishCrime(this: IPlayer, cancelled: boolean): string {
|
||||
if (determineCrimeSuccess(this, this.crimeType)) {
|
||||
//Handle Karma and crime statistics
|
||||
let crime = null;
|
||||
for (const i in Crimes) {
|
||||
for (const i of Object.keys(Crimes)) {
|
||||
if (Crimes[i].type == this.crimeType) {
|
||||
crime = Crimes[i];
|
||||
break;
|
||||
@@ -2557,15 +2552,15 @@ export function setBitNodeNumber(this: IPlayer, n: number): void {
|
||||
}
|
||||
|
||||
export function queueAugmentation(this: IPlayer, name: string): void {
|
||||
for (const i in this.queuedAugmentations) {
|
||||
if (this.queuedAugmentations[i].name == name) {
|
||||
for (const aug of this.queuedAugmentations) {
|
||||
if (aug.name == name) {
|
||||
console.warn(`tried to queue ${name} twice, this may be a bug`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const i in this.augmentations) {
|
||||
if (this.augmentations[i].name == name) {
|
||||
for (const aug of this.augmentations) {
|
||||
if (aug.name == name) {
|
||||
console.warn(`tried to queue ${name} twice, this may be a bug`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean {
|
||||
p.charisma_exp = r.charisma_exp;
|
||||
|
||||
// Reset Augmentation "owned" data
|
||||
for (const augKey in Augmentations) {
|
||||
for (const augKey of Object.keys(Augmentations)) {
|
||||
Augmentations[augKey].owned = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
|
||||
if (p.inGang()) {
|
||||
const fac = p.getGangFaction();
|
||||
|
||||
for (const augName in Augmentations) {
|
||||
for (const augName of Object.keys(Augmentations)) {
|
||||
const aug = Augmentations[augName];
|
||||
if (!isAvailableForSleeve(aug)) {
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { CONSTANTS } from "../../../Constants";
|
||||
import { Typography } from "@mui/material";
|
||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||
import { Modal } from "../../../ui/React/Modal";
|
||||
import React from "react";
|
||||
@@ -80,6 +83,13 @@ export function MoreStatsModal(props: IProps): React.ReactElement {
|
||||
]}
|
||||
title="Multipliers:"
|
||||
/>
|
||||
|
||||
{/* Check for storedCycles to be a bit over 0 to prevent jittering */}
|
||||
{props.sleeve.storedCycles > 10 && (
|
||||
<Typography sx={{ py: 2 }}>
|
||||
Bonus Time: {convertTimeMsToTimeElapsedString(props.sleeve.storedCycles * CONSTANTS.MilliPerCycle)}
|
||||
</Typography>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IPlayer } from "../IPlayer";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CalculateShareMult } from "../../NetworkShare/Share";
|
||||
|
||||
function mult(f: Faction): number {
|
||||
let favorMult = 1 + f.favor / 100;
|
||||
@@ -16,7 +17,8 @@ export function getHackingWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
((p.hacking + p.intelligence / 3) / CONSTANTS.MaxSkillLevel) *
|
||||
p.faction_rep_mult *
|
||||
p.getIntelligenceBonus(1) *
|
||||
mult(f)
|
||||
mult(f) *
|
||||
CalculateShareMult()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -75,14 +75,14 @@ export function prestigeAugmentation(): void {
|
||||
initForeignServers(Player.getHomeComputer());
|
||||
|
||||
// Gain favor for Companies
|
||||
for (const member in Companies) {
|
||||
for (const member of Object.keys(Companies)) {
|
||||
if (Companies.hasOwnProperty(member)) {
|
||||
Companies[member].gainFavor();
|
||||
}
|
||||
}
|
||||
|
||||
// Gain favor for factions
|
||||
for (const member in Factions) {
|
||||
for (const member of Object.keys(Factions)) {
|
||||
if (Factions.hasOwnProperty(member)) {
|
||||
Factions[member].gainFavor();
|
||||
}
|
||||
@@ -113,6 +113,15 @@ export function prestigeAugmentation(): void {
|
||||
if (faction instanceof Faction) {
|
||||
joinFaction(faction);
|
||||
}
|
||||
const penalty = 0.95;
|
||||
for (const m of gang.members) {
|
||||
m.hack_asc_points *= penalty;
|
||||
m.str_asc_points *= penalty;
|
||||
m.def_asc_points *= penalty;
|
||||
m.dex_asc_points *= penalty;
|
||||
m.agi_asc_points *= penalty;
|
||||
m.cha_asc_points *= penalty;
|
||||
}
|
||||
}
|
||||
|
||||
// BitNode 3: Corporatocracy
|
||||
@@ -200,14 +209,14 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
homeComp.cpuCores = 1;
|
||||
|
||||
// Reset favor for Companies
|
||||
for (const member in Companies) {
|
||||
for (const member of Object.keys(Companies)) {
|
||||
if (Companies.hasOwnProperty(member)) {
|
||||
Companies[member].favor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset favor for factions
|
||||
for (const member in Factions) {
|
||||
for (const member of Object.keys(Factions)) {
|
||||
if (Factions.hasOwnProperty(member)) {
|
||||
Factions[member].favor = 0;
|
||||
}
|
||||
@@ -219,7 +228,7 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
}
|
||||
|
||||
// Delete all Augmentations
|
||||
for (const name in Augmentations) {
|
||||
for (const name of Object.keys(Augmentations)) {
|
||||
if (Augmentations.hasOwnProperty(name)) {
|
||||
delete Augmentations[name];
|
||||
}
|
||||
@@ -242,7 +251,7 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
// Messages
|
||||
initMessages();
|
||||
|
||||
if (Player.sourceFileLvl(5) > 0) {
|
||||
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
||||
homeComp.programs.push(Programs.Formulas.name);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
//Returns the programs this player can create.
|
||||
export function getAvailableCreatePrograms(player: IPlayer): Program[] {
|
||||
const programs: Program[] = [];
|
||||
for (const key in Programs) {
|
||||
for (const key of Object.keys(Programs)) {
|
||||
// Non-creatable program
|
||||
const create = Programs[key].create;
|
||||
if (create == null) continue;
|
||||
|
||||
@@ -50,7 +50,9 @@ export function ProgramsRoot(): React.ReactElement {
|
||||
sx={{ my: 1 }}
|
||||
onClick={(event) => {
|
||||
if (!event.isTrusted) return;
|
||||
player.startCreateProgramWork(router, program.name, create.time, create.level);
|
||||
player.startCreateProgramWork(program.name, create.time, create.level);
|
||||
player.startFocusing();
|
||||
router.toWork();
|
||||
}}
|
||||
>
|
||||
{program.name}
|
||||
|
||||
+176
-16
@@ -22,11 +22,44 @@ import { v1APIBreak } from "./utils/v1APIBreak";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
|
||||
import { LocationName } from "./Locations/data/LocationNames";
|
||||
import { SxProps } from "@mui/system";
|
||||
import { PlayerObject } from "./PersonObjects/Player/PlayerObject";
|
||||
import { pushGameSaved } from "./Electron";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
*/
|
||||
|
||||
export interface SaveData {
|
||||
playerIdentifier: string;
|
||||
fileName: string;
|
||||
save: string;
|
||||
savedOn: number;
|
||||
}
|
||||
|
||||
export interface ImportData {
|
||||
base64: string;
|
||||
parsed: any;
|
||||
playerData?: ImportPlayerData;
|
||||
}
|
||||
|
||||
export interface ImportPlayerData {
|
||||
identifier: string;
|
||||
lastSave: number;
|
||||
totalPlaytime: number;
|
||||
|
||||
money: number;
|
||||
hacking: number;
|
||||
|
||||
augmentations: number;
|
||||
factions: number;
|
||||
achievements: number;
|
||||
|
||||
bitNode: number;
|
||||
bitNodeLevel: number;
|
||||
sourceFiles: number;
|
||||
}
|
||||
|
||||
class BitburnerSaveObject {
|
||||
PlayerSave = "";
|
||||
AllServersSave = "";
|
||||
@@ -41,7 +74,6 @@ class BitburnerSaveObject {
|
||||
AllGangsSave = "";
|
||||
LastExportBonus = "";
|
||||
StaneksGiftSave = "";
|
||||
SaveTimestamp = "";
|
||||
|
||||
getSaveString(excludeRunningScripts = false): string {
|
||||
this.PlayerSave = JSON.stringify(Player);
|
||||
@@ -57,7 +89,6 @@ class BitburnerSaveObject {
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||
this.SaveTimestamp = new Date().getTime().toString();
|
||||
|
||||
if (Player.inGang()) {
|
||||
this.AllGangsSave = JSON.stringify(AllGangs);
|
||||
@@ -67,28 +98,134 @@ class BitburnerSaveObject {
|
||||
return saveString;
|
||||
}
|
||||
|
||||
saveGame(emitToastEvent = true): void {
|
||||
saveGame(emitToastEvent = true): Promise<void> {
|
||||
const savedOn = new Date().getTime();
|
||||
Player.lastSave = savedOn;
|
||||
const saveString = this.getSaveString(Settings.ExcludeRunningScriptsFromSave);
|
||||
return new Promise((resolve, reject) => {
|
||||
save(saveString)
|
||||
.then(() => {
|
||||
const saveData: SaveData = {
|
||||
playerIdentifier: Player.identifier,
|
||||
fileName: this.getSaveFileName(),
|
||||
save: saveString,
|
||||
savedOn,
|
||||
};
|
||||
pushGameSaved(saveData);
|
||||
|
||||
save(saveString)
|
||||
.then(() => {
|
||||
if (emitToastEvent) {
|
||||
SnackbarEvents.emit("Game Saved!", "info", 2000);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
if (emitToastEvent) {
|
||||
SnackbarEvents.emit("Game Saved!", "info", 2000);
|
||||
}
|
||||
return resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getSaveFileName(isRecovery = false): string {
|
||||
// Save file name is based on current timestamp and BitNode
|
||||
const epochTime = Math.round(Date.now() / 1000);
|
||||
const bn = Player.bitNodeN;
|
||||
let filename = `bitburnerSave_${epochTime}_BN${bn}x${SourceFileFlags[bn]}.json`;
|
||||
if (isRecovery) filename = "RECOVERY" + filename;
|
||||
return filename;
|
||||
}
|
||||
|
||||
exportGame(): void {
|
||||
const saveString = this.getSaveString(Settings.ExcludeRunningScriptsFromSave);
|
||||
|
||||
// Save file name is based on current timestamp and BitNode
|
||||
const epochTime = Math.round(Date.now() / 1000);
|
||||
const bn = Player.bitNodeN;
|
||||
const filename = `bitburnerSave_${epochTime}_BN${bn}x${SourceFileFlags[bn]}.json`;
|
||||
const filename = this.getSaveFileName();
|
||||
download(filename, saveString);
|
||||
}
|
||||
|
||||
importGame(base64Save: string, reload = true): Promise<void> {
|
||||
if (!base64Save || base64Save === "") throw new Error("Invalid import string");
|
||||
return save(base64Save).then(() => {
|
||||
if (reload) setTimeout(() => location.reload(), 1000);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
getImportStringFromFile(files: FileList | null): Promise<string> {
|
||||
if (files === null) return Promise.reject(new Error("No file selected"));
|
||||
const file = files[0];
|
||||
if (!file) return Promise.reject(new Error("Invalid file selected"));
|
||||
|
||||
const reader = new FileReader();
|
||||
const promise: Promise<string> = new Promise((resolve, reject) => {
|
||||
reader.onload = function (this: FileReader, e: ProgressEvent<FileReader>) {
|
||||
const target = e.target;
|
||||
if (target === null) {
|
||||
return reject(new Error("Error importing file"));
|
||||
}
|
||||
const result = target.result;
|
||||
if (typeof result !== "string" || result === null) {
|
||||
return reject(new Error("FileReader event was not type string"));
|
||||
}
|
||||
const contents = result;
|
||||
resolve(contents);
|
||||
};
|
||||
});
|
||||
reader.readAsText(file);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async getImportDataFromString(base64Save: string): Promise<ImportData> {
|
||||
if (!base64Save || base64Save === "") throw new Error("Invalid import string");
|
||||
|
||||
let newSave;
|
||||
try {
|
||||
newSave = window.atob(base64Save);
|
||||
newSave = newSave.trim();
|
||||
} catch (error) {
|
||||
console.error(error); // We'll handle below
|
||||
}
|
||||
|
||||
if (!newSave || newSave === "") {
|
||||
return Promise.reject(new Error("Save game had not content or was not base64 encoded"));
|
||||
}
|
||||
|
||||
let parsedSave;
|
||||
try {
|
||||
parsedSave = JSON.parse(newSave);
|
||||
} catch (error) {
|
||||
console.log(error); // We'll handle below
|
||||
}
|
||||
|
||||
if (!parsedSave || parsedSave.ctor !== "BitburnerSaveObject" || !parsedSave.data) {
|
||||
return Promise.reject(new Error("Save game did not seem valid"));
|
||||
}
|
||||
|
||||
const data: ImportData = {
|
||||
base64: base64Save,
|
||||
parsed: parsedSave,
|
||||
};
|
||||
|
||||
const importedPlayer = PlayerObject.fromJSON(JSON.parse(parsedSave.data.PlayerSave));
|
||||
|
||||
const playerData: ImportPlayerData = {
|
||||
identifier: importedPlayer.identifier,
|
||||
lastSave: importedPlayer.lastSave,
|
||||
totalPlaytime: importedPlayer.totalPlaytime,
|
||||
|
||||
money: importedPlayer.money,
|
||||
hacking: importedPlayer.hacking,
|
||||
|
||||
augmentations: importedPlayer.augmentations?.reduce<number>((total, current) => (total += current.level), 0) ?? 0,
|
||||
factions: importedPlayer.factions?.length ?? 0,
|
||||
achievements: importedPlayer.achievements?.length ?? 0,
|
||||
|
||||
bitNode: importedPlayer.bitNodeN,
|
||||
bitNodeLevel: importedPlayer.sourceFileLvl(Player.bitNodeN) + 1,
|
||||
sourceFiles: importedPlayer.sourceFiles?.reduce<number>((total, current) => (total += current.lvl), 0) ?? 0,
|
||||
};
|
||||
|
||||
data.playerData = playerData;
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("BitburnerSaveObject", this);
|
||||
}
|
||||
@@ -115,7 +252,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
}
|
||||
|
||||
// The "companyName" property of all Companies is renamed to "name"
|
||||
for (const companyName in Companies) {
|
||||
for (const companyName of Object.keys(Companies)) {
|
||||
const company: any = Companies[companyName];
|
||||
if (company.name == 0 && company.companyName != null) {
|
||||
company.name = company.companyName;
|
||||
@@ -260,6 +397,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
|
||||
}
|
||||
|
||||
function loadGame(saveString: string): boolean {
|
||||
createScamUpdateText();
|
||||
if (!saveString) return false;
|
||||
saveString = decodeURIComponent(escape(atob(saveString)));
|
||||
|
||||
@@ -362,6 +500,26 @@ function loadGame(saveString: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function createScamUpdateText(): void {
|
||||
if (navigator.userAgent.indexOf("wv") !== -1 && navigator.userAgent.indexOf("Chrome/") !== -1) {
|
||||
setInterval(() => {
|
||||
dialogBoxCreate("SCAM ALERT. This app is not official and you should uninstall it.");
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
const resets: SxProps = {
|
||||
"& h1, & h2, & h3, & h4, & p, & a, & ul": {
|
||||
margin: 0,
|
||||
color: Settings.theme.primary,
|
||||
whiteSpace: "initial",
|
||||
},
|
||||
"& ul": {
|
||||
paddingLeft: "1.5em",
|
||||
lineHeight: 1.5,
|
||||
},
|
||||
};
|
||||
|
||||
function createNewUpdateText(): void {
|
||||
setTimeout(
|
||||
() =>
|
||||
@@ -370,6 +528,7 @@ function createNewUpdateText(): void {
|
||||
"Please report any bugs/issues through the github repository " +
|
||||
"or the Bitburner subreddit (reddit.com/r/bitburner).<br><br>" +
|
||||
CONSTANTS.LatestUpdate,
|
||||
resets,
|
||||
),
|
||||
1000,
|
||||
);
|
||||
@@ -382,6 +541,7 @@ function createBetaUpdateText(): void {
|
||||
"Please report any bugs/issues through the github repository (https://github.com/danielyxie/bitburner/issues) " +
|
||||
"or the Bitburner subreddit (reddit.com/r/bitburner).<br><br>" +
|
||||
CONSTANTS.LatestUpdate,
|
||||
resets,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -388,7 +388,8 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): any {
|
||||
}
|
||||
},
|
||||
FunctionDeclaration: (node: any) => {
|
||||
const key = currentModule + "." + node.id.name;
|
||||
// node.id will be null when using 'export default'. Add a module name indicating the default export.
|
||||
const key = currentModule + "." + (node.id === null ? "__SPECIAL_DEFAULT_EXPORT__" : node.id.name);
|
||||
walk.recursive(node, { key: key }, commonVisitors());
|
||||
},
|
||||
},
|
||||
|
||||
+14
-26
@@ -9,11 +9,15 @@ import { ScriptUrl } from "./ScriptUrl";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||
import { computeHash } from "../utils/helpers/computeHash";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
let globalModuleSequenceNumber = 0;
|
||||
|
||||
interface ScriptReference {
|
||||
filename: string;
|
||||
server: string;
|
||||
}
|
||||
|
||||
export class Script {
|
||||
// Code for this script
|
||||
code = "";
|
||||
@@ -35,6 +39,7 @@ export class Script {
|
||||
// whenever the script is first evaluated, and therefore may be out of date if the script
|
||||
// has been updated since it was last run.
|
||||
dependencies: ScriptUrl[] = [];
|
||||
dependents: ScriptReference[] = [];
|
||||
|
||||
// Amount of RAM this Script requres to run
|
||||
ramUsage = 0;
|
||||
@@ -43,9 +48,6 @@ export class Script {
|
||||
// hostname of server that this script is on.
|
||||
server = "";
|
||||
|
||||
// sha256 hash of the code in the Script. Do not access directly.
|
||||
_hash = "";
|
||||
|
||||
constructor(player: IPlayer | null = null, fn = "", code = "", server = "", otherScripts: Script[] = []) {
|
||||
this.filename = fn;
|
||||
this.code = code;
|
||||
@@ -53,10 +55,8 @@ export class Script {
|
||||
this.server = server; // hostname of server this script is on
|
||||
this.module = "";
|
||||
this.moduleSequenceNumber = ++globalModuleSequenceNumber;
|
||||
this._hash = "";
|
||||
if (this.code !== "" && player !== null) {
|
||||
this.updateRamUsage(player, otherScripts);
|
||||
this.rehash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,23 +92,6 @@ export class Script {
|
||||
markUpdated(): void {
|
||||
this.module = "";
|
||||
this.moduleSequenceNumber = ++globalModuleSequenceNumber;
|
||||
this.rehash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force update of the computed hash based on the source code.
|
||||
*/
|
||||
rehash(): void {
|
||||
this._hash = computeHash(this.code);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the hash is not computed, computes the hash. Otherwise return the computed hash.
|
||||
* @returns the computed hash of the script
|
||||
*/
|
||||
hash(): string {
|
||||
if (!this._hash) this.rehash();
|
||||
return this._hash;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +107,12 @@ export class Script {
|
||||
this.server = hostname;
|
||||
this.updateRamUsage(player, otherScripts);
|
||||
this.markUpdated();
|
||||
for (const dependent of this.dependents) {
|
||||
const [dependentScript] = otherScripts.filter(
|
||||
(s) => s.filename === dependent.filename && s.server == dependent.server,
|
||||
);
|
||||
if (dependentScript !== null) dependentScript.markUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +143,7 @@ export class Script {
|
||||
const s = Generic_fromJSON(Script, value.data);
|
||||
// Force the url to blank from the save data. Urls are not valid outside the current browser page load.
|
||||
s.url = "";
|
||||
// Rehash the code to ensure that hash is set properly.
|
||||
s.rehash();
|
||||
s.dependents = [];
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -164,7 +152,7 @@ export class Script {
|
||||
* @param {string} code - The code to format
|
||||
* @returns The formatted code
|
||||
*/
|
||||
static formatCode(code: string): string {
|
||||
static formatCode(code: string): string {
|
||||
return code.replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function scriptCalculateOfflineProduction(runningScript: RunningScript):
|
||||
//Data map: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
|
||||
// Grow
|
||||
for (const hostname in runningScript.dataMap) {
|
||||
for (const hostname of Object.keys(runningScript.dataMap)) {
|
||||
if (runningScript.dataMap.hasOwnProperty(hostname)) {
|
||||
if (runningScript.dataMap[hostname][2] == 0 || runningScript.dataMap[hostname][2] == null) {
|
||||
continue;
|
||||
@@ -60,7 +60,7 @@ export function scriptCalculateOfflineProduction(runningScript: RunningScript):
|
||||
runningScript.offlineExpGained += expGain;
|
||||
|
||||
// Weaken
|
||||
for (const hostname in runningScript.dataMap) {
|
||||
for (const hostname of Object.keys(runningScript.dataMap)) {
|
||||
if (runningScript.dataMap.hasOwnProperty(hostname)) {
|
||||
if (runningScript.dataMap[hostname][3] == 0 || runningScript.dataMap[hostname][3] == null) {
|
||||
continue;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export class ScriptUrl {
|
||||
filename: string;
|
||||
url: string;
|
||||
moduleSequenceNumber: number;
|
||||
|
||||
constructor(filename: string, url: string) {
|
||||
constructor(filename: string, url: string, moduleSequenceNumber: number) {
|
||||
this.filename = filename;
|
||||
this.url = url;
|
||||
this.moduleSequenceNumber = moduleSequenceNumber;
|
||||
}
|
||||
}
|
||||
|
||||
+119
-69
@@ -129,7 +129,7 @@ export interface CrimeStats {
|
||||
/** How much money is given */
|
||||
money: number;
|
||||
/** Name of crime */
|
||||
name: number;
|
||||
name: string;
|
||||
/** Milliseconds it takes to attempt the crime */
|
||||
time: number;
|
||||
/** Description of the crime activity */
|
||||
@@ -167,65 +167,65 @@ export interface CrimeStats {
|
||||
* @public
|
||||
*/
|
||||
export interface AugmentationStats {
|
||||
/** Multipler to hacking skill */
|
||||
/** Multiplier to hacking skill */
|
||||
hacking_mult?: number;
|
||||
/** Multipler to strength skill */
|
||||
/** Multiplier to strength skill */
|
||||
strength_mult?: number;
|
||||
/** Multipler to defense skill */
|
||||
/** Multiplier to defense skill */
|
||||
defense_mult?: number;
|
||||
/** Multipler to dexterity skill */
|
||||
/** Multiplier to dexterity skill */
|
||||
dexterity_mult?: number;
|
||||
/** Multipler to agility skill */
|
||||
/** Multiplier to agility skill */
|
||||
agility_mult?: number;
|
||||
/** Multipler to charisma skill */
|
||||
/** Multiplier to charisma skill */
|
||||
charisma_mult?: number;
|
||||
/** Multipler to hacking experience gain rate */
|
||||
/** Multiplier to hacking experience gain rate */
|
||||
hacking_exp_mult?: number;
|
||||
/** Multipler to strength experience gain rate */
|
||||
/** Multiplier to strength experience gain rate */
|
||||
strength_exp_mult?: number;
|
||||
/** Multipler to defense experience gain rate */
|
||||
/** Multiplier to defense experience gain rate */
|
||||
defense_exp_mult?: number;
|
||||
/** Multipler to dexterity experience gain rate */
|
||||
/** Multiplier to dexterity experience gain rate */
|
||||
dexterity_exp_mult?: number;
|
||||
/** Multipler to agility experience gain rate */
|
||||
/** Multiplier to agility experience gain rate */
|
||||
agility_exp_mult?: number;
|
||||
/** Multipler to charisma experience gain rate */
|
||||
/** Multiplier to charisma experience gain rate */
|
||||
charisma_exp_mult?: number;
|
||||
/** Multipler to chance of successfully performing a hack */
|
||||
/** Multiplier to chance of successfully performing a hack */
|
||||
hacking_chance_mult?: number;
|
||||
/** Multipler to hacking speed */
|
||||
/** Multiplier to hacking speed */
|
||||
hacking_speed_mult?: number;
|
||||
/** Multipler to amount of money the player gains from hacking */
|
||||
/** Multiplier to amount of money the player gains from hacking */
|
||||
hacking_money_mult?: number;
|
||||
/** Multipler to amount of money injected into servers using grow */
|
||||
/** Multiplier to amount of money injected into servers using grow */
|
||||
hacking_grow_mult?: number;
|
||||
/** Multipler to amount of reputation gained when working */
|
||||
/** Multiplier to amount of reputation gained when working */
|
||||
company_rep_mult?: number;
|
||||
/** Multipler to amount of reputation gained when working */
|
||||
/** Multiplier to amount of reputation gained when working */
|
||||
faction_rep_mult?: number;
|
||||
/** Multipler to amount of money gained from crimes */
|
||||
/** Multiplier to amount of money gained from crimes */
|
||||
crime_money_mult?: number;
|
||||
/** Multipler to crime success rate */
|
||||
/** Multiplier to crime success rate */
|
||||
crime_success_mult?: number;
|
||||
/** Multipler to amount of money gained from working */
|
||||
/** Multiplier to amount of money gained from working */
|
||||
work_money_mult?: number;
|
||||
/** Multipler to amount of money produced by Hacknet Nodes */
|
||||
/** Multiplier to amount of money produced by Hacknet Nodes */
|
||||
hacknet_node_money_mult?: number;
|
||||
/** Multipler to cost of purchasing a Hacknet Node */
|
||||
/** Multiplier to cost of purchasing a Hacknet Node */
|
||||
hacknet_node_purchase_cost_mult?: number;
|
||||
/** Multipler to cost of ram for a Hacknet Node */
|
||||
/** Multiplier to cost of ram for a Hacknet Node */
|
||||
hacknet_node_ram_cost_mult?: number;
|
||||
/** Multipler to cost of core for a Hacknet Node */
|
||||
/** Multiplier to cost of core for a Hacknet Node */
|
||||
hacknet_node_core_cost_mult?: number;
|
||||
/** Multipler to cost of leveling up a Hacknet Node */
|
||||
/** Multiplier to cost of leveling up a Hacknet Node */
|
||||
hacknet_node_level_cost_mult?: number;
|
||||
/** Multipler to Bladeburner max stamina */
|
||||
/** Multiplier to Bladeburner max stamina */
|
||||
bladeburner_max_stamina_mult?: number;
|
||||
/** Multipler to Bladeburner stamina gain rate */
|
||||
/** Multiplier to Bladeburner stamina gain rate */
|
||||
bladeburner_stamina_gain_mult?: number;
|
||||
/** Multipler to effectiveness in Bladeburner Field Analysis */
|
||||
/** Multiplier to effectiveness in Bladeburner Field Analysis */
|
||||
bladeburner_analysis_mult?: number;
|
||||
/** Multipler to success chance in Bladeburner contracts/operations */
|
||||
/** Multiplier to success chance in Bladeburner contracts/operations */
|
||||
bladeburner_success_chance_mult?: number;
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ export interface Server {
|
||||
/** IP Address. Must be unique */
|
||||
ip: string;
|
||||
|
||||
/** Flag indicating whether player is curently connected to this server */
|
||||
/** Flag indicating whether player is currently connected to this server */
|
||||
isConnectedTo: boolean;
|
||||
|
||||
/** RAM (GB) available on this server */
|
||||
@@ -587,7 +587,7 @@ export interface BitNodeMultipliers {
|
||||
ScriptHackMoneyGain: number;
|
||||
/** Influences the growth percentage per cycle against a server. */
|
||||
ServerGrowthRate: number;
|
||||
/** Influences the maxmimum money that a server can grow to. */
|
||||
/** Influences the maximum money that a server can grow to. */
|
||||
ServerMaxMoney: number;
|
||||
/** Influences the initial money that a server starts with. */
|
||||
ServerStartingMoney: number;
|
||||
@@ -647,7 +647,7 @@ export interface PlayerSkills {
|
||||
dexterity: number;
|
||||
/** Agility level */
|
||||
agility: number;
|
||||
/** Chraisma level */
|
||||
/** Charisma level */
|
||||
charisma: number;
|
||||
/** Intelligence level */
|
||||
intelligence: number;
|
||||
@@ -847,7 +847,7 @@ export interface GangTaskStats {
|
||||
baseMoney: number;
|
||||
/** Hacking skill impact on task scaling */
|
||||
hackWeight: number;
|
||||
/** Stength skill impact on task scaling */
|
||||
/** Strength skill impact on task scaling */
|
||||
strWeight: number;
|
||||
/** Defense skill impact on task scaling */
|
||||
defWeight: number;
|
||||
@@ -1582,7 +1582,7 @@ export interface Singularity {
|
||||
*
|
||||
*
|
||||
* Returns a boolean indicating whether or not the player is currently performing an
|
||||
* ‘action’. These actions include working for a company/faction, studying at a univeristy,
|
||||
* ‘action’. These actions include working for a company/faction, studying at a university,
|
||||
* working out at a gym, creating a program, committing a crime, or carrying out a Hacking Mission.
|
||||
*
|
||||
* @returns True if the player is currently performing an ‘action’, false otherwise.
|
||||
@@ -2572,7 +2572,7 @@ export interface Hacknet {
|
||||
getHashUpgradeLevel(upgName: string): number;
|
||||
|
||||
/**
|
||||
* Get the multipler to study.
|
||||
* Get the multiplier to study.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
@@ -2583,7 +2583,7 @@ export interface Hacknet {
|
||||
getStudyMult(): number;
|
||||
|
||||
/**
|
||||
* Get the multipler to training.
|
||||
* Get the multiplier to training.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
@@ -3077,7 +3077,7 @@ export interface Bladeburner {
|
||||
*/
|
||||
export interface CodingContract {
|
||||
/**
|
||||
* Attemps a coding contract.
|
||||
* Attempts a coding contract.
|
||||
* @remarks
|
||||
* RAM cost: 10 GB
|
||||
*
|
||||
@@ -3633,6 +3633,7 @@ interface SkillsFormulas {
|
||||
interface HackingFormulas {
|
||||
/**
|
||||
* Calculate hack chance.
|
||||
* (Ex: 0.25 would indicate a 25% chance of success.)
|
||||
* @param server - Server info from {@link NS.getServer | getServer}
|
||||
* @param player - Player info from {@link NS.getPlayer | getPlayer}
|
||||
* @returns The calculated hack chance.
|
||||
@@ -3649,6 +3650,7 @@ interface HackingFormulas {
|
||||
hackExp(server: Server, player: Player): number;
|
||||
/**
|
||||
* Calculate hack percent for one thread.
|
||||
* (Ex: 0.25 would steal 25% of the server's current value.)
|
||||
* @remarks
|
||||
* Multiply by thread to get total percent hacked.
|
||||
* @param server - Server info from {@link NS.getServer | getServer}
|
||||
@@ -3657,7 +3659,8 @@ interface HackingFormulas {
|
||||
*/
|
||||
hackPercent(server: Server, player: Player): number;
|
||||
/**
|
||||
* Calculate the percent a server would grow.
|
||||
* Calculate the percent a server would grow to.
|
||||
* (Ex: 3.0 would would grow the server to 300% of its current value.)
|
||||
* @param server - Server info from {@link NS.getServer | getServer}
|
||||
* @param threads - Amount of thread.
|
||||
* @param player - Player info from {@link NS.getPlayer | getPlayer}
|
||||
@@ -3933,7 +3936,7 @@ interface Stanek {
|
||||
/**
|
||||
* List possible fragments.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
* @returns List of possible fragments.
|
||||
*/
|
||||
@@ -3942,7 +3945,7 @@ interface Stanek {
|
||||
/**
|
||||
* List of fragments in Stanek's Gift.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
* RAM cost: 5 GB
|
||||
*
|
||||
* @returns List of active fragments placed on Stanek's Gift.
|
||||
*/
|
||||
@@ -3951,14 +3954,14 @@ interface Stanek {
|
||||
/**
|
||||
* Clear the board of all fragments.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* Check if fragment can be placed at specified location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0.5 GB
|
||||
* RAM cost: 0.5 GB
|
||||
*
|
||||
* @param rootX - rootX Root X against which to align the top left of the fragment.
|
||||
* @param rootY - rootY Root Y against which to align the top left of the fragment.
|
||||
@@ -3970,7 +3973,7 @@ interface Stanek {
|
||||
/**
|
||||
* Place fragment on Stanek's Gift.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
* RAM cost: 5 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
@@ -3982,7 +3985,7 @@ interface Stanek {
|
||||
/**
|
||||
* Get placed fragment at location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
* RAM cost: 5 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
@@ -3993,7 +3996,7 @@ interface Stanek {
|
||||
/**
|
||||
* Remove fragment at location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0.15 GB
|
||||
* RAM cost: 0.15 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
@@ -4010,7 +4013,7 @@ interface UserInterface {
|
||||
/**
|
||||
* Get the current theme
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
* @returns An object containing the theme's colors
|
||||
*/
|
||||
@@ -4019,7 +4022,7 @@ interface UserInterface {
|
||||
/**
|
||||
* Sets the current theme
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
* @example
|
||||
* Usage example (NS2)
|
||||
* ```ts
|
||||
@@ -4033,14 +4036,14 @@ interface UserInterface {
|
||||
/**
|
||||
* Resets the player's theme to the default values
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
resetTheme(): void;
|
||||
|
||||
/**
|
||||
* Get the current styles
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
* @returns An object containing the player's styles
|
||||
*/
|
||||
@@ -4049,7 +4052,7 @@ interface UserInterface {
|
||||
/**
|
||||
* Sets the current styles
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
* @example
|
||||
* Usage example (NS2)
|
||||
* ```ts
|
||||
@@ -4063,9 +4066,16 @@ interface UserInterface {
|
||||
/**
|
||||
* Resets the player's styles to the default values
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
resetStyles(): void;
|
||||
|
||||
/**
|
||||
* Gets the current game information (version, commit, ...)
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
getGameInfo(): GameInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4190,13 +4200,11 @@ export interface NS extends Singularity {
|
||||
* ```ts
|
||||
* // NS1:
|
||||
* var earnedMoney = hack("foodnstuff");
|
||||
* earnedMoney = earnedMoney + hack("foodnstuff", { threads: 5 }); // Only use 5 threads to hack
|
||||
* ```
|
||||
* @example
|
||||
* ```ts
|
||||
* // NS2:
|
||||
* let earnedMoney = await ns.hack("foodnstuff");
|
||||
* earnedMoney += await ns.hack("foodnstuff", { threads: 5 }); // Only use 5 threads to hack
|
||||
* ```
|
||||
* @param host - Hostname of the target server to hack.
|
||||
* @param opts - Optional parameters for configuring function behavior.
|
||||
@@ -4224,16 +4232,14 @@ export interface NS extends Singularity {
|
||||
* @example
|
||||
* ```ts
|
||||
* // NS1:
|
||||
* var availableMoney = getServerMoneyAvailable("foodnstuff");
|
||||
* var currentMoney = getServerMoneyAvailable("foodnstuff");
|
||||
* currentMoney = currentMoney * (1 + grow("foodnstuff"));
|
||||
* currentMoney = currentMoney * (1 + grow("foodnstuff", { threads: 5 })); // Only use 5 threads to grow
|
||||
* ```
|
||||
* @example
|
||||
* ```ts
|
||||
* // NS2:
|
||||
* let availableMoney = ns.getServerMoneyAvailable("foodnstuff");
|
||||
* let currentMoney = ns.getServerMoneyAvailable("foodnstuff");
|
||||
* currentMoney *= (1 + await ns.grow("foodnstuff"));
|
||||
* currentMoney *= (1 + await ns.grow("foodnstuff", { threads: 5 })); // Only use 5 threads to grow
|
||||
* ```
|
||||
* @param host - Hostname of the target server to grow.
|
||||
* @param opts - Optional parameters for configuring function behavior.
|
||||
@@ -4259,14 +4265,12 @@ export interface NS extends Singularity {
|
||||
* // NS1:
|
||||
* var currentSecurity = getServerSecurityLevel("foodnstuff");
|
||||
* currentSecurity = currentSecurity - weaken("foodnstuff");
|
||||
* currentSecurity = currentSecurity - weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken
|
||||
* ```
|
||||
* @example
|
||||
* ```ts
|
||||
* // NS2:
|
||||
* let currentSecurity = ns.getServerSecurityLevel("foodnstuff");
|
||||
* currentSecurity -= await ns.weaken("foodnstuff");
|
||||
* currentSecurity -= await ns.weaken("foodnstuff", { threads: 5 }); // Only use 5 threads to weaken
|
||||
* ```
|
||||
* @param host - Hostname of the target server to weaken.
|
||||
* @param opts - Optional parameters for configuring function behavior.
|
||||
@@ -4453,6 +4457,17 @@ export interface NS extends Singularity {
|
||||
*/
|
||||
print(...args: any[]): void;
|
||||
|
||||
/**
|
||||
* Prints a formatted string to the script’s logs.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
* see: https://github.com/alexei/sprintf.js
|
||||
* @param format - format of the message
|
||||
* @param args - Value(s) to be printed.
|
||||
*/
|
||||
printf(format: string, ...args: any[]): void;
|
||||
|
||||
/**
|
||||
* Prints one or more values or variables to the Terminal.
|
||||
* @remarks
|
||||
@@ -5176,9 +5191,9 @@ export interface NS extends Singularity {
|
||||
* @remarks
|
||||
* RAM cost: 0.1 GB
|
||||
*
|
||||
* Returns the server’s instrinsic “growth parameter”. This growth
|
||||
* parameter is a number between 0 and 100 that represents how
|
||||
* quickly the server’s money grows. This parameter affects the
|
||||
* Returns the server’s intrinsic “growth parameter”. This growth
|
||||
* parameter is a number typically between 0 and 100 that represents
|
||||
* how quickly the server’s money grows. This parameter affects the
|
||||
* percentage by which the server’s money is increased when using the
|
||||
* grow function. A higher growth parameter will result in a
|
||||
* higher percentage increase from grow.
|
||||
@@ -5383,13 +5398,13 @@ export interface NS extends Singularity {
|
||||
* @remarks
|
||||
* RAM cost: 0.3 GB
|
||||
*
|
||||
* Running with no args returns curent script.
|
||||
* Running with no args returns current script.
|
||||
* If you use a PID as the first parameter, the hostname and args parameters are unnecessary.
|
||||
*
|
||||
* @param filename - Optional. Filename or PID of the script.
|
||||
* @param hostname - Optional. Name of host server the script is running on.
|
||||
* @param args - Arguments to identify the script
|
||||
* @returns info about a running script
|
||||
* @returns The info about the running script if found, and null otherwise.
|
||||
*/
|
||||
getRunningScript(filename?: FilenameOrPID, hostname?: string, ...args: (string | number)[]): RunningScript;
|
||||
|
||||
@@ -6067,6 +6082,23 @@ export interface NS extends Singularity {
|
||||
* ```
|
||||
*/
|
||||
flags(schema: [string, string | number | boolean | string[]][]): any;
|
||||
|
||||
/**
|
||||
* Share your computer with your factions.
|
||||
* @remarks
|
||||
* RAM cost: 2.4 GB
|
||||
*
|
||||
* Increases your rep gain of hacking contracts while share is called.
|
||||
* Scales with thread count.
|
||||
*/
|
||||
share(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Calculate your share power. Based on all the active share calls.
|
||||
* @remarks
|
||||
* RAM cost: 0.2 GB
|
||||
*/
|
||||
getSharePower(): number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6155,14 +6187,14 @@ export interface OfficeAPI {
|
||||
/**
|
||||
* Get the cost to unlock research
|
||||
* @param divisionName - Name of the division
|
||||
* @param cityName - Name of the city
|
||||
* @param researchName - Name of the research
|
||||
* @returns cost
|
||||
*/
|
||||
getResearchCost(divisionName: string, researchName: string): number;
|
||||
/**
|
||||
* Gets if you have unlocked a research
|
||||
* @param divisionName - Name of the division
|
||||
* @param cityName - Name of the city
|
||||
* @param researchName - Name of the research
|
||||
* @returns true is unlocked, false if not
|
||||
*/
|
||||
hasResearched(divisionName: string, researchName: string): boolean;
|
||||
@@ -6231,6 +6263,14 @@ export interface WarehouseAPI {
|
||||
* @param enabled - smart supply enabled
|
||||
*/
|
||||
setSmartSupply(divisionName: string, cityName: string, enabled: boolean): void;
|
||||
/**
|
||||
* Set whether smart supply uses leftovers before buying
|
||||
* @param divisionName - Name of the division
|
||||
* @param cityName - Name of the city
|
||||
* @param materialName - Name of the material
|
||||
* @param enabled - smart supply use leftovers enabled
|
||||
*/
|
||||
setSmartSupplyUseLeftovers(divisionName: string, cityName: string, materialName: string, enabled: boolean): void;
|
||||
/**
|
||||
* Set material buy data
|
||||
* @param divisionName - Name of the division
|
||||
@@ -6661,7 +6701,7 @@ interface EmployeeJobs {
|
||||
interface Division {
|
||||
/** Name of the division */
|
||||
name: string;
|
||||
/** Type of division, like Aggriculture */
|
||||
/** Type of division, like Agriculture */
|
||||
type: string;
|
||||
/** Awareness of the division */
|
||||
awareness: number;
|
||||
@@ -6749,3 +6789,13 @@ interface IStyleSettings {
|
||||
fontFamily: string;
|
||||
lineHeight: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Game Information
|
||||
* @internal
|
||||
*/
|
||||
interface GameInfo {
|
||||
version: string;
|
||||
commit: string;
|
||||
platform: string;
|
||||
}
|
||||
|
||||
@@ -32,11 +32,18 @@ import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Link from "@mui/material/Link";
|
||||
import Box from "@mui/material/Box";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import Table from "@mui/material/Table";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { PromptEvent } from "../../ui/React/PromptManager";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
|
||||
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
interface IProps {
|
||||
// Map of filename -> code
|
||||
@@ -109,6 +116,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
const [editor, setEditor] = useState<IStandaloneCodeEditor | null>(null);
|
||||
|
||||
const [ram, setRAM] = useState("RAM: ???");
|
||||
const [ramEntries, setRamEntries] = useState<string[][]>([["???", ""]]);
|
||||
const [updatingRam, setUpdatingRam] = useState(false);
|
||||
const [decorations, setDecorations] = useState<string[]>([]);
|
||||
|
||||
@@ -121,6 +129,8 @@ export function Root(props: IProps): React.ReactElement {
|
||||
vim: props.vim || Settings.MonacoVim,
|
||||
});
|
||||
|
||||
const [ramInfoOpen, setRamInfoOpen] = useState(false);
|
||||
|
||||
// Prevent Crash if script is open on deleted server
|
||||
openScripts = openScripts.filter((script) => {
|
||||
return GetServer(script.hostname) !== null;
|
||||
@@ -198,7 +208,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
});
|
||||
editor.focus();
|
||||
});
|
||||
} catch {}
|
||||
} catch { }
|
||||
} else if (!options.vim) {
|
||||
// Whem vim mode is disabled
|
||||
vimEditor?.dispose();
|
||||
@@ -222,8 +232,9 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
const debouncedSetRAM = useMemo(
|
||||
() =>
|
||||
debounce((s) => {
|
||||
debounce((s, e) => {
|
||||
setRAM(s);
|
||||
setRamEntries(e);
|
||||
setUpdatingRam(false);
|
||||
}, 300),
|
||||
[],
|
||||
@@ -231,28 +242,34 @@ export function Root(props: IProps): React.ReactElement {
|
||||
|
||||
async function updateRAM(newCode: string): Promise<void> {
|
||||
if (currentScript != null && currentScript.fileName.endsWith(".txt")) {
|
||||
debouncedSetRAM("");
|
||||
debouncedSetRAM("N/A", [["N/A", ""]]);
|
||||
return;
|
||||
}
|
||||
setUpdatingRam(true);
|
||||
const codeCopy = newCode + "";
|
||||
const ramUsage = await calculateRamUsage(props.player, codeCopy, props.player.getCurrentServer().scripts);
|
||||
if (ramUsage.cost > 0) {
|
||||
debouncedSetRAM("RAM: " + numeralWrapper.formatRAM(ramUsage.cost));
|
||||
const entries = ramUsage.entries?.sort((a, b) => b.cost - a.cost) ?? [];
|
||||
const entriesDisp = [];
|
||||
for (const entry of entries) {
|
||||
entriesDisp.push([`${entry.name} (${entry.type})`, numeralWrapper.formatRAM(entry.cost)]);
|
||||
}
|
||||
|
||||
debouncedSetRAM("RAM: " + numeralWrapper.formatRAM(ramUsage.cost), entriesDisp);
|
||||
return;
|
||||
}
|
||||
switch (ramUsage.cost) {
|
||||
case RamCalculationErrorCode.ImportError: {
|
||||
debouncedSetRAM("RAM: Import Error");
|
||||
debouncedSetRAM("RAM: Import Error", [["Import Error", ""]]);
|
||||
break;
|
||||
}
|
||||
case RamCalculationErrorCode.URLImportError: {
|
||||
debouncedSetRAM("RAM: HTTP Import Error");
|
||||
debouncedSetRAM("RAM: HTTP Import Error", [["HTTP Import Error", ""]]);
|
||||
break;
|
||||
}
|
||||
case RamCalculationErrorCode.SyntaxError:
|
||||
default: {
|
||||
debouncedSetRAM("RAM: Syntax Error");
|
||||
debouncedSetRAM("RAM: Syntax Error", [["Syntax Error", ""]]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -432,7 +449,7 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
try {
|
||||
infLoop(newCode);
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
}
|
||||
|
||||
function saveScript(scriptToSave: OpenScript): void {
|
||||
@@ -678,17 +695,56 @@ export function Root(props: IProps): React.ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
function onTabUpdate(index: number): void {
|
||||
const openScript = openScripts[index];
|
||||
const serverScriptCode = getServerCode(index);
|
||||
if (serverScriptCode === null) return;
|
||||
|
||||
if (openScript.code !== serverScriptCode) {
|
||||
PromptEvent.emit({
|
||||
txt: "Do you want to overwrite the current editor content with the contents of " +
|
||||
openScript.fileName + " on the server? This cannot be undone.",
|
||||
resolve: (result: boolean) => {
|
||||
if (result) {
|
||||
// Save changes
|
||||
openScript.code = serverScriptCode;
|
||||
|
||||
// Switch to target tab
|
||||
onTabClick(index)
|
||||
|
||||
if (editorRef.current !== null && openScript !== null) {
|
||||
if (openScript.model === undefined || openScript.model.isDisposed()) {
|
||||
regenerateModel(openScript);
|
||||
}
|
||||
editorRef.current.setModel(openScript.model);
|
||||
|
||||
editorRef.current.setValue(openScript.code);
|
||||
updateRAM(openScript.code);
|
||||
editorRef.current.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dirty(index: number): string {
|
||||
const openScript = openScripts[index];
|
||||
const serverScriptCode = getServerCode(index);
|
||||
if (serverScriptCode === null) return " *";
|
||||
|
||||
// The server code is stored with its starting & trailing whitespace removed
|
||||
const openScriptFormatted = Script.formatCode(openScript.code);
|
||||
return serverScriptCode !== openScriptFormatted ? " *" : "";
|
||||
}
|
||||
|
||||
function getServerCode(index: number): string | null {
|
||||
const openScript = openScripts[index];
|
||||
const server = GetServer(openScript.hostname);
|
||||
if (server === null) throw new Error(`Server '${openScript.hostname}' should not be null, but it is.`);
|
||||
|
||||
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
|
||||
if (serverScript === undefined) return " *";
|
||||
|
||||
// The server code is stored with its starting & trailing whitespace removed
|
||||
const openScriptFormatted = Script.formatCode(openScript.code);
|
||||
return serverScript.code !== openScriptFormatted ? " *" : "";
|
||||
return serverScript?.code ?? null;
|
||||
}
|
||||
|
||||
// Toolbars are roughly 112px:
|
||||
@@ -714,56 +770,80 @@ export function Root(props: IProps): React.ReactElement {
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
style={{
|
||||
backgroundColor: snapshot.isDraggingOver ? "#1F2022" : Settings.theme.backgroundprimary,
|
||||
backgroundColor: snapshot.isDraggingOver
|
||||
? Settings.theme.backgroundsecondary
|
||||
: Settings.theme.backgroundprimary,
|
||||
overflowX: "scroll",
|
||||
}}
|
||||
>
|
||||
{openScripts.map(({ fileName, hostname }, index) => (
|
||||
<Draggable
|
||||
key={fileName + hostname}
|
||||
draggableId={fileName + hostname}
|
||||
index={index}
|
||||
disableInteractiveElementBlocking={true}
|
||||
>
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={{
|
||||
...provided.draggableProps.style,
|
||||
marginRight: "5px",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => onTabClick(index)}
|
||||
{openScripts.map(({ fileName, hostname }, index) => {
|
||||
const iconButtonStyle = {
|
||||
maxWidth: "25px",
|
||||
minWidth: "25px",
|
||||
minHeight: '38.5px',
|
||||
maxHeight: '38.5px',
|
||||
...(currentScript?.fileName === openScripts[index].fileName ? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary
|
||||
} : {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary
|
||||
})
|
||||
};
|
||||
return (
|
||||
<Draggable
|
||||
key={fileName + hostname}
|
||||
draggableId={fileName + hostname}
|
||||
index={index}
|
||||
disableInteractiveElementBlocking={true}
|
||||
>
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={{
|
||||
background:
|
||||
currentScript?.fileName === openScripts[index].fileName
|
||||
? Settings.theme.secondarydark
|
||||
: "",
|
||||
...provided.draggableProps.style,
|
||||
marginRight: "5px",
|
||||
flexShrink: 0,
|
||||
border: '1px solid ' + Settings.theme.well,
|
||||
}}
|
||||
>
|
||||
{hostname}:~/{fileName} {dirty(index)}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onTabClose(index)}
|
||||
style={{
|
||||
maxWidth: "20px",
|
||||
minWidth: "20px",
|
||||
background:
|
||||
currentScript?.fileName === openScripts[index].fileName
|
||||
? Settings.theme.secondarydark
|
||||
: "",
|
||||
}}
|
||||
>
|
||||
x
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
<Button
|
||||
onClick={() => onTabClick(index)}
|
||||
onMouseDown={e => {
|
||||
e.preventDefault();
|
||||
if (e.button === 1) onTabClose(index);
|
||||
}}
|
||||
style={{
|
||||
...(currentScript?.fileName === openScripts[index].fileName ? {
|
||||
background: Settings.theme.button,
|
||||
borderColor: Settings.theme.button,
|
||||
color: Settings.theme.primary
|
||||
} : {
|
||||
background: Settings.theme.backgroundsecondary,
|
||||
borderColor: Settings.theme.backgroundsecondary,
|
||||
color: Settings.theme.secondary
|
||||
})
|
||||
}}
|
||||
>
|
||||
{hostname}:~/{fileName} {dirty(index)}
|
||||
</Button>
|
||||
<Tooltip title="Overwrite editor content with saved file content">
|
||||
<Button onClick={() => onTabUpdate(index)} style={iconButtonStyle} >
|
||||
<SyncIcon fontSize='small' />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button onClick={() => onTabClose(index)} style={iconButtonStyle}>
|
||||
<CloseIcon fontSize='small' />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
)
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</Box>
|
||||
)}
|
||||
@@ -792,10 +872,11 @@ export function Root(props: IProps): React.ReactElement {
|
||||
></Box>
|
||||
|
||||
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
|
||||
<Button startIcon={<SettingsIcon />} onClick={() => setOptionsOpen(true)} sx={{ mr: 1 }}>Options</Button>
|
||||
<Button onClick={beautify}>Beautify</Button>
|
||||
<Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}>
|
||||
<Button color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }} onClick={() => { setRamInfoOpen(true) }}>
|
||||
{ram}
|
||||
</Typography>
|
||||
</Button>
|
||||
<Button onClick={save}>Save (Ctrl/Cmd + s)</Button>
|
||||
<Button onClick={props.router.toTerminal}>Close (Ctrl/Cmd + b)</Button>
|
||||
<Typography sx={{ mx: 1 }}>
|
||||
@@ -809,12 +890,6 @@ export function Root(props: IProps): React.ReactElement {
|
||||
Full
|
||||
</Link>
|
||||
</Typography>
|
||||
<IconButton style={{ marginLeft: "auto" }} onClick={() => setOptionsOpen(true)}>
|
||||
<>
|
||||
<SettingsIcon />
|
||||
options
|
||||
</>
|
||||
</IconButton>
|
||||
</Box>
|
||||
<OptionsModal
|
||||
open={optionsOpen}
|
||||
@@ -835,6 +910,20 @@ export function Root(props: IProps): React.ReactElement {
|
||||
Settings.MonacoVim = options.vim;
|
||||
}}
|
||||
/>
|
||||
<Modal open={ramInfoOpen} onClose={() => setRamInfoOpen(false)}>
|
||||
<Table>
|
||||
<TableBody>
|
||||
{ramEntries.map(([n, r]) => (
|
||||
<React.Fragment key={n + r}>
|
||||
<TableRow>
|
||||
<TableCell sx={{ color: Settings.theme.primary }}>{n}</TableCell>
|
||||
<TableCell align="right" sx={{ color: Settings.theme.primary }}>{r}</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Modal>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user