mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
UI: Share RAM to boost reputation gain (#1862)
This commit is contained in:
@@ -22,6 +22,7 @@ import { GangButton } from "./GangButton";
|
||||
import { FactionWork } from "../../Work/FactionWork";
|
||||
import { useCycleRerender } from "../../ui/React/hooks";
|
||||
import { repNeededToDonate } from "../formulas/donation";
|
||||
import { ShareOption } from "./ShareOption";
|
||||
|
||||
type FactionRootProps = {
|
||||
faction: Faction;
|
||||
@@ -45,7 +46,7 @@ const securityWorkInfo =
|
||||
"You will gain exp for all combat stats and hacking.";
|
||||
const augmentationsInfo =
|
||||
"As your reputation with this faction rises, you will " +
|
||||
"unlock Augmentations, which you can purchase to enhance " +
|
||||
"unlock augmentations, which you can purchase to enhance " +
|
||||
"your abilities.";
|
||||
const sleevePurchasesInfo = "Purchase Duplicate Sleeves and upgrades. These are permanent!";
|
||||
|
||||
@@ -131,6 +132,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
|
||||
{!isPlayersGang && factionInfo.offersWork() && (
|
||||
<DonateOption faction={faction} rerender={rerender} favorToDonate={favorToDonate} disabled={!canDonate} />
|
||||
)}
|
||||
{!isPlayersGang && factionInfo.offersWork() && <ShareOption rerender={rerender} />}
|
||||
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} />
|
||||
{canPurchaseSleeves && (
|
||||
<>
|
||||
|
||||
@@ -27,7 +27,6 @@ interface IProps {
|
||||
const useStyles = makeStyles()({
|
||||
noformat: {
|
||||
whiteSpace: "pre-wrap",
|
||||
lineHeight: "1em",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
87
src/Faction/ui/ShareOption.tsx
Normal file
87
src/Faction/ui/ShareOption.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { TextField } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import { Player } from "@player";
|
||||
import {
|
||||
calculateCurrentShareBonus,
|
||||
calculateShareBonusWithAdditionalThreads,
|
||||
pendingUIShareJobIds,
|
||||
ShareBonusTime,
|
||||
startSharing,
|
||||
} from "../../NetworkShare/Share";
|
||||
import { formatRam } from "../../ui/formatNumber";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { useCycleRerender } from "../../ui/React/hooks";
|
||||
import { roundToTwo } from "../../utils/helpers/roundToTwo";
|
||||
|
||||
export function ShareOption({ rerender }: { rerender: () => void }): React.ReactElement {
|
||||
const [ram, setRam] = useState<number>(0);
|
||||
useCycleRerender();
|
||||
|
||||
const home = Player.getHomeComputer();
|
||||
const threads = Math.floor(ram / 4);
|
||||
|
||||
function onShare(): void {
|
||||
if (threads === 0) {
|
||||
return;
|
||||
}
|
||||
if (!Number.isFinite(threads) || threads < 0) {
|
||||
dialogBoxCreate("Invalid RAM amount.");
|
||||
return;
|
||||
}
|
||||
const freeRAM = home.maxRam - home.ramUsed;
|
||||
const ramUsage = roundToTwo(4 * threads);
|
||||
if (ramUsage > freeRAM + 0.001) {
|
||||
dialogBoxCreate("Not enough RAM.");
|
||||
return;
|
||||
}
|
||||
|
||||
home.updateRamUsed(roundToTwo(home.ramUsed + ramUsage));
|
||||
const end = startSharing(threads, home.cpuCores);
|
||||
const jobId = window.setTimeout(() => {
|
||||
end();
|
||||
if (pendingUIShareJobIds.includes(jobId)) {
|
||||
home.updateRamUsed(roundToTwo(home.ramUsed - ramUsage));
|
||||
}
|
||||
rerender();
|
||||
}, ShareBonusTime);
|
||||
pendingUIShareJobIds.push(jobId);
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Typography>
|
||||
You can share free RAM of your home computer with your faction to get a bonus multiplier for reputation gain.
|
||||
Each time you share your free RAM, you get a boost for {ShareBonusTime / 1000} seconds. You can share free RAM
|
||||
of other servers that you have admin rights by using ns.share() API.
|
||||
<br />
|
||||
Free RAM on home computer: {formatRam(home.maxRam - home.ramUsed)}.
|
||||
<br />
|
||||
Current bonus: {calculateCurrentShareBonus()}. Bonus with {formatRam(ram)}:{" "}
|
||||
{calculateShareBonusWithAdditionalThreads(threads, home.cpuCores)}
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
value={ram}
|
||||
onChange={(event) => {
|
||||
if (event.target.value === "") {
|
||||
setRam(0);
|
||||
return;
|
||||
}
|
||||
const value = Number.parseFloat(event.target.value);
|
||||
if (!Number.isFinite(value) || value < 0) {
|
||||
return;
|
||||
}
|
||||
setRam(value);
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: <Button onClick={onShare}>Share</Button>,
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
numCycleForGrowthCorrected,
|
||||
processSingleServerGrowth,
|
||||
safelyCreateUniqueServer,
|
||||
getCoreBonus,
|
||||
getWeakenEffect,
|
||||
} from "./Server/ServerHelpers";
|
||||
import {
|
||||
@@ -89,8 +88,7 @@ import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { matchScriptPathExact } from "./utils/helpers/scriptKey";
|
||||
|
||||
import { Flags } from "./NetscriptFunctions/Flags";
|
||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||
import { calculateCurrentShareBonus, ShareBonusTime, startSharing } from "./NetworkShare/Share";
|
||||
import { recentScripts } from "./Netscript/RecentScripts";
|
||||
import { InternalAPI, setRemovedFunctions, NSProxy } from "./Netscript/APIWrapper";
|
||||
import { INetscriptExtra } from "./NetscriptFunctions/Extra";
|
||||
@@ -421,19 +419,17 @@ export const ns: InternalAPI<NSFull> = {
|
||||
return getWeakenEffect(threads, cores);
|
||||
},
|
||||
share: (ctx) => () => {
|
||||
const cores = helpers.getServer(ctx, ctx.workerScript.hostname).cpuCores;
|
||||
const coreBonus = getCoreBonus(cores);
|
||||
helpers.log(ctx, () => "Sharing this computer.");
|
||||
const end = StartSharing(
|
||||
ctx.workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.skills.intelligence, 2) * coreBonus,
|
||||
);
|
||||
return helpers.netscriptDelay(ctx, 10000).finally(function () {
|
||||
helpers.log(ctx, () => "Finished sharing this computer.");
|
||||
const threads = ctx.workerScript.scriptRef.threads;
|
||||
const hostname = ctx.workerScript.hostname;
|
||||
helpers.log(ctx, () => `Sharing ${threads} threads on ${hostname}.`);
|
||||
const end = startSharing(threads, helpers.getServer(ctx, hostname).cpuCores);
|
||||
return helpers.netscriptDelay(ctx, ShareBonusTime).finally(function () {
|
||||
helpers.log(ctx, () => `Finished sharing ${threads} threads on ${hostname}.`);
|
||||
end();
|
||||
});
|
||||
},
|
||||
getSharePower: () => () => {
|
||||
return CalculateShareMult();
|
||||
return calculateCurrentShareBonus();
|
||||
},
|
||||
print:
|
||||
(ctx) =>
|
||||
|
||||
57
src/NetworkShare/Share.ts
Normal file
57
src/NetworkShare/Share.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Player } from "@player";
|
||||
import { calculateIntelligenceBonus } from "../PersonObjects/formulas/intelligence";
|
||||
import { getCoreBonus } from "../Server/ServerHelpers";
|
||||
import { clampNumber } from "../utils/helpers/clampNumber";
|
||||
|
||||
let sharePower = 1;
|
||||
|
||||
export const ShareBonusTime = 10000;
|
||||
|
||||
/**
|
||||
* When the player shares free RAM via UI, it's a "pending job". After that job finishes, we restore the free RAM by
|
||||
* decreasing server.ramUsed by calling server.updateRamUsed(). However, if the player prestiges before that, all
|
||||
* servers are reset and ramUsed is reset to 0. This means that when a job finishes, we may modify ramUsed of a new
|
||||
* server.
|
||||
*
|
||||
* To solve this problem, we use an array to save job IDs. When the player prestiges, we clear this array. When a job
|
||||
* finishes, we check if that job ID is still in this array. If it is not, it means that the player performed a
|
||||
* prestige, and we do not need to decrease ramUsed.
|
||||
*/
|
||||
export const pendingUIShareJobIds: number[] = [];
|
||||
|
||||
export function calculateEffectiveSharedThreads(threads: number, cpuCores: number): number {
|
||||
const coreBonus = getCoreBonus(cpuCores);
|
||||
return threads * calculateIntelligenceBonus(Player.skills.intelligence, 2) * coreBonus;
|
||||
}
|
||||
|
||||
export function startSharing(threads: number, cpuCores: number): () => void {
|
||||
const effectiveThreads = calculateEffectiveSharedThreads(threads, cpuCores);
|
||||
sharePower += effectiveThreads;
|
||||
return () => {
|
||||
/**
|
||||
* Due to floating point inaccuracy, sharePower may be slightly higher or lower than 1 after many times the player
|
||||
* shares their RAM. We need to make sure that it's not smaller than 1.
|
||||
*/
|
||||
sharePower = clampNumber(sharePower - effectiveThreads, 1);
|
||||
// sharePower may be slightly higher than 1. Reset sharePower if it's smaller than a threshold.
|
||||
if (sharePower < 1.00001) {
|
||||
sharePower = 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function calculateShareBonus(sharePower: number): number {
|
||||
const bonus = 1 + Math.log(sharePower) / 25;
|
||||
if (!Number.isFinite(bonus)) {
|
||||
return 1;
|
||||
}
|
||||
return bonus;
|
||||
}
|
||||
|
||||
export function calculateShareBonusWithAdditionalThreads(threads: number, cpuCores: number): number {
|
||||
return calculateShareBonus(sharePower + calculateEffectiveSharedThreads(threads, cpuCores));
|
||||
}
|
||||
|
||||
export function calculateCurrentShareBonus(): number {
|
||||
return calculateShareBonus(sharePower);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export function CalculateShareMult(power: number): number {
|
||||
const x = 1 + Math.log(power) / 25;
|
||||
if (isNaN(x) || !isFinite(x)) return 1;
|
||||
return x;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
|
||||
import { CalculateShareMult } from "../../NetworkShare/Share";
|
||||
import { calculateCurrentShareBonus } from "../../NetworkShare/Share";
|
||||
import { Person as IPerson } from "@nsdefs";
|
||||
import { calculateIntelligenceBonus } from "./intelligence";
|
||||
|
||||
@@ -18,7 +18,7 @@ export function getHackingWorkRepGain(p: IPerson, favor: number): number {
|
||||
p.mults.faction_rep *
|
||||
calculateIntelligenceBonus(p.skills.intelligence, 1) *
|
||||
mult(favor) *
|
||||
CalculateShareMult()
|
||||
calculateCurrentShareBonus()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function getFactionSecurityWorkRepGain(p: IPerson, favor: number): number
|
||||
p.skills.defense +
|
||||
p.skills.dexterity +
|
||||
p.skills.agility +
|
||||
(p.skills.hacking + p.skills.intelligence) * CalculateShareMult())) /
|
||||
(p.skills.hacking + p.skills.intelligence) * calculateCurrentShareBonus())) /
|
||||
CONSTANTS.MaxSkillLevel /
|
||||
4.5;
|
||||
return t * p.mults.faction_rep * mult(favor) * calculateIntelligenceBonus(p.skills.intelligence, 1);
|
||||
@@ -43,7 +43,7 @@ export function getFactionFieldWorkRepGain(p: IPerson, favor: number): number {
|
||||
p.skills.dexterity +
|
||||
p.skills.agility +
|
||||
p.skills.charisma +
|
||||
(p.skills.hacking + p.skills.intelligence) * CalculateShareMult())) /
|
||||
(p.skills.hacking + p.skills.intelligence) * calculateCurrentShareBonus())) /
|
||||
CONSTANTS.MaxSkillLevel /
|
||||
5.5;
|
||||
return t * p.mults.faction_rep * mult(favor) * calculateIntelligenceBonus(p.skills.intelligence, 1);
|
||||
|
||||
@@ -29,6 +29,7 @@ import { Go } from "./Go/Go";
|
||||
import { calculateExp } from "./PersonObjects/formulas/skill";
|
||||
import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
|
||||
import { canAccessBitNodeFeature } from "./BitNode/BitNodeUtils";
|
||||
import { pendingUIShareJobIds } from "./NetworkShare/Share";
|
||||
|
||||
const BitNode8StartingMoney = 250e6;
|
||||
function delayedDialog(message: string) {
|
||||
@@ -75,6 +76,9 @@ export function prestigeAugmentation(): void {
|
||||
AddToAllServers(homeComp);
|
||||
prestigeHomeComputer(homeComp);
|
||||
|
||||
// Clear all pending share jobs created via UI
|
||||
pendingUIShareJobIds.length = 0;
|
||||
|
||||
// Receive starting money and programs from installed augmentations
|
||||
for (const ownedAug of Player.augmentations) {
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
@@ -211,6 +215,10 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
||||
// Reset home computer (only the programs) and add to AllServers
|
||||
AddToAllServers(homeComp);
|
||||
prestigeHomeComputer(homeComp);
|
||||
|
||||
// Clear all pending share jobs created via UI
|
||||
pendingUIShareJobIds.length = 0;
|
||||
|
||||
// Ram usage needs to be cleared for bitnode-level resets, due to possible change in singularity cost.
|
||||
for (const script of homeComp.scripts.values()) script.ramUsage = null;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Settings } from "../Settings/Settings";
|
||||
|
||||
import type { ScriptKey } from "../utils/helpers/scriptKey";
|
||||
import { objectAssert } from "../utils/helpers/typeAssertion";
|
||||
import { clampNumber } from "../utils/helpers/clampNumber";
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
@@ -232,7 +233,7 @@ export abstract class BaseServer implements IServer {
|
||||
}
|
||||
|
||||
updateRamUsed(ram: number): void {
|
||||
this.ramUsed = ram;
|
||||
this.ramUsed = clampNumber(ram, 0, this.maxRam);
|
||||
}
|
||||
|
||||
pushProgram(program: ProgramFilePath | CompletedProgramName): void {
|
||||
|
||||
Reference in New Issue
Block a user