Merge pull request #2095 from MartinFournier/fix/bladeburner

Bladeburner console fixes & command checks
This commit is contained in:
hydroflame 2021-12-21 10:59:47 -05:00 committed by GitHub
commit f01b12a56a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 85 deletions

@ -34,6 +34,12 @@ import { getTimestamp } from "../utils/helpers/getTimestamp";
import { joinFaction } from "../Faction/FactionHelpers";
import { WorkerScript } from "../Netscript/WorkerScript";
interface BlackOpsAttempt {
error?: string;
isAvailable?: boolean;
action?: BlackOperation;
}
export class Bladeburner implements IBladeburner {
numHosp = 0;
moneyLost = 0;
@ -113,6 +119,44 @@ export class Bladeburner implements IBladeburner {
return Math.min(1, this.stamina / (0.5 * this.maxStamina));
}
canAttemptBlackOp(actionId: IActionIdentifier): BlackOpsAttempt {
// Safety measure - don't repeat BlackOps that are already done
if (this.blackops[actionId.name] != null) {
return { error: "Tried to start a Black Operation that had already been completed" }
}
const action = this.getActionObject(actionId);
if (!(action instanceof BlackOperation)) throw new Error(`Action should be BlackOperation but isn't`);
if (action == null) throw new Error("Failed to get BlackOperation object for: " + actionId.name);
if (action.reqdRank > this.rank) {
return { error: "Tried to start a Black Operation without the rank requirement" };
}
// Can't start a BlackOp if you haven't done the one before it
const blackops = [];
for (const nm in BlackOperations) {
if (BlackOperations.hasOwnProperty(nm)) {
blackops.push(nm);
}
}
blackops.sort(function (a, b) {
return BlackOperations[a].reqdRank - BlackOperations[b].reqdRank; // Sort black ops in intended order
});
const i = blackops.indexOf(actionId.name);
if (i === -1) {
return { error: `Invalid Black Op: '${name}'` };
}
if (i > 0 && this.blackops[blackops[i - 1]] == null) {
return { error: `Preceding Black Op must be completed before starting '${actionId.name}'.` }
}
return { isAvailable: true, action }
}
startAction(player: IPlayer, actionId: IActionIdentifier): void {
if (actionId == null) return;
this.action = actionId;
@ -156,18 +200,13 @@ export class Bladeburner implements IBladeburner {
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]: {
try {
// Safety measure - don't repeat BlackOps that are already done
if (this.blackops[actionId.name] != null) {
const testBlackOp = this.canAttemptBlackOp(actionId);
if (!testBlackOp.isAvailable) {
this.resetAction();
this.log("Error: Tried to start a Black Operation that had already been completed");
this.log(`Error: ${testBlackOp.error}`);
break;
}
const action = this.getActionObject(actionId);
if (action == null) {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
this.actionTimeToComplete = action.getActionTime(this);
this.actionTimeToComplete = testBlackOp.action.getActionTime(this);
} catch (e: any) {
exceptionAlert(e);
}
@ -502,6 +541,7 @@ export class Bladeburner implements IBladeburner {
const skill = Skills[skillName];
if (skill == null || !(skill instanceof Skill)) {
this.postToConsole("Invalid skill name (Note that it is case-sensitive): " + skillName);
break;
}
if (args[1].toLowerCase() === "list") {
let level = 0;
@ -515,7 +555,11 @@ export class Bladeburner implements IBladeburner {
currentLevel = this.skills[skillName];
}
const pointCost = skill.calculateCost(currentLevel);
if (this.skillPoints >= pointCost) {
if (skill.maxLvl !== 0 && currentLevel >= skill.maxLvl) {
this.postToConsole(
`This skill ${skill.name} is already at max level (${currentLevel}/${skill.maxLvl}).`,
);
} else if (this.skillPoints >= pointCost) {
this.skillPoints -= pointCost;
this.upgradeSkill(skill);
this.log(skill.name + " upgraded to Level " + this.skills[skillName]);
@ -2032,44 +2076,9 @@ export class Bladeburner implements IBladeburner {
// Special logic for Black Ops
if (actionId.type === ActionTypes["BlackOp"]) {
// Can't start a BlackOp if you don't have the required rank
const action = this.getActionObject(actionId);
if (action == null) throw new Error(`Action not found ${actionId.type}, ${actionId.name}`);
if (!(action instanceof BlackOperation)) throw new Error(`Action should be BlackOperation but isn't`);
//const blackOp = (action as BlackOperation);
if (action.reqdRank > this.rank) {
workerScript.log("bladeburner.startAction", () => `Insufficient rank to start Black Op '${actionId.name}'.`);
return false;
}
// Can't start a BlackOp if its already been done
if (this.blackops[actionId.name] != null) {
workerScript.log("bladeburner.startAction", () => `Black Op ${actionId.name} has already been completed.`);
return false;
}
// Can't start a BlackOp if you haven't done the one before it
const blackops = [];
for (const nm in BlackOperations) {
if (BlackOperations.hasOwnProperty(nm)) {
blackops.push(nm);
}
}
blackops.sort(function (a, b) {
return BlackOperations[a].reqdRank - BlackOperations[b].reqdRank; // Sort black ops in intended order
});
const i = blackops.indexOf(actionId.name);
if (i === -1) {
workerScript.log("bladeburner.startAction", () => `Invalid Black Op: '${name}'`);
return false;
}
if (i > 0 && this.blackops[blackops[i - 1]] == null) {
workerScript.log(
"bladeburner.startAction",
() => `Preceding Black Op must be completed before starting '${actionId.name}'.`,
);
const canRunOp = this.canAttemptBlackOp(actionId);
if (!canRunOp.isAvailable) {
workerScript.log("bladeburner.startAction", () => canRunOp.error);
return false;
}
}

@ -54,7 +54,6 @@ interface IProps {
export function Console(props: IProps): React.ReactElement {
const classes = useStyles();
const scrollHook = useRef<HTMLDivElement>(null);
const [command, setCommand] = useState("");
const setRerender = useState(false)[1];
@ -64,22 +63,14 @@ export function Console(props: IProps): React.ReactElement {
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
// TODO: Figure out how to actually make the scrolling work correctly.
function scrollToBottom(): void {
if (!scrollHook.current) return;
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
}
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
const id2 = setInterval(scrollToBottom, 100);
return () => {
clearInterval(id);
clearInterval(id2);
};
}, []);
@ -113,6 +104,7 @@ export function Console(props: IProps): React.ReactElement {
setConsoleHistoryIndex(i);
const prevCommand = consoleHistory[i];
event.currentTarget.value = prevCommand;
setCommand(prevCommand);
}
if (event.keyCode === 40) {
@ -134,39 +126,68 @@ export function Console(props: IProps): React.ReactElement {
setConsoleHistoryIndex(consoleHistoryIndex + 1);
const prevCommand = consoleHistory[consoleHistoryIndex + 1];
event.currentTarget.value = prevCommand;
setCommand(prevCommand);
}
}
}
return (
<Box height={"60vh"} display={"flex"} alignItems={"stretch"} component={Paper}>
<Box>
<List sx={{ height: "100%", overflow: "auto" }}>
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
<Line key={i} content={log} />
))}
<TextField
classes={{ root: classes.textfield }}
autoFocus
tabIndex={1}
type="text"
value={command}
onChange={handleCommandChange}
onKeyDown={handleKeyDown}
InputProps={{
// for players to hook in
className: classes.input,
startAdornment: (
<>
<Typography>&gt;&nbsp;</Typography>
</>
),
spellCheck: false,
}}
/>
</List>
<div ref={scrollHook}></div>
<Paper>
<Box sx={{
height: '60vh',
paddingBottom: '8px',
display: 'flex',
alignItems: 'stretch',
}}>
<Box>
<Logs entries={[...props.bladeburner.consoleLogs]} />
</Box>
</Box>
</Box>
<TextField
classes={{ root: classes.textfield }}
autoFocus
tabIndex={1}
type="text"
value={command}
onChange={handleCommandChange}
onKeyDown={handleKeyDown}
InputProps={{
// for players to hook in
className: classes.input,
startAdornment: (
<>
<Typography>&gt;&nbsp;</Typography>
</>
),
spellCheck: false,
}}
/>
</Paper>
);
}
interface ILogProps {
entries: string[];
}
function Logs({entries}: ILogProps): React.ReactElement {
const scrollHook = useRef<HTMLUListElement>(null);
// TODO: Text gets shifted up as new entries appear, if the user scrolled up it should attempt to keep the text focused
function scrollToBottom(): void {
if (!scrollHook.current) return;
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
}
useEffect(() => {
scrollToBottom();
}, [entries]);
return (
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
{entries && entries.map((log: any, i: number) => (
<Line key={i} content={log} />
))}
</List>
);
}