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.
|
||||
*/
|
||||
const content = Buffer.from(saveData).toString("base64");
|
||||
log.debug(`saveData: ${saveData.length} bytes`);
|
||||
log.debug(`Base64 string of saveData: ${content.length} bytes`);
|
||||
const content = saveData.toString("base64");
|
||||
log.debug(`Uncompressed: ${saveData.length} bytes`);
|
||||
log.debug(`Compressed: ${content.length} bytes`);
|
||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||
|
||||
try {
|
||||
|
@ -158,12 +158,10 @@ export const CONSTANTS: {
|
||||
|
||||
// Also update doc/source/changelog.rst
|
||||
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
|
||||
|
||||
### HOTFIX (changes also added to 2.6.1 post release)
|
||||
|
||||
- Fixed an issue with invalid format on steam cloud save (@catloversg)
|
||||
No changes yet since 2.6.1 release
|
||||
`,
|
||||
};
|
||||
|
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,
|
||||
} as const;
|
||||
|
||||
const factory: any = new Proxy(
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// UI API
|
||||
const ui = {
|
||||
getTheme: 0,
|
||||
@ -469,6 +478,7 @@ export const RamCosts: RamCostTree<NSFull> = {
|
||||
codingcontract,
|
||||
sleeve,
|
||||
stanek,
|
||||
factory,
|
||||
ui,
|
||||
grafting,
|
||||
|
||||
|
@ -108,6 +108,7 @@ import { getEnumHelper } from "./utils/EnumHelper";
|
||||
import { setDeprecatedProperties, deprecationWarning } from "./utils/DeprecationHelper";
|
||||
import { ServerConstants } from "./Server/data/Constants";
|
||||
import { assertFunction } from "./Netscript/TypeAssertion";
|
||||
import { NetscriptFactory } from "./NetscriptFunctions/Factory";
|
||||
|
||||
export const enums: NSEnums = {
|
||||
CityName,
|
||||
@ -135,6 +136,7 @@ export const ns: InternalAPI<NSFull> = {
|
||||
sleeve: NetscriptSleeve(),
|
||||
corporation: NetscriptCorporation(),
|
||||
stanek: NetscriptStanek(),
|
||||
factory: NetscriptFactory(),
|
||||
infiltration: NetscriptInfiltration(),
|
||||
ui: NetscriptUserInterface(),
|
||||
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 { breakInfos261 } from "./utils/APIBreaks/2.6.1";
|
||||
import { handleGetSaveDataError } from "./Netscript/ErrorMessages";
|
||||
import { factory, loadFactory } from "./Factory/Helper";
|
||||
|
||||
/* SaveObject.js
|
||||
* Defines the object used to save/load games
|
||||
@ -96,6 +97,7 @@ class BitburnerSaveObject {
|
||||
AllGangsSave = "";
|
||||
LastExportBonus = "0";
|
||||
StaneksGiftSave = "";
|
||||
FactorySave = "";
|
||||
GoSave = "";
|
||||
|
||||
async getSaveData(forceExcludeRunningScripts = false): Promise<SaveData> {
|
||||
@ -116,6 +118,7 @@ class BitburnerSaveObject {
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||
this.FactorySave = JSON.stringify(factory);
|
||||
this.GoSave = JSON.stringify(getGoSave());
|
||||
|
||||
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`);
|
||||
loadStaneksGift("");
|
||||
}
|
||||
|
||||
if (Object.hasOwn(saveObj, "FactorySave")) {
|
||||
loadFactory(saveObj.FactorySave);
|
||||
} else {
|
||||
console.warn(`Could not load Factory from save`);
|
||||
}
|
||||
if (Object.hasOwn(saveObj, "AliasesSave")) {
|
||||
try {
|
||||
loadAliases(saveObj.AliasesSave);
|
||||
@ -859,17 +868,17 @@ function createNewUpdateText() {
|
||||
}
|
||||
|
||||
function createBetaUpdateText() {
|
||||
setTimeout(
|
||||
() =>
|
||||
dialogBoxCreate(
|
||||
"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" +
|
||||
"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" +
|
||||
CONSTANTS.LatestUpdate,
|
||||
),
|
||||
1000,
|
||||
);
|
||||
// setTimeout(
|
||||
// () =>
|
||||
// dialogBoxCreate(
|
||||
// "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" +
|
||||
// "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" +
|
||||
// CONSTANTS.LatestUpdate,
|
||||
// ),
|
||||
// 1000,
|
||||
// );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
interface InfiltrationReward {
|
||||
tradeRep: number;
|
||||
@ -5348,6 +5447,12 @@ export interface NS {
|
||||
*/
|
||||
readonly stanek: Stanek;
|
||||
|
||||
/**
|
||||
* Namespace for factory functions. Contains spoilers.
|
||||
* @remarks RAM cost: 0 GB
|
||||
*/
|
||||
readonly factory: Factory;
|
||||
|
||||
/**
|
||||
* Namespace for infiltration functions.
|
||||
* @remarks RAM cost: 0 GB
|
||||
|
@ -354,6 +354,7 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
||||
canCorporation && { key_: Page.Corporation, icon: BusinessIcon },
|
||||
canGang && { key_: Page.Gang, icon: SportsMmaIcon },
|
||||
canIPvGO && { key_: Page.Go, icon: BorderInnerSharp },
|
||||
{ key_: Page.Factory, icon: SportsMmaIcon },
|
||||
]}
|
||||
/>
|
||||
<Divider />
|
||||
|
@ -347,31 +347,31 @@ const Engine: {
|
||||
|
||||
Player.lastUpdate = Engine._lastUpdate;
|
||||
Engine.start(); // Run main game loop and Scripts loop
|
||||
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
setTimeout(
|
||||
() =>
|
||||
AlertEvents.emit(
|
||||
<>
|
||||
<Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography>
|
||||
Your scripts generated <Money money={offlineHackingIncome} />
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography>
|
||||
You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</>,
|
||||
),
|
||||
250,
|
||||
);
|
||||
// const timeOfflineString = convertTimeMsToTimeElapsedString(time);
|
||||
// setTimeout(
|
||||
// () =>
|
||||
// AlertEvents.emit(
|
||||
// <>
|
||||
// <Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||
// <ul>
|
||||
// <li>
|
||||
// <Typography>
|
||||
// Your scripts generated <Money money={offlineHackingIncome} />
|
||||
// </Typography>
|
||||
// </li>
|
||||
// <li>
|
||||
// <Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||
// </li>
|
||||
// <li>
|
||||
// <Typography>
|
||||
// You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||
// </Typography>
|
||||
// </li>
|
||||
// </ul>
|
||||
// </>,
|
||||
// ),
|
||||
// 250,
|
||||
// );
|
||||
} else {
|
||||
// No save found, start new game
|
||||
FormatsNeedToChange.emit();
|
||||
|
@ -72,6 +72,7 @@ import { MathJaxContext } from "better-react-mathjax";
|
||||
import { useRerender } from "./React/hooks";
|
||||
import { HistoryProvider } from "./React/Documentation";
|
||||
import { GoRoot } from "../Go/ui/GoRoot";
|
||||
import { FactoryRoot } from "../Factory/ui/FactoryRoot";
|
||||
|
||||
const htmlLocation = location;
|
||||
|
||||
@ -115,6 +116,7 @@ export let Router: IRouter = {
|
||||
function determineStartPage() {
|
||||
if (RecoveryMode) return Page.Recovery;
|
||||
if (Player.currentWork !== null) return Page.Work;
|
||||
return Page.Factory;
|
||||
return Page.Terminal;
|
||||
}
|
||||
|
||||
@ -360,6 +362,10 @@ export function GameRoot(): React.ReactElement {
|
||||
mainPage = <GoRoot />;
|
||||
break;
|
||||
}
|
||||
case Page.Factory: {
|
||||
mainPage = <FactoryRoot />;
|
||||
break;
|
||||
}
|
||||
case Page.Achievements: {
|
||||
mainPage = <AchievementsRoot />;
|
||||
break;
|
||||
|
@ -37,6 +37,7 @@ export enum SimplePage {
|
||||
Recovery = "Recovery",
|
||||
Achievements = "Achievements",
|
||||
ThemeBrowser = "Theme Browser",
|
||||
Factory = "Factory",
|
||||
}
|
||||
|
||||
export enum ComplexPage {
|
||||
|
Loading…
Reference in New Issue
Block a user