mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-23 16:13:49 +01:00
factory v0.1
This commit is contained in:
parent
76ce2f9955
commit
f0b9b2d13f
@ -192,9 +192,9 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
|||||||
*
|
*
|
||||||
* Instead of implementing it, the old code (encoding in base64) is used here for backward compatibility.
|
* Instead of implementing it, the old code (encoding in base64) is used here for backward compatibility.
|
||||||
*/
|
*/
|
||||||
const content = Buffer.from(saveData).toString("base64");
|
const content = saveData.toString("base64");
|
||||||
log.debug(`saveData: ${saveData.length} bytes`);
|
log.debug(`Uncompressed: ${saveData.length} bytes`);
|
||||||
log.debug(`Base64 string of saveData: ${content.length} bytes`);
|
log.debug(`Compressed: ${content.length} bytes`);
|
||||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -158,12 +158,10 @@ export const CONSTANTS: {
|
|||||||
|
|
||||||
// Also update doc/source/changelog.rst
|
// Also update doc/source/changelog.rst
|
||||||
LatestUpdate: `
|
LatestUpdate: `
|
||||||
## v2.6.2 dev - Last update 2 June 2024
|
## v2.6.2 dev - Last update 22 May 2024
|
||||||
|
|
||||||
See 2.6.1 changelog at https://github.com/bitburner-official/bitburner-src/blob/v2.6.1/src/Documentation/doc/changelog.md
|
See 2.6.1 changelog at https://github.com/bitburner-official/bitburner-src/blob/v2.6.1/src/Documentation/doc/changelog.md
|
||||||
|
|
||||||
### HOTFIX (changes also added to 2.6.1 post release)
|
No changes yet since 2.6.1 release
|
||||||
|
|
||||||
- Fixed an issue with invalid format on steam cloud save (@catloversg)
|
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
26
src/Factory/Factory.ts
Normal file
26
src/Factory/Factory.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Bot, Entity, EntityType, Item } from "@nsdefs";
|
||||||
|
import { factory } from "./Helper";
|
||||||
|
|
||||||
|
export interface Factory {
|
||||||
|
bits: number;
|
||||||
|
entities: Entity[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const distance = (a: Entity, b: Entity) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
||||||
|
|
||||||
|
export const adjacent = (a: Entity, b: Entity) => distance(a, b) === 1;
|
||||||
|
|
||||||
|
export const findBot = (name: string) =>
|
||||||
|
factory.entities.find((e): e is Bot => e.type === EntityType.Bot && e.name === name);
|
||||||
|
|
||||||
|
export const findEntityAt = <T extends Entity>(x: number, y: number, type?: EntityType): T | undefined =>
|
||||||
|
factory.entities.find((e): e is T => (!type || e.type === type) && e.x === x && e.y === y);
|
||||||
|
|
||||||
|
export const bitsMap: Record<Item, number> = {
|
||||||
|
[Item.BasicR]: 1,
|
||||||
|
[Item.BasicG]: 1,
|
||||||
|
[Item.BasicB]: 1,
|
||||||
|
[Item.ComplexR]: 4,
|
||||||
|
[Item.ComplexG]: 4,
|
||||||
|
[Item.ComplexB]: 4,
|
||||||
|
};
|
127
src/Factory/Helper.tsx
Normal file
127
src/Factory/Helper.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { EntityType, Item } from "@nsdefs";
|
||||||
|
import { Factory } from "./Factory";
|
||||||
|
|
||||||
|
export const factorySize = 12;
|
||||||
|
|
||||||
|
const defaultFactory: Factory = {
|
||||||
|
bits: 0,
|
||||||
|
entities: [
|
||||||
|
{
|
||||||
|
type: EntityType.Dispenser,
|
||||||
|
dispensing: Item.BasicR,
|
||||||
|
x: Math.floor(factorySize / 4),
|
||||||
|
y: 0,
|
||||||
|
cooldown: 10000,
|
||||||
|
cooldownUntil: 0,
|
||||||
|
inventory: [Item.BasicR],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dispenser,
|
||||||
|
dispensing: Item.BasicG,
|
||||||
|
x: Math.floor(factorySize / 2),
|
||||||
|
y: 0,
|
||||||
|
cooldown: 10000,
|
||||||
|
cooldownUntil: 0,
|
||||||
|
inventory: [Item.BasicG],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dispenser,
|
||||||
|
dispensing: Item.BasicB,
|
||||||
|
x: Math.floor((factorySize * 3) / 4),
|
||||||
|
y: 0,
|
||||||
|
cooldown: 10000,
|
||||||
|
cooldownUntil: 0,
|
||||||
|
inventory: [Item.BasicB],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dock,
|
||||||
|
x: Math.floor(factorySize / 4),
|
||||||
|
y: Math.floor(factorySize - 1),
|
||||||
|
potentialRequest: [Item.BasicR, Item.BasicG, Item.BasicB],
|
||||||
|
potentialRequestCount: 1,
|
||||||
|
currentRequest: [Item.BasicR],
|
||||||
|
inventory: [],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dock,
|
||||||
|
x: Math.floor(factorySize / 2),
|
||||||
|
y: Math.floor(factorySize - 1),
|
||||||
|
potentialRequest: [Item.BasicR, Item.BasicG, Item.BasicB],
|
||||||
|
potentialRequestCount: 1,
|
||||||
|
currentRequest: [Item.BasicG],
|
||||||
|
inventory: [],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dock,
|
||||||
|
x: Math.floor((factorySize * 3) / 4),
|
||||||
|
y: Math.floor(factorySize - 1),
|
||||||
|
potentialRequest: [Item.BasicR, Item.BasicG, Item.BasicB],
|
||||||
|
potentialRequestCount: 1,
|
||||||
|
currentRequest: [Item.BasicB],
|
||||||
|
inventory: [],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Dock,
|
||||||
|
x: 0,
|
||||||
|
y: Math.floor(factorySize - 1),
|
||||||
|
potentialRequest: [Item.ComplexR],
|
||||||
|
potentialRequestCount: 1,
|
||||||
|
currentRequest: [Item.ComplexR],
|
||||||
|
inventory: [],
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Bot,
|
||||||
|
x: Math.floor(factorySize / 2),
|
||||||
|
y: Math.floor(factorySize / 2),
|
||||||
|
inventory: [],
|
||||||
|
energy: 16,
|
||||||
|
name: "alice",
|
||||||
|
maxInventory: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Crafter,
|
||||||
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
inventory: [],
|
||||||
|
maxInventory: 3,
|
||||||
|
recipe: {
|
||||||
|
input: [Item.BasicR],
|
||||||
|
output: [Item.ComplexR],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: EntityType.Chest,
|
||||||
|
inventory: [Item.BasicR],
|
||||||
|
maxInventory: 3,
|
||||||
|
x: Math.floor(factorySize / 2) + 1,
|
||||||
|
y: Math.floor(factorySize / 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const factory: Factory = defaultFactory;
|
||||||
|
|
||||||
|
export const loadFactory = (save: string) => {
|
||||||
|
if (!save) return;
|
||||||
|
// const savedFactory = JSON.parse(save);
|
||||||
|
// Object.assign(factory, savedFactory);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NewBot = (name: string, x: number, y: number) => {
|
||||||
|
factory.entities.push({
|
||||||
|
type: EntityType.Bot,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
inventory: [],
|
||||||
|
energy: 16,
|
||||||
|
name,
|
||||||
|
maxInventory: 1,
|
||||||
|
});
|
||||||
|
};
|
1
src/Factory/formulas/formulas.ts
Normal file
1
src/Factory/formulas/formulas.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const botPrice = (currentBots: number): number => Math.pow(2, currentBots + 3);
|
150
src/Factory/ui/EntityIcon.tsx
Normal file
150
src/Factory/ui/EntityIcon.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import React from "react";
|
||||||
|
import SmartToyIcon from "@mui/icons-material/SmartToy";
|
||||||
|
import { styled } from "@mui/system";
|
||||||
|
import { Dispenser, Entity, EntityType, Item } from "@nsdefs";
|
||||||
|
|
||||||
|
import MoveToInboxIcon from "@mui/icons-material/MoveToInbox";
|
||||||
|
import OutboxIcon from "@mui/icons-material/Outbox";
|
||||||
|
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
|
||||||
|
import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing";
|
||||||
|
import { Tooltip, Typography } from "@mui/material";
|
||||||
|
|
||||||
|
export const cellSize = 48;
|
||||||
|
|
||||||
|
const defaultIconStyle = {
|
||||||
|
width: cellSize + "px",
|
||||||
|
height: cellSize + "px",
|
||||||
|
color: "white",
|
||||||
|
};
|
||||||
|
|
||||||
|
const colorRed = "red";
|
||||||
|
const colorBlue = "#1E90FF";
|
||||||
|
const colorGreen = "#7CFC00";
|
||||||
|
|
||||||
|
const itemColorMap: Record<Item, string> = {
|
||||||
|
[Item.BasicR]: colorRed,
|
||||||
|
[Item.BasicB]: colorBlue,
|
||||||
|
[Item.BasicG]: colorGreen,
|
||||||
|
[Item.ComplexR]: colorRed,
|
||||||
|
[Item.ComplexB]: colorBlue,
|
||||||
|
[Item.ComplexG]: colorGreen,
|
||||||
|
};
|
||||||
|
|
||||||
|
const BotIcon = styled(SmartToyIcon)(defaultIconStyle);
|
||||||
|
const DispenserIcon = styled(OutboxIcon)((props: { dispenser: Dispenser; col: string }) => ({
|
||||||
|
...defaultIconStyle,
|
||||||
|
color: new Date().getTime() > props.dispenser.cooldownUntil ? props.col : "gray",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DockIcon = styled(MoveToInboxIcon)({
|
||||||
|
...defaultIconStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const CrafterIcon = styled(PrecisionManufacturingIcon)({
|
||||||
|
...defaultIconStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChestIcon = styled(CheckBoxOutlineBlankIcon)({
|
||||||
|
...defaultIconStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ITooltipContentProps {
|
||||||
|
entity: Entity;
|
||||||
|
content: React.ReactElement;
|
||||||
|
}
|
||||||
|
const TooltipContent = ({ entity, content }: ITooltipContentProps): React.ReactElement => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography>{entity.type}</Typography>
|
||||||
|
{content}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TooltipInventory = ({ entity }: { entity: Entity }): React.ReactElement => {
|
||||||
|
return (
|
||||||
|
<Typography component="span">
|
||||||
|
{entity.inventory.map((item) => (
|
||||||
|
<span key={item} style={{ color: itemColorMap[item] }}>
|
||||||
|
{item}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EntityIcon = ({ entity }: { entity: Entity }): React.ReactElement => {
|
||||||
|
switch (entity.type) {
|
||||||
|
case EntityType.Chest: {
|
||||||
|
return (
|
||||||
|
<Tooltip title={<TooltipContent entity={entity} content={<TooltipInventory entity={entity} />} />}>
|
||||||
|
<ChestIcon />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case EntityType.Bot: {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
{entity.name}
|
||||||
|
<br /> <TooltipInventory entity={entity} />
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BotIcon />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case EntityType.Dispenser: {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
<TooltipContent entity={entity} content={<Typography>{`dispensing: ${entity.dispensing}`}</Typography>} />
|
||||||
|
<TooltipInventory entity={entity} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DispenserIcon dispenser={entity} col={itemColorMap[entity.dispensing]} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EntityType.Dock: {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<TooltipContent
|
||||||
|
entity={entity}
|
||||||
|
content={<Typography>{`requesting: ${entity.currentRequest}`}</Typography>}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DockIcon sx={{ color: itemColorMap[entity.currentRequest[0] ?? Item.BasicR] }} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case EntityType.Crafter: {
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<TooltipContent
|
||||||
|
entity={entity}
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<Typography>{`input: ${entity.recipe.input}, output: ${entity.recipe.output}`}</Typography>
|
||||||
|
<TooltipInventory entity={entity} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CrafterIcon />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <></>;
|
||||||
|
};
|
69
src/Factory/ui/FactoryRoot.tsx
Normal file
69
src/Factory/ui/FactoryRoot.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Container, Typography } from "@mui/material";
|
||||||
|
|
||||||
|
import { styled } from "@mui/system";
|
||||||
|
import { factory, factorySize } from "../Helper";
|
||||||
|
import { useRerender } from "../../ui/React/hooks";
|
||||||
|
import { EntityIcon, cellSize } from "./EntityIcon";
|
||||||
|
|
||||||
|
const CellD = styled("div")({
|
||||||
|
width: cellSize + "px",
|
||||||
|
height: cellSize + "px",
|
||||||
|
backgroundColor: "#444",
|
||||||
|
padding: 0,
|
||||||
|
margin: "2px",
|
||||||
|
marginTop: "4px",
|
||||||
|
marginBottom: "4px",
|
||||||
|
});
|
||||||
|
|
||||||
|
const Cell = ({ x, y }: { x: number; y: number }): React.ReactElement => {
|
||||||
|
const entity = factory.entities.find((e) => e.x === x && e.y === y);
|
||||||
|
return <CellD>{entity && <EntityIcon entity={entity} />}</CellD>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RowD = styled("div")({
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IColProps {
|
||||||
|
x: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Col = ({ x }: IColProps): React.ReactElement => {
|
||||||
|
return (
|
||||||
|
<RowD>
|
||||||
|
{new Array(factorySize).fill(null).map((_, y) => (
|
||||||
|
<Cell key={y} x={x} y={y}></Cell>
|
||||||
|
))}
|
||||||
|
</RowD>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Table = styled("div")({
|
||||||
|
border: "1px solid #fff",
|
||||||
|
borderSpacing: "2px",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
paddingLeft: "2px",
|
||||||
|
paddingRight: "2px",
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IProps {}
|
||||||
|
|
||||||
|
export const FactoryRoot = (__props: IProps): React.ReactElement => {
|
||||||
|
useRerender(200);
|
||||||
|
return (
|
||||||
|
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||||
|
<Typography variant="h4">Factory</Typography>
|
||||||
|
<div style={{ display: "flex" }}>
|
||||||
|
<Table>
|
||||||
|
{new Array(factorySize).fill(null).map((_, index) => (
|
||||||
|
<Col key={index} x={index} />
|
||||||
|
))}
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
@ -367,6 +367,15 @@ const stanek = {
|
|||||||
acceptGift: RamCostConstants.StanekAcceptGift,
|
acceptGift: RamCostConstants.StanekAcceptGift,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
const factory: any = new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get() {
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// UI API
|
// UI API
|
||||||
const ui = {
|
const ui = {
|
||||||
getTheme: 0,
|
getTheme: 0,
|
||||||
@ -469,6 +478,7 @@ export const RamCosts: RamCostTree<NSFull> = {
|
|||||||
codingcontract,
|
codingcontract,
|
||||||
sleeve,
|
sleeve,
|
||||||
stanek,
|
stanek,
|
||||||
|
factory,
|
||||||
ui,
|
ui,
|
||||||
grafting,
|
grafting,
|
||||||
|
|
||||||
|
@ -108,6 +108,7 @@ import { getEnumHelper } from "./utils/EnumHelper";
|
|||||||
import { setDeprecatedProperties, deprecationWarning } from "./utils/DeprecationHelper";
|
import { setDeprecatedProperties, deprecationWarning } from "./utils/DeprecationHelper";
|
||||||
import { ServerConstants } from "./Server/data/Constants";
|
import { ServerConstants } from "./Server/data/Constants";
|
||||||
import { assertFunction } from "./Netscript/TypeAssertion";
|
import { assertFunction } from "./Netscript/TypeAssertion";
|
||||||
|
import { NetscriptFactory } from "./NetscriptFunctions/Factory";
|
||||||
|
|
||||||
export const enums: NSEnums = {
|
export const enums: NSEnums = {
|
||||||
CityName,
|
CityName,
|
||||||
@ -135,6 +136,7 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
sleeve: NetscriptSleeve(),
|
sleeve: NetscriptSleeve(),
|
||||||
corporation: NetscriptCorporation(),
|
corporation: NetscriptCorporation(),
|
||||||
stanek: NetscriptStanek(),
|
stanek: NetscriptStanek(),
|
||||||
|
factory: NetscriptFactory(),
|
||||||
infiltration: NetscriptInfiltration(),
|
infiltration: NetscriptInfiltration(),
|
||||||
ui: NetscriptUserInterface(),
|
ui: NetscriptUserInterface(),
|
||||||
formulas: NetscriptFormulas(),
|
formulas: NetscriptFormulas(),
|
||||||
|
184
src/NetscriptFunctions/Factory.ts
Normal file
184
src/NetscriptFunctions/Factory.ts
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import { Bot, Factory as IFactory, EntityType, Item, Crafter } from "@nsdefs";
|
||||||
|
import { InternalAPI } from "../Netscript/APIWrapper";
|
||||||
|
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||||
|
import { NewBot, factory, factorySize } from "../Factory/Helper";
|
||||||
|
import { adjacent, bitsMap, findBot, findEntityAt } from "../Factory/Factory";
|
||||||
|
import { botPrice } from "../Factory/formulas/formulas";
|
||||||
|
|
||||||
|
export function NetscriptFactory(): InternalAPI<IFactory> {
|
||||||
|
return {
|
||||||
|
getBits: () => () => factory.bits,
|
||||||
|
getBotPrice: () => () => {
|
||||||
|
const botCount = factory.entities.reduce((acc, e) => (e.type === EntityType.Bot ? acc + 1 : acc), 0);
|
||||||
|
return botPrice(botCount);
|
||||||
|
},
|
||||||
|
purchaseBot: (ctx) => (_name, _x, _y) => {
|
||||||
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
const x = helpers.number(ctx, "x", _x);
|
||||||
|
const y = helpers.number(ctx, "y", _y);
|
||||||
|
const another = factory.entities.find((e): e is Bot => e.type === EntityType.Bot && e.name === name);
|
||||||
|
if (another) {
|
||||||
|
helpers.log(ctx, () => `bot "${name}" already exists`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const location = factory.entities.find((e) => e.x === x && e.y === y);
|
||||||
|
if (location) {
|
||||||
|
helpers.log(ctx, () => `location [${x}, ${y}] is occupied`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const botCount = factory.entities.reduce((acc, e) => (e.type === EntityType.Bot ? acc + 1 : acc), 0);
|
||||||
|
const price = botPrice(botCount);
|
||||||
|
if (factory.bits < price) {
|
||||||
|
helpers.log(ctx, () => `not enough bits to purchase bot`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory.bits -= price;
|
||||||
|
NewBot(name, x, y);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
moveBot:
|
||||||
|
(ctx) =>
|
||||||
|
async (_name, _x, _y): Promise<boolean> => {
|
||||||
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
const x = helpers.number(ctx, "x", _x);
|
||||||
|
const y = helpers.number(ctx, "y", _y);
|
||||||
|
|
||||||
|
const bot = factory.entities.find((e) => e.type === EntityType.Bot && e.name === name);
|
||||||
|
if (!bot) return Promise.resolve(false);
|
||||||
|
const dist = Math.abs(bot.x - x) + Math.abs(bot.y - y);
|
||||||
|
if (dist !== 1 || x < 0 || x > factorySize || y < 0 || y > factorySize) return Promise.resolve(false);
|
||||||
|
if (factory.entities.find((e) => e.x === x && e.y === y)) return Promise.resolve(false);
|
||||||
|
return helpers.netscriptDelay(ctx, 50).then(function () {
|
||||||
|
bot.x = x;
|
||||||
|
bot.y = y;
|
||||||
|
return Promise.resolve(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getBot:
|
||||||
|
(ctx) =>
|
||||||
|
(_name): Bot | undefined => {
|
||||||
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
const bot = factory.entities.find((e): e is Bot => e.type === EntityType.Bot && e.name === name);
|
||||||
|
if (!bot) return;
|
||||||
|
return JSON.parse(JSON.stringify(bot));
|
||||||
|
},
|
||||||
|
entityAt: (ctx) => (_x, _y) => {
|
||||||
|
const x = helpers.number(ctx, "x", _x);
|
||||||
|
const y = helpers.number(ctx, "y", _y);
|
||||||
|
return factory.entities.find((e) => e.x === x && e.y === y);
|
||||||
|
},
|
||||||
|
entities: (__ctx) => () => JSON.parse(JSON.stringify(factory.entities)),
|
||||||
|
|
||||||
|
transfer:
|
||||||
|
(ctx) =>
|
||||||
|
async (_name, _x, _y, _pickup, _drop): Promise<boolean> => {
|
||||||
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
const x = helpers.number(ctx, "x", _x);
|
||||||
|
const y = helpers.number(ctx, "y", _y);
|
||||||
|
const pickup = _pickup as Item[];
|
||||||
|
const drop = _drop as Item[];
|
||||||
|
|
||||||
|
const bot = findBot(name);
|
||||||
|
if (!bot) {
|
||||||
|
helpers.log(ctx, () => `bot "${name}" not found`);
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
const container = findEntityAt(x, y);
|
||||||
|
if (!container) {
|
||||||
|
helpers.log(ctx, () => `container not found at [${x}, ${y}]`);
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
if (!adjacent(bot, container)) {
|
||||||
|
helpers.log(ctx, () => "bot is not adjacent to container");
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const botFinalSize = bot.inventory.length + pickup.length - drop.length;
|
||||||
|
const containerFinalSize = container.inventory.length + drop.length - pickup.length;
|
||||||
|
if (botFinalSize > bot.maxInventory || containerFinalSize > container.maxInventory) {
|
||||||
|
helpers.log(ctx, () => "not enough space in bot or container");
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const containerHasItems = pickup.every((item) => container.inventory.includes(item));
|
||||||
|
const botHasItems = drop.every((item) => bot.inventory.includes(item));
|
||||||
|
if (!containerHasItems || !botHasItems) {
|
||||||
|
helpers.log(ctx, () => "container or bot does not have items");
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.inventory = container.inventory.filter((item) => !pickup.includes(item));
|
||||||
|
container.inventory.push(...drop);
|
||||||
|
|
||||||
|
bot.inventory = bot.inventory.filter((item) => !drop.includes(item));
|
||||||
|
bot.inventory.push(...pickup);
|
||||||
|
|
||||||
|
switch (container.type) {
|
||||||
|
case EntityType.Dispenser: {
|
||||||
|
container.cooldownUntil = Date.now() + container.cooldown;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (container.inventory.length < container.maxInventory) {
|
||||||
|
container.inventory.push(container.dispensing);
|
||||||
|
}
|
||||||
|
}, container.cooldown);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EntityType.Dock: {
|
||||||
|
if (
|
||||||
|
container.inventory.every((item) => container.currentRequest.includes(item)) &&
|
||||||
|
container.currentRequest.every((item) => container.inventory.includes(item))
|
||||||
|
) {
|
||||||
|
factory.bits += container.inventory.map((i) => bitsMap[i]).reduce((a, b) => a + b, 0);
|
||||||
|
container.inventory = [];
|
||||||
|
const newRequest = new Array(container.potentialRequestCount)
|
||||||
|
.fill(null)
|
||||||
|
.map(() => container.potentialRequest[Math.floor(Math.random() * container.potentialRequest.length)]);
|
||||||
|
container.currentRequest = newRequest;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(true);
|
||||||
|
},
|
||||||
|
craft:
|
||||||
|
(ctx) =>
|
||||||
|
async (_name, _x, _y): Promise<boolean> => {
|
||||||
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
const x = helpers.number(ctx, "x", _x);
|
||||||
|
const y = helpers.number(ctx, "y", _y);
|
||||||
|
const bot = findBot(name);
|
||||||
|
if (!bot) {
|
||||||
|
helpers.log(ctx, () => `bot "${name}" not found`);
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
const crafter = findEntityAt<Crafter>(x, y, EntityType.Crafter);
|
||||||
|
if (!crafter) {
|
||||||
|
helpers.log(ctx, () => `crafter not found at [${x}, ${y}]`);
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!adjacent(bot, crafter)) {
|
||||||
|
helpers.log(ctx, () => "bot is not adjacent to crafter");
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!crafter.recipe.input.every((item) => crafter.inventory.includes(item)) &&
|
||||||
|
crafter.inventory.every((item) => crafter.recipe.input.includes(item))
|
||||||
|
) {
|
||||||
|
helpers.log(ctx, () => "crafter is missing ingredients");
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpers.netscriptDelay(ctx, 5000).then(function () {
|
||||||
|
crafter.inventory = [...crafter.recipe.output];
|
||||||
|
return Promise.resolve(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -45,6 +45,7 @@ import { downloadContentAsFile } from "./utils/FileUtils";
|
|||||||
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
import { showAPIBreaks } from "./utils/APIBreaks/APIBreak";
|
||||||
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
import { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
||||||
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
|
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
|
||||||
|
import { factory, loadFactory } from "./Factory/Helper";
|
||||||
|
|
||||||
/* SaveObject.js
|
/* SaveObject.js
|
||||||
* Defines the object used to save/load games
|
* Defines the object used to save/load games
|
||||||
@ -96,6 +97,7 @@ class BitburnerSaveObject {
|
|||||||
AllGangsSave = "";
|
AllGangsSave = "";
|
||||||
LastExportBonus = "0";
|
LastExportBonus = "0";
|
||||||
StaneksGiftSave = "";
|
StaneksGiftSave = "";
|
||||||
|
FactorySave = "";
|
||||||
GoSave = "";
|
GoSave = "";
|
||||||
|
|
||||||
async getSaveData(forceExcludeRunningScripts = false): Promise<SaveData> {
|
async getSaveData(forceExcludeRunningScripts = false): Promise<SaveData> {
|
||||||
@ -116,6 +118,7 @@ class BitburnerSaveObject {
|
|||||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||||
|
this.FactorySave = JSON.stringify(factory);
|
||||||
this.GoSave = JSON.stringify(getGoSave());
|
this.GoSave = JSON.stringify(getGoSave());
|
||||||
|
|
||||||
if (Player.gang) this.AllGangsSave = JSON.stringify(AllGangs);
|
if (Player.gang) this.AllGangsSave = JSON.stringify(AllGangs);
|
||||||
@ -761,6 +764,12 @@ async function loadGame(saveData: SaveData): Promise<boolean> {
|
|||||||
console.warn(`Could not load Staneks Gift from save`);
|
console.warn(`Could not load Staneks Gift from save`);
|
||||||
loadStaneksGift("");
|
loadStaneksGift("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Object.hasOwn(saveObj, "FactorySave")) {
|
||||||
|
loadFactory(saveObj.FactorySave);
|
||||||
|
} else {
|
||||||
|
console.warn(`Could not load Factory from save`);
|
||||||
|
}
|
||||||
if (Object.hasOwn(saveObj, "AliasesSave")) {
|
if (Object.hasOwn(saveObj, "AliasesSave")) {
|
||||||
try {
|
try {
|
||||||
loadAliases(saveObj.AliasesSave);
|
loadAliases(saveObj.AliasesSave);
|
||||||
@ -859,17 +868,17 @@ function createNewUpdateText() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createBetaUpdateText() {
|
function createBetaUpdateText() {
|
||||||
setTimeout(
|
// setTimeout(
|
||||||
() =>
|
// () =>
|
||||||
dialogBoxCreate(
|
// dialogBoxCreate(
|
||||||
"You are playing on the beta environment! This branch of the game " +
|
// "You are playing on the beta environment! This branch of the game " +
|
||||||
"features the latest developments in the game. This version may be unstable.\n" +
|
// "features the latest developments in the game. This version may be unstable.\n" +
|
||||||
"Please report any bugs/issues through the github repository (https://github.com/bitburner-official/bitburner-src/issues) " +
|
// "Please report any bugs/issues through the github repository (https://github.com/bitburner-official/bitburner-src/issues) " +
|
||||||
"or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
|
// "or the Bitburner subreddit (reddit.com/r/bitburner).\n\n" +
|
||||||
CONSTANTS.LatestUpdate,
|
// CONSTANTS.LatestUpdate,
|
||||||
),
|
// ),
|
||||||
1000,
|
// 1000,
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
constructorsForReviver.BitburnerSaveObject = BitburnerSaveObject;
|
constructorsForReviver.BitburnerSaveObject = BitburnerSaveObject;
|
||||||
|
105
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
105
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -5143,6 +5143,105 @@ interface Stanek {
|
|||||||
acceptGift(): boolean;
|
acceptGift(): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Tile {
|
||||||
|
Empty = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EntityType {
|
||||||
|
Bot = "bot",
|
||||||
|
Dispenser = "dispenser",
|
||||||
|
Dock = "dock",
|
||||||
|
Crafter = "crafter",
|
||||||
|
Chest = "chest",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Item {
|
||||||
|
BasicR = "basicr",
|
||||||
|
BasicG = "basicg",
|
||||||
|
BasicB = "basicb",
|
||||||
|
ComplexR = "complexr",
|
||||||
|
ComplexG = "complexg",
|
||||||
|
ComplexB = "complexb",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseEntity {
|
||||||
|
type: EntityType;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Bot extends InventoryEntity {
|
||||||
|
type: EntityType.Bot;
|
||||||
|
name: string;
|
||||||
|
energy: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InventoryEntity extends BaseEntity {
|
||||||
|
inventory: Item[];
|
||||||
|
maxInventory: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Dispenser extends InventoryEntity {
|
||||||
|
type: EntityType.Dispenser;
|
||||||
|
dispensing: Item;
|
||||||
|
cooldown: number;
|
||||||
|
cooldownUntil: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Dock extends InventoryEntity {
|
||||||
|
type: EntityType.Dock;
|
||||||
|
potentialRequest: Item[];
|
||||||
|
potentialRequestCount: number;
|
||||||
|
currentRequest: Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Chest extends InventoryEntity {
|
||||||
|
type: EntityType.Chest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Crafter extends InventoryEntity {
|
||||||
|
type: EntityType.Crafter;
|
||||||
|
recipe: Recipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Recipe {
|
||||||
|
input: Item[];
|
||||||
|
output: Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Entity = Bot | Dispenser | Dock | Crafter | Chest;
|
||||||
|
|
||||||
|
interface Factory {
|
||||||
|
/**
|
||||||
|
* Move a bot
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
* @returns true if the move succeeded, false otherwise.
|
||||||
|
*/
|
||||||
|
moveBot(name: string, x: number, y: number): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information about a bot
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
* @returns bot information
|
||||||
|
*/
|
||||||
|
getBot(name: string): Bot | undefined;
|
||||||
|
|
||||||
|
entityAt(x: number, y: number): Entity | undefined;
|
||||||
|
|
||||||
|
entities(): Entity[];
|
||||||
|
|
||||||
|
transfer(name: string, x: number, y: number, pickup: Item[], drop: Item[]): Promise<boolean>;
|
||||||
|
|
||||||
|
craft(name: string, x: number, y: number): Promise<boolean>;
|
||||||
|
|
||||||
|
getBits(): number;
|
||||||
|
|
||||||
|
getBotPrice(): number;
|
||||||
|
purchaseBot(name: string, x: number, y: number): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
interface InfiltrationReward {
|
interface InfiltrationReward {
|
||||||
tradeRep: number;
|
tradeRep: number;
|
||||||
@ -5348,6 +5447,12 @@ export interface NS {
|
|||||||
*/
|
*/
|
||||||
readonly stanek: Stanek;
|
readonly stanek: Stanek;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace for factory functions. Contains spoilers.
|
||||||
|
* @remarks RAM cost: 0 GB
|
||||||
|
*/
|
||||||
|
readonly factory: Factory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Namespace for infiltration functions.
|
* Namespace for infiltration functions.
|
||||||
* @remarks RAM cost: 0 GB
|
* @remarks RAM cost: 0 GB
|
||||||
|
@ -354,6 +354,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
|||||||
canCorporation && { key_: Page.Corporation, icon: BusinessIcon },
|
canCorporation && { key_: Page.Corporation, icon: BusinessIcon },
|
||||||
canGang && { key_: Page.Gang, icon: SportsMmaIcon },
|
canGang && { key_: Page.Gang, icon: SportsMmaIcon },
|
||||||
canIPvGO && { key_: Page.Go, icon: BorderInnerSharp },
|
canIPvGO && { key_: Page.Go, icon: BorderInnerSharp },
|
||||||
|
{ key_: Page.Factory, icon: SportsMmaIcon },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -347,31 +347,31 @@ const Engine: {
|
|||||||
|
|
||||||
Player.lastUpdate = Engine._lastUpdate;
|
Player.lastUpdate = Engine._lastUpdate;
|
||||||
Engine.start(); // Run main game loop and Scripts loop
|
Engine.start(); // Run main game loop and Scripts loop
|
||||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
// const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||||
setTimeout(
|
// setTimeout(
|
||||||
() =>
|
// () =>
|
||||||
AlertEvents.emit(
|
// AlertEvents.emit(
|
||||||
<>
|
// <>
|
||||||
<Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
// <Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||||
<ul>
|
// <ul>
|
||||||
<li>
|
// <li>
|
||||||
<Typography>
|
// <Typography>
|
||||||
Your scripts generated <Money money={offlineHackingIncome} />
|
// Your scripts generated <Money money={offlineHackingIncome} />
|
||||||
</Typography>
|
// </Typography>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
// <Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||||
</li>
|
// </li>
|
||||||
<li>
|
// <li>
|
||||||
<Typography>
|
// <Typography>
|
||||||
You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
// You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||||
</Typography>
|
// </Typography>
|
||||||
</li>
|
// </li>
|
||||||
</ul>
|
// </ul>
|
||||||
</>,
|
// </>,
|
||||||
),
|
// ),
|
||||||
250,
|
// 250,
|
||||||
);
|
// );
|
||||||
} else {
|
} else {
|
||||||
// No save found, start new game
|
// No save found, start new game
|
||||||
FormatsNeedToChange.emit();
|
FormatsNeedToChange.emit();
|
||||||
|
@ -72,6 +72,7 @@ import { MathJaxContext } from "better-react-mathjax";
|
|||||||
import { useRerender } from "./React/hooks";
|
import { useRerender } from "./React/hooks";
|
||||||
import { HistoryProvider } from "./React/Documentation";
|
import { HistoryProvider } from "./React/Documentation";
|
||||||
import { GoRoot } from "../Go/ui/GoRoot";
|
import { GoRoot } from "../Go/ui/GoRoot";
|
||||||
|
import { FactoryRoot } from "../Factory/ui/FactoryRoot";
|
||||||
|
|
||||||
const htmlLocation = location;
|
const htmlLocation = location;
|
||||||
|
|
||||||
@ -115,6 +116,7 @@ export let Router: IRouter = {
|
|||||||
function determineStartPage() {
|
function determineStartPage() {
|
||||||
if (RecoveryMode) return Page.Recovery;
|
if (RecoveryMode) return Page.Recovery;
|
||||||
if (Player.currentWork !== null) return Page.Work;
|
if (Player.currentWork !== null) return Page.Work;
|
||||||
|
return Page.Factory;
|
||||||
return Page.Terminal;
|
return Page.Terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,6 +362,10 @@ export function GameRoot(): React.ReactElement {
|
|||||||
mainPage = <GoRoot />;
|
mainPage = <GoRoot />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Page.Factory: {
|
||||||
|
mainPage = <FactoryRoot />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Page.Achievements: {
|
case Page.Achievements: {
|
||||||
mainPage = <AchievementsRoot />;
|
mainPage = <AchievementsRoot />;
|
||||||
break;
|
break;
|
||||||
|
@ -37,6 +37,7 @@ export enum SimplePage {
|
|||||||
Recovery = "Recovery",
|
Recovery = "Recovery",
|
||||||
Achievements = "Achievements",
|
Achievements = "Achievements",
|
||||||
ThemeBrowser = "Theme Browser",
|
ThemeBrowser = "Theme Browser",
|
||||||
|
Factory = "Factory",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ComplexPage {
|
export enum ComplexPage {
|
||||||
|
Loading…
Reference in New Issue
Block a user