more updates

* added more keycodes constant support
* implemented new faction mechanics for mini games
* more small refactors
This commit is contained in:
phyzical 2022-03-23 22:31:56 +08:00
parent 4e73e489ed
commit 0b171822df
16 changed files with 236 additions and 97 deletions

@ -131,7 +131,7 @@ export const infiltratorsAugmentations = [
factions: [FactionNames.Infiltrators], factions: [FactionNames.Infiltrators],
}), }),
new Augmentation({ new Augmentation({
name: AugmentationNames.AmuletOfPersuasian, name: AugmentationNames.AmuletOfPersuasion,
repCost: 1e2, repCost: 1e2,
moneyCost: 1e6, moneyCost: 1e6,
info: info:

@ -114,7 +114,7 @@ export const AugmentationNames: {
BagOfSand: string; BagOfSand: string;
IntellisenseModule: string; IntellisenseModule: string;
ReverseDictionary: string; ReverseDictionary: string;
AmuletOfPersuasian: string; AmuletOfPersuasion: string;
LameSharkRepository: string; LameSharkRepository: string;
CyberDecoder: string; CyberDecoder: string;
MineDetector: string; MineDetector: string;
@ -237,7 +237,7 @@ export const AugmentationNames: {
BagOfSand: "A Bag of Sand", BagOfSand: "A Bag of Sand",
IntellisenseModule: "Intellisense Module", IntellisenseModule: "Intellisense Module",
ReverseDictionary: "Reverse Dictionary", ReverseDictionary: "Reverse Dictionary",
AmuletOfPersuasian: "Amulet of Persuasian", AmuletOfPersuasion: "Amulet of Persuasian",
LameSharkRepository: "Lame Shark Repository", LameSharkRepository: "Lame Shark Repository",
CyberDecoder: "Cyber Decoder", CyberDecoder: "Cyber Decoder",
MineDetector: "Mine Detector", MineDetector: "Mine Detector",

@ -170,7 +170,6 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<> <>
{Object.values(BitNodes) {Object.values(BitNodes)
.filter((node) => { .filter((node) => {
console.log(node.desc);
return node.desc !== "COMING SOON"; return node.desc !== "COMING SOON";
}) })
.map((node) => { .map((node) => {

@ -109,7 +109,7 @@ export function Console(props: IProps): React.ReactElement {
setCommand(prevCommand); setCommand(prevCommand);
} }
if (event.key === KEY.DOWNARROW) { if (event.key === KEY.DOWN_ARROW) {
const i = consoleHistoryIndex; const i = consoleHistoryIndex;
const len = consoleHistory.length; const len = consoleHistory.length;
@ -140,14 +140,16 @@ export function Console(props: IProps): React.ReactElement {
return ( return (
<Paper sx={{ p: 1 }}> <Paper sx={{ p: 1 }}>
<Box sx={{ <Box
height: '60vh', sx={{
paddingBottom: '8px', height: "60vh",
display: 'flex', paddingBottom: "8px",
alignItems: 'stretch', display: "flex",
whiteSpace: 'pre-wrap', alignItems: "stretch",
whiteSpace: "pre-wrap",
}} }}
onClick={handleClick}> onClick={handleClick}
>
<Box> <Box>
<Logs entries={[...props.bladeburner.consoleLogs]} /> <Logs entries={[...props.bladeburner.consoleLogs]} />
</Box> </Box>
@ -195,9 +197,7 @@ function Logs({ entries }: ILogProps): React.ReactElement {
return ( return (
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}> <List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
{entries && entries.map((log: any, i: number) => ( {entries && entries.map((log: any, i: number) => <Line key={i} content={log} />)}
<Line key={i} content={log} />
))}
</List> </List>
); );
} }

@ -7,6 +7,9 @@ import { random } from "../utils";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import { BlinkingCursor } from "./BlinkingCursor"; import { BlinkingCursor } from "./BlinkingCursor";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { KEY } from "../../utils/helpers/keyCodes";
import { Player } from "../../Player";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -33,16 +36,26 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
const timer = difficulty.timer; const timer = difficulty.timer;
const [answer] = useState(makeAnswer(difficulty)); const [answer] = useState(makeAnswer(difficulty));
const [guess, setGuess] = useState(""); const [guess, setGuess] = useState("");
const hasAugment = Player.hasAugmentation(AugmentationNames.ReverseDictionary, true);
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
if (event.key === "Backspace") return; if (event.key === KEY.BACKSPACE) return;
const nextGuess = guess + event.key.toUpperCase(); const nextGuess = guess + event.key.toUpperCase();
if (!answer.startsWith(nextGuess)) props.onFailure(); if (!answer.startsWith(nextGuess)) props.onFailure();
else if (answer === nextGuess) props.onSuccess(); else if (answer === nextGuess) props.onSuccess();
else setGuess(nextGuess); else setGuess(nextGuess);
} }
interface AnswerStyle {
transform?: string;
}
const answerStyle: AnswerStyle = { transform: "scaleX(-1)" };
if (hasAugment) {
delete answerStyle.transform;
}
return ( return (
<Grid container spacing={3}> <Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} /> <GameTimer millis={timer} onExpire={props.onFailure} />
@ -51,7 +64,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
<KeyHandler onKeyDown={press} onFailure={props.onFailure} /> <KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Typography style={{ transform: "scaleX(-1)" }}>{answer}</Typography> <Typography style={answerStyle}>{answer}</Typography>
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Typography> <Typography>

@ -7,6 +7,9 @@ import { random } from "../utils";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import { BlinkingCursor } from "./BlinkingCursor"; import { BlinkingCursor } from "./BlinkingCursor";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { Player } from "../../Player";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { KEY } from "../../utils/helpers/keyCodes";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -29,28 +32,32 @@ const difficulties: {
function generateLeftSide(difficulty: Difficulty): string { function generateLeftSide(difficulty: Difficulty): string {
let str = ""; let str = "";
const options = [KEY.OPEN_BRACKET, KEY.LESS_THAN, KEY.OPEN_PARENTHESIS, KEY.OPEN_BRACE];
if (Player.hasAugmentation(AugmentationNames.IntellisenseModule, true)) {
options.splice(0, 1);
}
const length = random(difficulty.min, difficulty.max); const length = random(difficulty.min, difficulty.max);
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
str += ["[", "<", "(", "{"][Math.floor(Math.random() * 4)]; str += options[Math.floor(Math.random() * 4)];
} }
return str; return str;
} }
function getChar(event: KeyboardEvent): string { function getChar(event: KeyboardEvent): string {
if (event.key === ")") return ")"; if (event.key === KEY.CLOSE_PARENTHESIS) return KEY.CLOSE_PARENTHESIS;
if (event.key === "]") return "]"; if (event.key === KEY.CLOSE_BRACKET) return KEY.CLOSE_BRACKET;
if (event.key === "}") return "}"; if (event.key === KEY.CLOSE_BRACE) return KEY.CLOSE_BRACE;
if (event.key === ">") return ">"; if (event.key === KEY.GREATER_THAN) return KEY.GREATER_THAN;
return ""; return "";
} }
function match(left: string, right: string): boolean { function match(left: string, right: string): boolean {
return ( return (
(left === "[" && right === "]") || (left === KEY.OPEN_BRACKET && right === KEY.CLOSE_BRACKET) ||
(left === "<" && right === ">") || (left === KEY.LESS_THAN && right === KEY.GREATER_THAN) ||
(left === "(" && right === ")") || (left === KEY.OPEN_PARENTHESIS && right === KEY.CLOSE_PARENTHESIS) ||
(left === "{" && right === "}") (left === KEY.OPEN_BRACE && right === KEY.CLOSE_BRACE)
); );
} }

@ -1,10 +1,15 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import { IMinigameProps } from "./IMinigameProps"; import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler"; import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { KEY } from "../../utils/helpers/keyCodes";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings";
import { downArrowSymbol, upArrowSymbol } from "../utils";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -29,20 +34,40 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
interpolate(difficulties, props.difficulty, difficulty); interpolate(difficulties, props.difficulty, difficulty);
const timer = difficulty.timer; const timer = difficulty.timer;
const [choices] = useState(makeChoices(difficulty)); const [choices] = useState(makeChoices(difficulty));
const [correctIndex, setCorrectIndex] = useState(0);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const currentChoice = choices[index];
useEffect(() => {
setCorrectIndex(choices.findIndex((choice) => positive.includes(choice)));
}, [choices]);
const defaultColor = Settings.theme.primary;
const disabledColor = Settings.theme.disabled;
let upColor = defaultColor;
let downColor = defaultColor;
let choiceColor = defaultColor;
const hasAugment = Player.hasAugmentation(AugmentationNames.AmuletOfPersuasion, true);
if (hasAugment) {
upColor = correctIndex < index ? upColor : disabledColor;
downColor = correctIndex > index ? upColor : disabledColor;
choiceColor = correctIndex == index ? Settings.theme.success : disabledColor;
}
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
const k = event.key; const k = event.key;
if (k === " ") { if (k === KEY.SPACE) {
if (positive.includes(choices[index])) props.onSuccess(); if (positive.includes(currentChoice)) props.onSuccess();
else props.onFailure(); else props.onFailure();
return; return;
} }
let newIndex = index; let newIndex = index;
if (["ArrowUp", "w", "ArrowRight", "d"].includes(k)) newIndex++; if ([KEY.UP_ARROW, KEY.W, KEY.RIGHT_ARROW, KEY.D].map((k) => k as string).includes(k)) newIndex++;
if (["ArrowDown", "s", "ArrowLeft", "a"].includes(k)) newIndex--; if ([KEY.DOWN_ARROW, KEY.S, KEY.LEFT_ARROW, KEY.A].map((k) => k as string).includes(k)) newIndex--;
while (newIndex < 0) newIndex += choices.length; while (newIndex < 0) newIndex += choices.length;
while (newIndex > choices.length - 1) newIndex -= choices.length; while (newIndex > choices.length - 1) newIndex -= choices.length;
setIndex(newIndex); setIndex(newIndex);
@ -56,14 +81,14 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
<KeyHandler onKeyDown={press} onFailure={props.onFailure} /> <KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Typography variant="h5" color="primary"> <Typography variant="h5" color={upColor}>
{upArrowSymbol}
</Typography> </Typography>
<Typography variant="h5" color="primary"> <Typography variant="h5" color={choiceColor}>
{choices[index]} {currentChoice}
</Typography> </Typography>
<Typography variant="h5" color="primary"> <Typography variant="h5" color={downColor}>
{downArrowSymbol}
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>

@ -3,9 +3,19 @@ import Grid from "@mui/material/Grid";
import { IMinigameProps } from "./IMinigameProps"; import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler"; import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { random, getArrow } from "../utils"; import {
random,
getArrow,
getInverseArrow,
leftArrowSymbol,
rightArrowSymbol,
upArrowSymbol,
downArrowSymbol,
} from "../utils";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -32,10 +42,11 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
const timer = difficulty.timer; const timer = difficulty.timer;
const [code] = useState(generateCode(difficulty)); const [code] = useState(generateCode(difficulty));
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const hasAugment = Player.hasAugmentation(AugmentationNames.LameSharkRepository, true);
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
if (code[index] !== getArrow(event)) { if (code[index] !== getArrow(event) || (hasAugment && getInverseArrow(event))) {
props.onFailure(); props.onFailure();
return; return;
} }
@ -56,7 +67,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
} }
function generateCode(difficulty: Difficulty): string { function generateCode(difficulty: Difficulty): string {
const arrows = ["←", "→", "↑", "↓"]; const arrows = [leftArrowSymbol, rightArrowSymbol, upArrowSymbol, downArrowSymbol];
let code = ""; let code = "";
for (let i = 0; i < random(difficulty.min, difficulty.max); i++) { for (let i = 0; i < random(difficulty.min, difficulty.max); i++) {
let arrow = arrows[Math.floor(4 * Math.random())]; let arrow = arrows[Math.floor(4 * Math.random())];

@ -4,8 +4,12 @@ import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler"; import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import { getArrow } from "../utils"; import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { KEY } from "../../utils/helpers/keyCodes";
import { Settings } from "../../Settings/Settings";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -32,25 +36,26 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
interpolate(difficulties, props.difficulty, difficulty); interpolate(difficulties, props.difficulty, difficulty);
const timer = difficulty.timer; const timer = difficulty.timer;
const [grid] = useState(generatePuzzle(difficulty)); const [grid] = useState(generatePuzzle(difficulty));
const [answer] = useState(generateAnswer(grid, difficulty)); const [answers] = useState(generateAnswers(grid, difficulty));
const [index, setIndex] = useState(0); const [currentAnswerIndex, setCurrentAnswerIndex] = useState(0);
const [pos, setPos] = useState([0, 0]); const [pos, setPos] = useState([0, 0]);
const hasAugment = Player.hasAugmentation(AugmentationNames.CyberDecoder, true);
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
const move = [0, 0]; const move = [0, 0];
const arrow = getArrow(event); const arrow = getArrow(event);
switch (arrow) { switch (arrow) {
case "↑": case upArrowSymbol:
move[1]--; move[1]--;
break; break;
case "←": case leftArrowSymbol:
move[0]--; move[0]--;
break; break;
case "↓": case downArrowSymbol:
move[1]++; move[1]++;
break; break;
case "→": case rightArrowSymbol:
move[0]++; move[0]++;
break; break;
} }
@ -59,15 +64,15 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
next[1] = (next[1] + grid.length) % grid.length; next[1] = (next[1] + grid.length) % grid.length;
setPos(next); setPos(next);
if (event.key === " ") { if (event.key === KEY.SPACE) {
const selected = grid[pos[1]][pos[0]]; const selected = grid[pos[1]][pos[0]];
const expected = answer[index]; const expected = answers[currentAnswerIndex];
if (selected !== expected) { if (selected !== expected) {
props.onFailure(); props.onFailure();
return; return;
} }
setIndex(index + 1); setCurrentAnswerIndex(currentAnswerIndex + 1);
if (answer.length === index + 1) props.onSuccess(); if (answers.length === currentAnswerIndex + 1) props.onSuccess();
} }
} }
@ -77,17 +82,17 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
<GameTimer millis={timer} onExpire={props.onFailure} /> <GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h4">Match the symbols!</Typography> <Typography variant="h4">Match the symbols!</Typography>
<Typography variant="h5" color="primary"> <Typography variant="h5" color={Settings.theme.primary}>
Targets:{" "} Targets:{" "}
{answer.map((a, i) => { {answers.map((a, i) => {
if (i == index) if (i == currentAnswerIndex)
return ( return (
<span key={`${i}`} style={{ fontSize: "1em", color: "blue" }}> <span key={`${i}`} style={{ fontSize: "1em", color: "blue" }}>
{a}&nbsp; {a}&nbsp;
</span> </span>
); );
return ( return (
<span key={`${i}`} style={{ fontSize: "1em" }}> <span key={`${i}`} style={{ fontSize: "1em", color: Settings.theme.primary }}>
{a}&nbsp; {a}&nbsp;
</span> </span>
); );
@ -98,14 +103,20 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
<div key={y}> <div key={y}>
<Typography> <Typography>
{line.map((cell, x) => { {line.map((cell, x) => {
if (x == pos[0] && y == pos[1]) const isCorrectAnswer = cell === answers[currentAnswerIndex];
if (x == pos[0] && y == pos[1]) {
const selectOptionColor = hasAugment && isCorrectAnswer ? Settings.theme.success : "blue";
return ( return (
<span key={`${x}${y}`} style={{ fontSize: fontSize, color: "blue" }}> <span key={`${x}${y}`} style={{ fontSize: fontSize, color: selectOptionColor }}>
{cell}&nbsp; {cell}&nbsp;
</span> </span>
); );
}
const optionColor = hasAugment && isCorrectAnswer ? Settings.theme.success : Settings.theme.primary;
return ( return (
<span key={`${x}${y}`} style={{ fontSize: fontSize }}> <span key={`${x}${y}`} style={{ fontSize: fontSize, color: optionColor }}>
{cell}&nbsp; {cell}&nbsp;
</span> </span>
); );
@ -120,12 +131,12 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
); );
} }
function generateAnswer(grid: string[][], difficulty: Difficulty): string[] { function generateAnswers(grid: string[][], difficulty: Difficulty): string[] {
const answer = []; const answers = [];
for (let i = 0; i < Math.round(difficulty.symbols); i++) { for (let i = 0; i < Math.round(difficulty.symbols); i++) {
answer.push(grid[Math.floor(Math.random() * grid.length)][Math.floor(Math.random() * grid[0].length)]); answers.push(grid[Math.floor(Math.random() * grid.length)][Math.floor(Math.random() * grid[0].length)]);
} }
return answer; return answers;
} }
function randChar(): string { function randChar(): string {

@ -42,7 +42,6 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
const startingDifficulty = props.location.infiltrationData.startingSecurityLevel; const startingDifficulty = props.location.infiltrationData.startingSecurityLevel;
const difficulty = calcDifficulty(player, startingDifficulty); const difficulty = calcDifficulty(player, startingDifficulty);
const reward = calcReward(player, startingDifficulty); const reward = calcReward(player, startingDifficulty);
console.log(`${difficulty} ${reward}`);
function cancel(): void { function cancel(): void {
router.toCity(); router.toCity();

@ -4,8 +4,11 @@ import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler"; import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import { getArrow } from "../utils"; import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { KEY } from "../../utils/helpers/keyCodes";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -35,23 +38,23 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
const [answer, setAnswer] = useState(generateEmptyField(difficulty)); const [answer, setAnswer] = useState(generateEmptyField(difficulty));
const [pos, setPos] = useState([0, 0]); const [pos, setPos] = useState([0, 0]);
const [memoryPhase, setMemoryPhase] = useState(true); const [memoryPhase, setMemoryPhase] = useState(true);
const hasAugment = Player.hasAugmentation(AugmentationNames.MineDetector, true);
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
if (memoryPhase) return; if (memoryPhase) return;
const move = [0, 0]; const move = [0, 0];
const arrow = getArrow(event); const arrow = getArrow(event);
switch (arrow) { switch (arrow) {
case "↑": case upArrowSymbol:
move[1]--; move[1]--;
break; break;
case "←": case leftArrowSymbol:
move[0]--; move[0]--;
break; break;
case "↓": case downArrowSymbol:
move[1]++; move[1]++;
break; break;
case "→": case rightArrowSymbol:
move[0]++; move[0]++;
break; break;
} }
@ -60,7 +63,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
next[1] = (next[1] + minefield.length) % minefield.length; next[1] = (next[1] + minefield.length) % minefield.length;
setPos(next); setPos(next);
if (event.key == " ") { if (event.key == KEY.SPACE) {
if (!minefield[pos[1]][pos[0]]) { if (!minefield[pos[1]][pos[0]]) {
props.onFailure(); props.onFailure();
return; return;
@ -93,6 +96,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
} else { } else {
if (x == pos[0] && y == pos[1]) return <span key={x}>[X]&nbsp;</span>; if (x == pos[0] && y == pos[1]) return <span key={x}>[X]&nbsp;</span>;
if (answer[y][x]) return <span key={x}>[.]&nbsp;</span>; if (answer[y][x]) return <span key={x}>[.]&nbsp;</span>;
if (hasAugment && minefield[y][x]) return <span key={x}>[?]&nbsp;</span>;
return <span key={x}>[&nbsp;]&nbsp;</span>; return <span key={x}>[&nbsp;]&nbsp;</span>;
} }
})} })}

@ -5,6 +5,9 @@ import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { KEY } from "../../utils/helpers/keyCodes";
import { Player } from "../../Player";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -30,13 +33,17 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
if (event.key !== " ") return; if (event.key !== KEY.SPACE) return;
if (phase !== 2) { if (phase !== 2) {
props.onFailure(); props.onFailure();
} else { } else {
props.onSuccess(); props.onSuccess();
} }
} }
const hasAugment = Player.hasAugmentation(AugmentationNames.BagOfSand, true);
const phaseZeroTime = Math.random() * 3250 + 1500 - (250 + difficulty.window);
const phaseOneTime = 250;
const timeUntilAttacking = phaseZeroTime + phaseOneTime;
useEffect(() => { useEffect(() => {
let id = window.setTimeout(() => { let id = window.setTimeout(() => {
@ -44,8 +51,8 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
id = window.setTimeout(() => { id = window.setTimeout(() => {
setPhase(2); setPhase(2);
id = window.setTimeout(() => setPhase(0), difficulty.window); id = window.setTimeout(() => setPhase(0), difficulty.window);
}, 250); }, phaseOneTime);
}, Math.random() * 3250 + 1500 - (250 + difficulty.window)); }, phaseZeroTime);
return () => { return () => {
clearInterval(id); clearInterval(id);
}; };
@ -56,6 +63,14 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
<GameTimer millis={5000} onExpire={props.onFailure} /> <GameTimer millis={5000} onExpire={props.onFailure} />
<Grid item xs={12}> <Grid item xs={12}>
<Typography variant="h4">Slash when his guard is down!</Typography> <Typography variant="h4">Slash when his guard is down!</Typography>
{hasAugment ? (
<>
<Typography variant="h4">Guard will drop in...</Typography>
<GameTimer millis={timeUntilAttacking} onExpire={props.onFailure} />
</>
) : (
<></>
)}
{phase === 0 && <Typography variant="h4">Guarding ...</Typography>} {phase === 0 && <Typography variant="h4">Guarding ...</Typography>}
{phase === 1 && <Typography variant="h4">Preparing?</Typography>} {phase === 1 && <Typography variant="h4">Preparing?</Typography>}
{phase === 2 && <Typography variant="h4">ATTACKING!</Typography>} {phase === 2 && <Typography variant="h4">ATTACKING!</Typography>}

@ -6,6 +6,10 @@ import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer"; import { GameTimer } from "./GameTimer";
import { random } from "../utils"; import { random } from "../utils";
import { interpolate } from "./Difficulty"; import { interpolate } from "./Difficulty";
import { KEY } from "../../utils/helpers/keyCodes";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings";
interface Difficulty { interface Difficulty {
[key: string]: number; [key: string]: number;
@ -27,7 +31,7 @@ const difficulties: {
Impossible: { timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4 }, Impossible: { timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4 },
}; };
const types = ["|", ".", "/", "-", "█", "#"]; const types = [KEY.PIPE, KEY.DOT, KEY.FORWARD_SLASH, KEY.HYPHEN, "█", KEY.HASH];
const colors = ["red", "#FFC107", "blue", "white"]; const colors = ["red", "#FFC107", "blue", "white"];
@ -56,10 +60,15 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
rules: 0, rules: 0,
}; };
interpolate(difficulties, props.difficulty, difficulty); interpolate(difficulties, props.difficulty, difficulty);
const timer = difficulty.timer; const timer = 99999;
const [wires] = useState(generateWires(difficulty)); const [wires] = useState(generateWires(difficulty));
const [cutWires, setCutWires] = useState(new Array(wires.length).fill(false)); const [cutWires, setCutWires] = useState(new Array(wires.length).fill(false));
const [questions] = useState(generateQuestion(wires, difficulty)); const [questions] = useState(generateQuestion(wires, difficulty));
const hasAugment = Player.hasAugmentation(AugmentationNames.WireCuttingManual, true);
function checkWire(wireNum: number): boolean {
return questions.some((q) => q.shouldCut(wires[wireNum - 1], wireNum - 1));
}
function press(this: Document, event: KeyboardEvent): void { function press(this: Document, event: KeyboardEvent): void {
event.preventDefault(); event.preventDefault();
@ -69,7 +78,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
setCutWires((old) => { setCutWires((old) => {
const next = [...old]; const next = [...old];
next[wireNum - 1] = true; next[wireNum - 1] = true;
if (!questions.some((q) => q.shouldCut(wires[wireNum - 1], wireNum - 1))) { if (!checkWire(wireNum)) {
props.onFailure(); props.onFailure();
} }
@ -107,10 +116,14 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
<div key={i}> <div key={i}>
<Typography> <Typography>
{wires.map((wire, j) => { {wires.map((wire, j) => {
if ((i === 3 || i === 4) && cutWires[j]) if ((i === 3 || i === 4) && cutWires[j]) {
return <span key={j}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>; return <span key={j}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>;
}
const isCorrectWire = checkWire(j);
const wireColor =
hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length];
return ( return (
<span key={j} style={{ color: wire.colors[i % wire.colors.length] }}> <span key={j} style={{ color: wireColor }}>
|{wire.tpe}|&nbsp;&nbsp;&nbsp; |{wire.tpe}|&nbsp;&nbsp;&nbsp;
</span> </span>
); );

@ -1,21 +1,46 @@
import { KEY } from "../utils/helpers/keyCodes";
export function random(min: number, max: number): number { export function random(min: number, max: number): number {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
export const upArrowSymbol = "↑";
export const downArrowSymbol = "↑";
export const leftArrowSymbol = "↑";
export const rightArrowSymbol = "↑";
export function getArrow(event: KeyboardEvent): string { export function getArrow(event: KeyboardEvent): string {
switch (event.key) { switch (event.key) {
case "ArrowUp": case KEY.UP_ARROW:
case "w": case KEY.W:
return "↑"; return upArrowSymbol;
case "ArrowLeft": case KEY.LEFT_ARROW:
case "a": case KEY.A:
return "←"; return leftArrowSymbol;
case "ArrowDown": case KEY.DOWN_ARROW:
case "s": case KEY.S:
return "↓"; return downArrowSymbol;
case "ArrowRight": case KEY.RIGHT_ARROW:
case "d": case KEY.D:
return "→"; return rightArrowSymbol;
}
return "";
}
export function getInverseArrow(event: KeyboardEvent): string {
switch (event.key) {
case KEY.DOWN_ARROW:
case KEY.S:
return upArrowSymbol;
case KEY.RIGHT_ARROW:
case KEY.D:
return leftArrowSymbol;
case KEY.UP_ARROW:
case KEY.W:
return downArrowSymbol;
case KEY.LEFT_ARROW:
case KEY.A:
return rightArrowSymbol;
} }
return ""; return "";
} }

@ -262,7 +262,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
} }
// Select previous command. // Select previous command.
if (event.key === KEY.UPARROW || (Settings.EnableBashHotkeys && event.key === "p" && event.ctrlKey)) { if (event.key === KEY.UP_ARROW || (Settings.EnableBashHotkeys && event.key === KEY.P && event.ctrlKey)) {
if (Settings.EnableBashHotkeys) { if (Settings.EnableBashHotkeys) {
event.preventDefault(); event.preventDefault();
} }
@ -290,7 +290,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
} }
// Select next command // Select next command
if (event.key === KEY.DOWNARROW || (Settings.EnableBashHotkeys && event.key === "m" && event.ctrlKey)) { if (event.key === KEY.DOWN_ARROW || (Settings.EnableBashHotkeys && event.key === KEY.M && event.ctrlKey)) {
if (Settings.EnableBashHotkeys) { if (Settings.EnableBashHotkeys) {
event.preventDefault(); event.preventDefault();
} }

@ -8,10 +8,27 @@ export enum KEY {
ENTER = "Enter", ENTER = "Enter",
ESC = "Escape", ESC = "Escape",
TAB = "Tab", TAB = "Tab",
UPARROW = "ArrowUp", SPACE = " ",
DOWNARROW = "ArrowDown", BACKSPACE = "Backspace",
LEFTARROW = "ArrowLeft", UP_ARROW = "ArrowUp",
RIGHTARROW = "ArrowRight", DOWN_ARROW = "ArrowDown",
LEFT_ARROW = "ArrowLeft",
RIGHT_ARROW = "ArrowRight",
OPEN_BRACKET = "[",
CLOSE_BRACKET = "]",
LESS_THAN = "<",
GREATER_THAN = ">",
OPEN_PARENTHESIS = "(",
CLOSE_PARENTHESIS = ")",
OPEN_BRACE = "{",
CLOSE_BRACE = "}",
PIPE = "|",
DOT = ".",
FORWARD_SLASH = "/",
HYPHEN = "-",
HASH = "#",
k0 = "0", k0 = "0",
k1 = "1", k1 = "1",