mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-26 11:10:58 +02:00
prettify, sorry for the big ass commit
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -8,100 +8,304 @@ import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 16000, min: 3, max: 4},
|
||||
Normal: {timer: 12500, min: 2, max: 3},
|
||||
Hard: {timer: 15000, min: 3, max: 4},
|
||||
Impossible: {timer: 8000, min: 4, max: 4},
|
||||
}
|
||||
Trivial: { timer: 16000, min: 3, max: 4 },
|
||||
Normal: { timer: 12500, min: 2, max: 3 },
|
||||
Hard: { timer: 15000, min: 3, max: 4 },
|
||||
Impossible: { timer: 8000, min: 4, max: 4 },
|
||||
};
|
||||
|
||||
export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [answer] = useState(makeAnswer(difficulty));
|
||||
const [guess, setGuess] = useState("");
|
||||
const difficulty: Difficulty = { timer: 0, min: 0, max: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [answer] = useState(makeAnswer(difficulty));
|
||||
const [guess, setGuess] = useState("");
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(event.keyCode === 16) return;
|
||||
const nextGuess = guess + event.key.toUpperCase();
|
||||
if(!answer.startsWith(nextGuess)) props.onFailure();
|
||||
else if (answer === nextGuess) props.onSuccess();
|
||||
else setGuess(nextGuess);
|
||||
}
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if (event.keyCode === 16) return;
|
||||
const nextGuess = guess + event.key.toUpperCase();
|
||||
if (!answer.startsWith(nextGuess)) props.onFailure();
|
||||
else if (answer === nextGuess) props.onSuccess();
|
||||
else setGuess(nextGuess);
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Type it backward</h1>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p>{guess}<BlinkingCursor /></p>
|
||||
</Grid>
|
||||
</Grid>)
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Type it backward</h1>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p style={{ transform: "scaleX(-1)" }}>{answer}</p>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p>
|
||||
{guess}
|
||||
<BlinkingCursor />
|
||||
</p>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function makeAnswer(difficulty: Difficulty): string {
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
let answer = "";
|
||||
for(let i = 0; i < length; i++) {
|
||||
if(i > 0) answer += " "
|
||||
answer += words[Math.floor(Math.random() * words.length)];
|
||||
}
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
let answer = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (i > 0) answer += " ";
|
||||
answer += words[Math.floor(Math.random() * words.length)];
|
||||
}
|
||||
|
||||
return answer;
|
||||
return answer;
|
||||
}
|
||||
|
||||
const words = ["ALGORITHM", "ANALOG", "APP", "APPLICATION", "ARRAY", "BACKUP",
|
||||
"BANDWIDTH", "BINARY", "BIT", "BITE", "BITMAP", "BLOG", "BLOGGER",
|
||||
"BOOKMARK", "BOOT", "BROADBAND", "BROWSER", "BUFFER", "BUG", "BUS", "BYTE",
|
||||
"CACHE", "CAPS LOCK", "CAPTCHA", "CD", "CD-ROM", "CLIENT",
|
||||
"CLIPBOARD", "CLOUD", "COMPUTING", "COMMAND", "COMPILE", "COMPRESS",
|
||||
"COMPUTER", "CONFIGURE", "COOKIE", "COPY", "CPU",
|
||||
"CYBERCRIME", "CYBERSPACE", "DASHBOARD", "DATA", "MINING", "DATABASE",
|
||||
"DEBUG", "DECOMPRESS", "DELETE", "DESKTOP", "DEVELOPMENT", "DIGITAL",
|
||||
"DISK", "DNS", "DOCUMENT", "DOMAIN", "DOMAIN NAME", "DOT", "DOT MATRIX",
|
||||
"DOWNLOAD", "DRAG", "DVD", "DYNAMIC", "EMAIL", "EMOTICON", "ENCRYPT",
|
||||
"ENCRYPTION", "ENTER", "EXABYTE", "FAQ", "FILE", "FINDER", "FIREWALL",
|
||||
"FIRMWARE", "FLAMING", "FLASH", "FLASH DRIVE", "FLOPPY DISK", "FLOWCHART",
|
||||
"FOLDER", "FONT", "FORMAT", "FRAME", "FREEWARE", "GIGABYTE", "GRAPHICS",
|
||||
"HACK", "HACKER", "HARDWARE", "HOME PAGE", "HOST", "HTML", "HYPERLINK",
|
||||
"HYPERTEXT", "ICON", "INBOX", "INTEGER", "INTERFACE", "INTERNET",
|
||||
"IP ADDRESS", "ITERATION", "JAVA", "JOYSTICK", "JUNKMAIL", "KERNEL",
|
||||
"KEY", "KEYBOARD", "KEYWORD", "LAPTOP", "LASER PRINTER", "LINK", "LINUX",
|
||||
"LOG OUT", "LOGIC", "LOGIN", "LURKING", "MACINTOSH", "MACRO", "MAINFRAME",
|
||||
"MALWARE", "MEDIA", "MEMORY", "MIRROR", "MODEM", "MONITOR", "MOTHERBOARD",
|
||||
"MOUSE", "MULTIMEDIA", "NET", "NETWORK", "NODE", "NOTEBOOK", "COMPUTER",
|
||||
"OFFLINE", "ONLINE", "OPENSOURCE", "OPERATING", "SYSTEM", "OPTION", "OUTPUT",
|
||||
"PAGE", "PASSWORD", "PASTE", "PATH", "PHISHING", "PIRACY", "PIRATE",
|
||||
"PLATFORM", "PLUGIN", "PODCAST", "POPUP", "PORTAL", "PRINT", "PRINTER",
|
||||
"PRIVACY", "PROCESS", "PROGRAM", "PROGRAMMER", "PROTOCOL", "QUEUE",
|
||||
"QWERTY", "RAM", "REALTIME", "REBOOT", "RESOLUTION", "RESTORE", "ROM",
|
||||
"ROOT", "ROUTER", "RUNTIME", "SAVE", "SCAN", "SCANNER", "SCREEN",
|
||||
"SCREENSHOT", "SCRIPT", "SCROLL", "SCROLL", "SEARCH", "ENGINE",
|
||||
"SECURITY", "SERVER", "SHAREWARE", "SHELL", "SHIFT", "SHIFT KEY",
|
||||
"SNAPSHOT", "SOCIAL NETWORKING", "SOFTWARE", "SPAM", "SPAMMER",
|
||||
"SPREADSHEET", "SPYWARE", "STATUS", "STORAGE", "SUPERCOMPUTER", "SURF",
|
||||
"SYNTAX", "TABLE", "TAG", "TERMINAL", "TEMPLATE", "TERABYTE", "TEXT EDITOR",
|
||||
"THREAD", "TOOLBAR", "TRASH", "TROJAN HORSE", "TYPEFACE", "UNDO", "UNIX",
|
||||
"UPLOAD", "URL", "USER", "USER INTERFACE", "USERNAME", "UTILITY", "VERSION",
|
||||
"VIRTUAL", "VIRTUAL MEMORY", "VIRUS", "WEB", "WEBMASTER",
|
||||
"WEBSITE", "WIDGET", "WIKI", "WINDOW", "WINDOWS", "WIRELESS",
|
||||
"PROCESSOR", "WORKSTATION", "WEB", "WORM", "WWW", "XML",
|
||||
"ZIP"];
|
||||
const words = [
|
||||
"ALGORITHM",
|
||||
"ANALOG",
|
||||
"APP",
|
||||
"APPLICATION",
|
||||
"ARRAY",
|
||||
"BACKUP",
|
||||
"BANDWIDTH",
|
||||
"BINARY",
|
||||
"BIT",
|
||||
"BITE",
|
||||
"BITMAP",
|
||||
"BLOG",
|
||||
"BLOGGER",
|
||||
"BOOKMARK",
|
||||
"BOOT",
|
||||
"BROADBAND",
|
||||
"BROWSER",
|
||||
"BUFFER",
|
||||
"BUG",
|
||||
"BUS",
|
||||
"BYTE",
|
||||
"CACHE",
|
||||
"CAPS LOCK",
|
||||
"CAPTCHA",
|
||||
"CD",
|
||||
"CD-ROM",
|
||||
"CLIENT",
|
||||
"CLIPBOARD",
|
||||
"CLOUD",
|
||||
"COMPUTING",
|
||||
"COMMAND",
|
||||
"COMPILE",
|
||||
"COMPRESS",
|
||||
"COMPUTER",
|
||||
"CONFIGURE",
|
||||
"COOKIE",
|
||||
"COPY",
|
||||
"CPU",
|
||||
"CYBERCRIME",
|
||||
"CYBERSPACE",
|
||||
"DASHBOARD",
|
||||
"DATA",
|
||||
"MINING",
|
||||
"DATABASE",
|
||||
"DEBUG",
|
||||
"DECOMPRESS",
|
||||
"DELETE",
|
||||
"DESKTOP",
|
||||
"DEVELOPMENT",
|
||||
"DIGITAL",
|
||||
"DISK",
|
||||
"DNS",
|
||||
"DOCUMENT",
|
||||
"DOMAIN",
|
||||
"DOMAIN NAME",
|
||||
"DOT",
|
||||
"DOT MATRIX",
|
||||
"DOWNLOAD",
|
||||
"DRAG",
|
||||
"DVD",
|
||||
"DYNAMIC",
|
||||
"EMAIL",
|
||||
"EMOTICON",
|
||||
"ENCRYPT",
|
||||
"ENCRYPTION",
|
||||
"ENTER",
|
||||
"EXABYTE",
|
||||
"FAQ",
|
||||
"FILE",
|
||||
"FINDER",
|
||||
"FIREWALL",
|
||||
"FIRMWARE",
|
||||
"FLAMING",
|
||||
"FLASH",
|
||||
"FLASH DRIVE",
|
||||
"FLOPPY DISK",
|
||||
"FLOWCHART",
|
||||
"FOLDER",
|
||||
"FONT",
|
||||
"FORMAT",
|
||||
"FRAME",
|
||||
"FREEWARE",
|
||||
"GIGABYTE",
|
||||
"GRAPHICS",
|
||||
"HACK",
|
||||
"HACKER",
|
||||
"HARDWARE",
|
||||
"HOME PAGE",
|
||||
"HOST",
|
||||
"HTML",
|
||||
"HYPERLINK",
|
||||
"HYPERTEXT",
|
||||
"ICON",
|
||||
"INBOX",
|
||||
"INTEGER",
|
||||
"INTERFACE",
|
||||
"INTERNET",
|
||||
"IP ADDRESS",
|
||||
"ITERATION",
|
||||
"JAVA",
|
||||
"JOYSTICK",
|
||||
"JUNKMAIL",
|
||||
"KERNEL",
|
||||
"KEY",
|
||||
"KEYBOARD",
|
||||
"KEYWORD",
|
||||
"LAPTOP",
|
||||
"LASER PRINTER",
|
||||
"LINK",
|
||||
"LINUX",
|
||||
"LOG OUT",
|
||||
"LOGIC",
|
||||
"LOGIN",
|
||||
"LURKING",
|
||||
"MACINTOSH",
|
||||
"MACRO",
|
||||
"MAINFRAME",
|
||||
"MALWARE",
|
||||
"MEDIA",
|
||||
"MEMORY",
|
||||
"MIRROR",
|
||||
"MODEM",
|
||||
"MONITOR",
|
||||
"MOTHERBOARD",
|
||||
"MOUSE",
|
||||
"MULTIMEDIA",
|
||||
"NET",
|
||||
"NETWORK",
|
||||
"NODE",
|
||||
"NOTEBOOK",
|
||||
"COMPUTER",
|
||||
"OFFLINE",
|
||||
"ONLINE",
|
||||
"OPENSOURCE",
|
||||
"OPERATING",
|
||||
"SYSTEM",
|
||||
"OPTION",
|
||||
"OUTPUT",
|
||||
"PAGE",
|
||||
"PASSWORD",
|
||||
"PASTE",
|
||||
"PATH",
|
||||
"PHISHING",
|
||||
"PIRACY",
|
||||
"PIRATE",
|
||||
"PLATFORM",
|
||||
"PLUGIN",
|
||||
"PODCAST",
|
||||
"POPUP",
|
||||
"PORTAL",
|
||||
"PRINT",
|
||||
"PRINTER",
|
||||
"PRIVACY",
|
||||
"PROCESS",
|
||||
"PROGRAM",
|
||||
"PROGRAMMER",
|
||||
"PROTOCOL",
|
||||
"QUEUE",
|
||||
"QWERTY",
|
||||
"RAM",
|
||||
"REALTIME",
|
||||
"REBOOT",
|
||||
"RESOLUTION",
|
||||
"RESTORE",
|
||||
"ROM",
|
||||
"ROOT",
|
||||
"ROUTER",
|
||||
"RUNTIME",
|
||||
"SAVE",
|
||||
"SCAN",
|
||||
"SCANNER",
|
||||
"SCREEN",
|
||||
"SCREENSHOT",
|
||||
"SCRIPT",
|
||||
"SCROLL",
|
||||
"SCROLL",
|
||||
"SEARCH",
|
||||
"ENGINE",
|
||||
"SECURITY",
|
||||
"SERVER",
|
||||
"SHAREWARE",
|
||||
"SHELL",
|
||||
"SHIFT",
|
||||
"SHIFT KEY",
|
||||
"SNAPSHOT",
|
||||
"SOCIAL NETWORKING",
|
||||
"SOFTWARE",
|
||||
"SPAM",
|
||||
"SPAMMER",
|
||||
"SPREADSHEET",
|
||||
"SPYWARE",
|
||||
"STATUS",
|
||||
"STORAGE",
|
||||
"SUPERCOMPUTER",
|
||||
"SURF",
|
||||
"SYNTAX",
|
||||
"TABLE",
|
||||
"TAG",
|
||||
"TERMINAL",
|
||||
"TEMPLATE",
|
||||
"TERABYTE",
|
||||
"TEXT EDITOR",
|
||||
"THREAD",
|
||||
"TOOLBAR",
|
||||
"TRASH",
|
||||
"TROJAN HORSE",
|
||||
"TYPEFACE",
|
||||
"UNDO",
|
||||
"UNIX",
|
||||
"UPLOAD",
|
||||
"URL",
|
||||
"USER",
|
||||
"USER INTERFACE",
|
||||
"USERNAME",
|
||||
"UTILITY",
|
||||
"VERSION",
|
||||
"VIRTUAL",
|
||||
"VIRTUAL MEMORY",
|
||||
"VIRUS",
|
||||
"WEB",
|
||||
"WEBMASTER",
|
||||
"WEBSITE",
|
||||
"WIDGET",
|
||||
"WIKI",
|
||||
"WINDOW",
|
||||
"WINDOWS",
|
||||
"WIRELESS",
|
||||
"PROCESSOR",
|
||||
"WORKSTATION",
|
||||
"WEB",
|
||||
"WORM",
|
||||
"WWW",
|
||||
"XML",
|
||||
"ZIP",
|
||||
];
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
export function BlinkingCursor(): React.ReactElement {
|
||||
return (<span style={{fontSize: "1em"}} className={"blinking-cursor"}>|</span>);
|
||||
}
|
||||
return (
|
||||
<span style={{ fontSize: "1em" }} className={"blinking-cursor"}>
|
||||
|
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -8,77 +8,84 @@ import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer:8000, min: 2, max: 3},
|
||||
Normal: {timer:6000, min: 4, max: 5},
|
||||
Hard: {timer:4000, min: 4, max: 6},
|
||||
Impossible: {timer: 2500, min: 7, max: 7},
|
||||
}
|
||||
Trivial: { timer: 8000, min: 2, max: 3 },
|
||||
Normal: { timer: 6000, min: 4, max: 5 },
|
||||
Hard: { timer: 4000, min: 4, max: 6 },
|
||||
Impossible: { timer: 2500, min: 7, max: 7 },
|
||||
};
|
||||
|
||||
function generateLeftSide(difficulty: Difficulty): string {
|
||||
let str = "";
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
for(let i = 0; i < length; i++) {
|
||||
str += ["[", '<', '(', '{'][Math.floor(Math.random()*4)];
|
||||
}
|
||||
let str = "";
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
for (let i = 0; i < length; i++) {
|
||||
str += ["[", "<", "(", "{"][Math.floor(Math.random() * 4)];
|
||||
}
|
||||
|
||||
return str;
|
||||
return str;
|
||||
}
|
||||
|
||||
function getChar(event: React.KeyboardEvent<HTMLElement>): string {
|
||||
if(event.keyCode == 48 && event.shiftKey) return ")";
|
||||
if(event.keyCode == 221 && !event.shiftKey) return "]";
|
||||
if(event.keyCode == 221 && event.shiftKey) return "}";
|
||||
if(event.keyCode == 190 && event.shiftKey) return ">";
|
||||
return "";
|
||||
if (event.keyCode == 48 && event.shiftKey) return ")";
|
||||
if (event.keyCode == 221 && !event.shiftKey) return "]";
|
||||
if (event.keyCode == 221 && event.shiftKey) return "}";
|
||||
if (event.keyCode == 190 && event.shiftKey) return ">";
|
||||
return "";
|
||||
}
|
||||
|
||||
function match(left: string, right: string): boolean {
|
||||
return (left === '[' && right === ']') ||
|
||||
(left === '<' && right === '>') ||
|
||||
(left === '(' && right === ')') ||
|
||||
(left === '{' && right === '}');
|
||||
return (
|
||||
(left === "[" && right === "]") ||
|
||||
(left === "<" && right === ">") ||
|
||||
(left === "(" && right === ")") ||
|
||||
(left === "{" && right === "}")
|
||||
);
|
||||
}
|
||||
|
||||
export function BracketGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer:0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [right, setRight] = useState("");
|
||||
const [left] = useState(generateLeftSide(difficulty));
|
||||
const difficulty: Difficulty = { timer: 0, min: 0, max: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [right, setRight] = useState("");
|
||||
const [left] = useState(generateLeftSide(difficulty));
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const char = getChar(event);
|
||||
if(!char) return;
|
||||
if(!match(left[left.length-right.length-1], char)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
if(left.length === right.length+1) {
|
||||
props.onSuccess();
|
||||
return;
|
||||
}
|
||||
setRight(right+char);
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const char = getChar(event);
|
||||
if (!char) return;
|
||||
if (!match(left[left.length - right.length - 1], char)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
if (left.length === right.length + 1) {
|
||||
props.onSuccess();
|
||||
return;
|
||||
}
|
||||
setRight(right + char);
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Close the brackets</h1>
|
||||
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Close the brackets</h1>
|
||||
<p style={{ fontSize: "5em" }}>
|
||||
{`${left}${right}`}
|
||||
<BlinkingCursor />
|
||||
</p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,96 +1,134 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
size: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 12000, size: 6},
|
||||
Normal: {timer: 9000, size: 8},
|
||||
Hard: {timer: 5000, size: 9},
|
||||
Impossible: {timer: 2500, size: 12},
|
||||
}
|
||||
Trivial: { timer: 12000, size: 6 },
|
||||
Normal: { timer: 9000, size: 8 },
|
||||
Hard: { timer: 5000, size: 9 },
|
||||
Impossible: { timer: 2500, size: 12 },
|
||||
};
|
||||
|
||||
export function BribeGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, size: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [choices] = useState(makeChoices(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
const difficulty: Difficulty = { timer: 0, size: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [choices] = useState(makeChoices(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const k = event.keyCode;
|
||||
if(k === 32) {
|
||||
if(positive.includes(choices[index])) props.onSuccess();
|
||||
else props.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
let newIndex = index;
|
||||
if([38, 87, 68, 39].includes(k)) newIndex++;
|
||||
if([65, 37, 83, 40].includes(k)) newIndex--;
|
||||
while(newIndex < 0) newIndex += choices.length;
|
||||
while(newIndex > choices.length-1) newIndex -= choices.length;
|
||||
setIndex(newIndex);
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const k = event.keyCode;
|
||||
if (k === 32) {
|
||||
if (positive.includes(choices[index])) props.onSuccess();
|
||||
else props.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1>Say something nice about the guard.</h1>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<h2 style={{fontSize: "2em"}}>↑</h2>
|
||||
<h2 style={{fontSize: "2em"}}>{choices[index]}</h2>
|
||||
<h2 style={{fontSize: "2em"}}>↓</h2>
|
||||
</Grid>
|
||||
</Grid>)
|
||||
let newIndex = index;
|
||||
if ([38, 87, 68, 39].includes(k)) newIndex++;
|
||||
if ([65, 37, 83, 40].includes(k)) newIndex--;
|
||||
while (newIndex < 0) newIndex += choices.length;
|
||||
while (newIndex > choices.length - 1) newIndex -= choices.length;
|
||||
setIndex(newIndex);
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1>Say something nice about the guard.</h1>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<h2 style={{ fontSize: "2em" }}>↑</h2>
|
||||
<h2 style={{ fontSize: "2em" }}>{choices[index]}</h2>
|
||||
<h2 style={{ fontSize: "2em" }}>↓</h2>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function shuffleArray(array: string[]): void {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
function makeChoices(difficulty: Difficulty): string[] {
|
||||
const choices = [];
|
||||
choices.push(positive[Math.floor(Math.random()*positive.length)]);
|
||||
for(let i = 0; i < difficulty.size; i++) {
|
||||
const option = negative[Math.floor(Math.random()*negative.length)];
|
||||
if(choices.includes(option)) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
choices.push(option);
|
||||
const choices = [];
|
||||
choices.push(positive[Math.floor(Math.random() * positive.length)]);
|
||||
for (let i = 0; i < difficulty.size; i++) {
|
||||
const option = negative[Math.floor(Math.random() * negative.length)];
|
||||
if (choices.includes(option)) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
shuffleArray(choices);
|
||||
return choices;
|
||||
choices.push(option);
|
||||
}
|
||||
shuffleArray(choices);
|
||||
return choices;
|
||||
}
|
||||
|
||||
const positive = ["affectionate","agreeable","bright","charming","creative",
|
||||
"determined","energetic","friendly","funny","generous","polite","likable",
|
||||
"diplomatic","helpful","giving","kind","hardworking","patient","dynamic",
|
||||
"loyal"];
|
||||
const positive = [
|
||||
"affectionate",
|
||||
"agreeable",
|
||||
"bright",
|
||||
"charming",
|
||||
"creative",
|
||||
"determined",
|
||||
"energetic",
|
||||
"friendly",
|
||||
"funny",
|
||||
"generous",
|
||||
"polite",
|
||||
"likable",
|
||||
"diplomatic",
|
||||
"helpful",
|
||||
"giving",
|
||||
"kind",
|
||||
"hardworking",
|
||||
"patient",
|
||||
"dynamic",
|
||||
"loyal",
|
||||
];
|
||||
|
||||
const negative = ["aggressive","aloof","arrogant","big-headed","boastful",
|
||||
"boring","bossy","careless","clingy","couch potato","cruel","cynical",
|
||||
"grumpy","hot air","know it all","obnoxious","pain in the neck","picky",
|
||||
"tactless","thoughtless"];
|
||||
const negative = [
|
||||
"aggressive",
|
||||
"aloof",
|
||||
"arrogant",
|
||||
"big-headed",
|
||||
"boastful",
|
||||
"boring",
|
||||
"bossy",
|
||||
"careless",
|
||||
"clingy",
|
||||
"couch potato",
|
||||
"cruel",
|
||||
"cynical",
|
||||
"grumpy",
|
||||
"hot air",
|
||||
"know it all",
|
||||
"obnoxious",
|
||||
"pain in the neck",
|
||||
"picky",
|
||||
"tactless",
|
||||
"thoughtless",
|
||||
];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -7,59 +7,62 @@ import { random, getArrow } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 13000, min: 6, max: 8},
|
||||
Normal: {timer: 7000, min: 7, max: 8},
|
||||
Hard: {timer: 5000, min: 8, max: 9},
|
||||
Impossible: {timer: 3000, min: 9, max: 9},
|
||||
}
|
||||
Trivial: { timer: 13000, min: 6, max: 8 },
|
||||
Normal: { timer: 7000, min: 7, max: 8 },
|
||||
Hard: { timer: 5000, min: 8, max: 9 },
|
||||
Impossible: { timer: 3000, min: 9, max: 9 },
|
||||
};
|
||||
|
||||
export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [code] = useState(generateCode(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
const difficulty: Difficulty = { timer: 0, min: 0, max: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [code] = useState(generateCode(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(code[index] !== getArrow(event)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index+1);
|
||||
if(index+1 >= code.length) props.onSuccess();
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if (code[index] !== getArrow(event)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index + 1);
|
||||
if (index + 1 >= code.length) props.onSuccess();
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Enter the Code!</h1>
|
||||
<p style={{fontSize: '5em'}}>{code[index]}</p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Enter the Code!</h1>
|
||||
<p style={{ fontSize: "5em" }}>{code[index]}</p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function generateCode(difficulty: Difficulty): string {
|
||||
const arrows = ['←', '→', '↑', '↓'];
|
||||
let code = '';
|
||||
for(let i = 0; i < random(difficulty.min, difficulty.max); i++) {
|
||||
let arrow = arrows[Math.floor(4*Math.random())];
|
||||
while(arrow === code[code.length-1]) arrow = arrows[Math.floor(4*Math.random())];
|
||||
code += arrow;
|
||||
}
|
||||
const arrows = ["←", "→", "↑", "↓"];
|
||||
let code = "";
|
||||
for (let i = 0; i < random(difficulty.min, difficulty.max); i++) {
|
||||
let arrow = arrows[Math.floor(4 * Math.random())];
|
||||
while (arrow === code[code.length - 1])
|
||||
arrow = arrows[Math.floor(4 * Math.random())];
|
||||
code += arrow;
|
||||
}
|
||||
|
||||
return code;
|
||||
return code;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
|
||||
interface IProps {
|
||||
onFinish: () => void;
|
||||
onFinish: () => void;
|
||||
}
|
||||
|
||||
export function Countdown(props: IProps): React.ReactElement {
|
||||
const [x, setX] = useState(3);
|
||||
useEffect(() => {
|
||||
if(x === 0) {
|
||||
props.onFinish();
|
||||
return;
|
||||
}
|
||||
setTimeout(()=>setX(x-1), 200);
|
||||
});
|
||||
const [x, setX] = useState(3);
|
||||
useEffect(() => {
|
||||
if (x === 0) {
|
||||
props.onFinish();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => setX(x - 1), 200);
|
||||
});
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<h1>Get Ready!</h1>
|
||||
<h1>{x}</h1>
|
||||
</Grid>
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<h1>Get Ready!</h1>
|
||||
<h1>{x}</h1>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -7,110 +7,145 @@ import { interpolate } from "./Difficulty";
|
||||
import { getArrow } from "../utils";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
symbols: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
symbols: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 12500, width: 3, height: 3, symbols: 6},
|
||||
Normal: {timer: 15000, width: 4, height: 4, symbols: 7},
|
||||
Hard: {timer: 12500, width: 5, height: 5, symbols: 8},
|
||||
Impossible: {timer: 10000, width: 6, height: 6, symbols: 9},
|
||||
}
|
||||
Trivial: { timer: 12500, width: 3, height: 3, symbols: 6 },
|
||||
Normal: { timer: 15000, width: 4, height: 4, symbols: 7 },
|
||||
Hard: { timer: 12500, width: 5, height: 5, symbols: 8 },
|
||||
Impossible: { timer: 10000, width: 6, height: 6, symbols: 9 },
|
||||
};
|
||||
|
||||
export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, width: 0, height: 0, symbols: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [grid] = useState(generatePuzzle(difficulty));
|
||||
const [answer] = useState(generateAnswer(grid, difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
const difficulty: Difficulty = { timer: 0, width: 0, height: 0, symbols: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [grid] = useState(generatePuzzle(difficulty));
|
||||
const [answer] = useState(generateAnswer(grid, difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch(arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0]+move[0], pos[1]+move[1]];
|
||||
next[0] = (next[0]+grid[0].length)%grid[0].length;
|
||||
next[1] = (next[1]+grid.length)%grid.length;
|
||||
setPos(next);
|
||||
|
||||
if(event.keyCode == 32) {
|
||||
const selected = grid[pos[1]][pos[0]];
|
||||
const expected = answer[index];
|
||||
if(selected !== expected) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index+1);
|
||||
if(answer.length === index+1) props.onSuccess();
|
||||
}
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch (arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0] + move[0], pos[1] + move[1]];
|
||||
next[0] = (next[0] + grid[0].length) % grid[0].length;
|
||||
next[1] = (next[1] + grid.length) % grid.length;
|
||||
setPos(next);
|
||||
|
||||
const fontSize = "2em";
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Match the symbols!</h1>
|
||||
<h2 style={{fontSize: fontSize}}>Targets: {answer.map((a, i) => {
|
||||
if(i == index)
|
||||
return <span key={`${i}`} style={{fontSize: "1em", color: 'blue'}}>{a} </span>
|
||||
return <span key={`${i}`} style={{fontSize: "1em"}}>{a} </span>
|
||||
})}</h2>
|
||||
if (event.keyCode == 32) {
|
||||
const selected = grid[pos[1]][pos[0]];
|
||||
const expected = answer[index];
|
||||
if (selected !== expected) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index + 1);
|
||||
if (answer.length === index + 1) props.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
const fontSize = "2em";
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Match the symbols!</h1>
|
||||
<h2 style={{ fontSize: fontSize }}>
|
||||
Targets:{" "}
|
||||
{answer.map((a, i) => {
|
||||
if (i == index)
|
||||
return (
|
||||
<span key={`${i}`} style={{ fontSize: "1em", color: "blue" }}>
|
||||
{a}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span key={`${i}`} style={{ fontSize: "1em" }}>
|
||||
{a}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</h2>
|
||||
<br />
|
||||
{grid.map((line, y) => (
|
||||
<div key={y}>
|
||||
<pre>
|
||||
{line.map((cell, x) => {
|
||||
if (x == pos[0] && y == pos[1])
|
||||
return (
|
||||
<span
|
||||
key={`${x}${y}`}
|
||||
style={{ fontSize: fontSize, color: "blue" }}
|
||||
>
|
||||
{cell}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span key={`${x}${y}`} style={{ fontSize: fontSize }}>
|
||||
{cell}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</pre>
|
||||
<br />
|
||||
{grid.map((line, y) => <div key={y}><pre>{line.map((cell, x) => {
|
||||
if(x == pos[0] && y == pos[1])
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell} </span>
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell} </span>
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
</div>
|
||||
))}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function generateAnswer(grid: string[][], difficulty: Difficulty): string[] {
|
||||
const answer = [];
|
||||
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)]);
|
||||
}
|
||||
return answer;
|
||||
const answer = [];
|
||||
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)
|
||||
],
|
||||
);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
function randChar(): string {
|
||||
return "ABCDEF0123456789"[Math.floor(Math.random()*16)];
|
||||
return "ABCDEF0123456789"[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
|
||||
function generatePuzzle(difficulty: Difficulty): string[][] {
|
||||
const puzzle = [];
|
||||
for(let i = 0; i < Math.round(difficulty.height); i++) {
|
||||
const line = [];
|
||||
for(let j = 0; j < Math.round(difficulty.width); j++) {
|
||||
line.push(randChar()+randChar());
|
||||
}
|
||||
puzzle.push(line);
|
||||
const puzzle = [];
|
||||
for (let i = 0; i < Math.round(difficulty.height); i++) {
|
||||
const line = [];
|
||||
for (let j = 0; j < Math.round(difficulty.width); j++) {
|
||||
line.push(randChar() + randChar());
|
||||
}
|
||||
return puzzle;
|
||||
puzzle.push(line);
|
||||
}
|
||||
return puzzle;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
interface DifficultySetting {
|
||||
[key: string]: number;
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
interface DifficultySettings {
|
||||
Trivial: DifficultySetting;
|
||||
Normal: DifficultySetting;
|
||||
Hard: DifficultySetting;
|
||||
Impossible: DifficultySetting;
|
||||
Trivial: DifficultySetting;
|
||||
Normal: DifficultySetting;
|
||||
Hard: DifficultySetting;
|
||||
Impossible: DifficultySetting;
|
||||
}
|
||||
|
||||
// I could use `any` to simply some of this but I also want to take advantage
|
||||
// of the type safety that typescript provides. I'm just not sure how in this
|
||||
// case.
|
||||
export function interpolate(settings: DifficultySettings, n: number, out: DifficultySetting): DifficultySetting {
|
||||
// interpolates between 2 difficulties.
|
||||
function lerpD(a: DifficultySetting, b: DifficultySetting, t: number): DifficultySetting {
|
||||
// interpolates between 2 numbers.
|
||||
function lerp(x: number, y: number, t: number): number {
|
||||
return (1 - t) * x + t * y;
|
||||
}
|
||||
for(const key of Object.keys(a)) {
|
||||
out[key] = lerp(a[key], b[key], t);
|
||||
}
|
||||
return a;
|
||||
export function interpolate(
|
||||
settings: DifficultySettings,
|
||||
n: number,
|
||||
out: DifficultySetting,
|
||||
): DifficultySetting {
|
||||
// interpolates between 2 difficulties.
|
||||
function lerpD(
|
||||
a: DifficultySetting,
|
||||
b: DifficultySetting,
|
||||
t: number,
|
||||
): DifficultySetting {
|
||||
// interpolates between 2 numbers.
|
||||
function lerp(x: number, y: number, t: number): number {
|
||||
return (1 - t) * x + t * y;
|
||||
}
|
||||
if(n < 0) return lerpD(settings.Trivial, settings.Trivial, 0);
|
||||
if(n >= 0 && n < 1) return lerpD(settings.Trivial, settings.Normal, n);
|
||||
if(n >= 1 && n < 2) return lerpD(settings.Normal, settings.Hard, n-1);
|
||||
if(n >= 2 && n < 3) return lerpD(settings.Hard, settings.Impossible, n-2);
|
||||
return lerpD(settings.Impossible, settings.Impossible, 0);
|
||||
}
|
||||
for (const key of Object.keys(a)) {
|
||||
out[key] = lerp(a[key], b[key], t);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
if (n < 0) return lerpD(settings.Trivial, settings.Trivial, 0);
|
||||
if (n >= 0 && n < 1) return lerpD(settings.Trivial, settings.Normal, n);
|
||||
if (n >= 1 && n < 2) return lerpD(settings.Normal, settings.Hard, n - 1);
|
||||
if (n >= 2 && n < 3) return lerpD(settings.Hard, settings.Impossible, n - 2);
|
||||
return lerpD(settings.Impossible, settings.Impossible, 0);
|
||||
}
|
||||
|
||||
+124
-99
@@ -1,7 +1,7 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Countdown } from "./Countdown";
|
||||
import { BracketGame } from "./BracketGame";
|
||||
import { SlashGame } from "./SlashGame";
|
||||
@@ -14,122 +14,147 @@ import { WireCuttingGame } from "./WireCuttingGame";
|
||||
import { Victory } from "./Victory";
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
enum Stage {
|
||||
Countdown = 0,
|
||||
Minigame,
|
||||
Result,
|
||||
Sell,
|
||||
Countdown = 0,
|
||||
Minigame,
|
||||
Result,
|
||||
Sell,
|
||||
}
|
||||
|
||||
const minigames = [
|
||||
SlashGame,
|
||||
BracketGame,
|
||||
BackwardGame,
|
||||
BribeGame,
|
||||
CheatCodeGame,
|
||||
Cyberpunk2077Game,
|
||||
MinesweeperGame,
|
||||
WireCuttingGame,
|
||||
]
|
||||
SlashGame,
|
||||
BracketGame,
|
||||
BackwardGame,
|
||||
BribeGame,
|
||||
CheatCodeGame,
|
||||
Cyberpunk2077Game,
|
||||
MinesweeperGame,
|
||||
WireCuttingGame,
|
||||
];
|
||||
|
||||
export function Game(props: IProps): React.ReactElement {
|
||||
const [level, setLevel] = useState(1);
|
||||
const [stage, setStage] = useState(Stage.Countdown);
|
||||
const [results, setResults] = useState('');
|
||||
const [gameIds, setGameIds] = useState({
|
||||
lastGames: [-1, -1],
|
||||
id: Math.floor(Math.random()*minigames.length),
|
||||
const [level, setLevel] = useState(1);
|
||||
const [stage, setStage] = useState(Stage.Countdown);
|
||||
const [results, setResults] = useState("");
|
||||
const [gameIds, setGameIds] = useState({
|
||||
lastGames: [-1, -1],
|
||||
id: Math.floor(Math.random() * minigames.length),
|
||||
});
|
||||
|
||||
function nextGameId(): number {
|
||||
let id = gameIds.lastGames[0];
|
||||
const ids = [gameIds.lastGames[0], gameIds.lastGames[1], gameIds.id];
|
||||
while (ids.includes(id)) {
|
||||
id = Math.floor(Math.random() * minigames.length);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function setupNextGame(): void {
|
||||
setGameIds({
|
||||
lastGames: [gameIds.lastGames[1], gameIds.id],
|
||||
id: nextGameId(),
|
||||
});
|
||||
}
|
||||
|
||||
function nextGameId(): number {
|
||||
let id = gameIds.lastGames[0];
|
||||
const ids = [gameIds.lastGames[0], gameIds.lastGames[1], gameIds.id];
|
||||
while(ids.includes(id)) {
|
||||
id = Math.floor(Math.random()*minigames.length);
|
||||
}
|
||||
return id;
|
||||
function success(): void {
|
||||
pushResult(true);
|
||||
if (level === props.MaxLevel) {
|
||||
setStage(Stage.Sell);
|
||||
} else {
|
||||
setStage(Stage.Countdown);
|
||||
setLevel(level + 1);
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
function pushResult(win: boolean): void {
|
||||
setResults((old) => {
|
||||
let next = old;
|
||||
next += win ? "✓" : "✗";
|
||||
if (next.length > 15) next = next.slice(1);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
function setupNextGame(): void {
|
||||
setGameIds({
|
||||
lastGames: [gameIds.lastGames[1], gameIds.id],
|
||||
id: nextGameId(),
|
||||
})
|
||||
function failure(options?: { automated: boolean }): void {
|
||||
setStage(Stage.Countdown);
|
||||
pushResult(false);
|
||||
// Kill the player immediately if they use automation, so
|
||||
// it's clear they're not meant to
|
||||
const damage = options?.automated
|
||||
? props.Player.hp
|
||||
: props.StartingDifficulty * 3;
|
||||
if (props.Player.takeDamage(damage)) {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if (menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
function success(): void {
|
||||
pushResult(true);
|
||||
if(level === props.MaxLevel) {
|
||||
setStage(Stage.Sell);
|
||||
} else {
|
||||
setStage(Stage.Countdown);
|
||||
setLevel(level+1);
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
function pushResult(win: boolean): void {
|
||||
setResults(old => {
|
||||
let next = old;
|
||||
next += win ? "✓" : "✗";
|
||||
if(next.length > 15) next = next.slice(1);
|
||||
return next;
|
||||
})
|
||||
}
|
||||
|
||||
function failure(options?: { automated: boolean }): void {
|
||||
setStage(Stage.Countdown);
|
||||
pushResult(false);
|
||||
// Kill the player immediately if they use automation, so
|
||||
// it's clear they're not meant to
|
||||
const damage = options?.automated ? props.Player.hp : props.StartingDifficulty*3;
|
||||
if(props.Player.takeDamage(damage)) {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
let stageComponent: React.ReactNode;
|
||||
switch(stage) {
|
||||
let stageComponent: React.ReactNode;
|
||||
switch (stage) {
|
||||
case Stage.Countdown:
|
||||
stageComponent = (<Countdown onFinish={() =>setStage(Stage.Minigame)} />);
|
||||
break;
|
||||
stageComponent = <Countdown onFinish={() => setStage(Stage.Minigame)} />;
|
||||
break;
|
||||
case Stage.Minigame: {
|
||||
const MiniGame = minigames[gameIds.id];
|
||||
stageComponent = (<MiniGame onSuccess={success} onFailure={failure} difficulty={props.Difficulty+level/50} />);
|
||||
break;
|
||||
const MiniGame = minigames[gameIds.id];
|
||||
stageComponent = (
|
||||
<MiniGame
|
||||
onSuccess={success}
|
||||
onFailure={failure}
|
||||
difficulty={props.Difficulty + level / 50}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Stage.Sell:
|
||||
stageComponent = (<Victory Player={props.Player} Engine={props.Engine} StartingDifficulty={props.StartingDifficulty} Difficulty={props.Difficulty} MaxLevel={props.MaxLevel} />);
|
||||
break;
|
||||
}
|
||||
stageComponent = (
|
||||
<Victory
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
StartingDifficulty={props.StartingDifficulty}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
function Progress(): React.ReactElement {
|
||||
return (
|
||||
<h4>
|
||||
<span style={{ color: "gray" }}>
|
||||
{results.slice(0, results.length - 1)}
|
||||
</span>
|
||||
{results[results.length - 1]}
|
||||
</h4>
|
||||
);
|
||||
}
|
||||
|
||||
function Progress(): React.ReactElement {
|
||||
return <h4><span style={{color: "gray"}}>{results.slice(0, results.length-1)}</span>{results[results.length-1]}</h4>
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<h3>Level: {level} / {props.MaxLevel}</h3>
|
||||
<Progress />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
{stageComponent}
|
||||
</Grid>
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<h3>
|
||||
Level: {level} / {props.MaxLevel}
|
||||
</h3>
|
||||
<Progress />
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
||||
|
||||
<Grid item xs={12}>
|
||||
{stageComponent}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import LinearProgress from "@material-ui/core/LinearProgress";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
|
||||
const TimerProgress = withStyles(() => ({
|
||||
bar: {
|
||||
@@ -11,31 +11,32 @@ const TimerProgress = withStyles(() => ({
|
||||
}))(LinearProgress);
|
||||
|
||||
interface IProps {
|
||||
millis: number;
|
||||
onExpire: () => void;
|
||||
millis: number;
|
||||
onExpire: () => void;
|
||||
}
|
||||
|
||||
export function GameTimer(props: IProps): React.ReactElement {
|
||||
const [v, setV] = useState(100);
|
||||
const [v, setV] = useState(100);
|
||||
|
||||
const tick = 200;
|
||||
useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
setV(old => {
|
||||
if(old <= 0) props.onExpire();
|
||||
return old - tick / props.millis * 100;
|
||||
});
|
||||
}, tick);
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, []);
|
||||
const tick = 200;
|
||||
useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
setV((old) => {
|
||||
if (old <= 0) props.onExpire();
|
||||
return old - (tick / props.millis) * 100;
|
||||
});
|
||||
}, tick);
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||
// bar physically reaches the end
|
||||
return (<Grid item xs={12}>
|
||||
<TimerProgress variant="determinate" value={v} />
|
||||
</Grid>);
|
||||
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||
// bar physically reaches the end
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<TimerProgress variant="determinate" value={v} />
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export interface IMinigameProps {
|
||||
onSuccess: () => void;
|
||||
onFailure: (options?: {
|
||||
/** Failed due to using untrusted events (automation) */
|
||||
automated: boolean;
|
||||
}) => void;
|
||||
difficulty: number;
|
||||
}
|
||||
onSuccess: () => void;
|
||||
onFailure: (options?: {
|
||||
/** Failed due to using untrusted events (automation) */
|
||||
automated: boolean;
|
||||
}) => void;
|
||||
difficulty: number;
|
||||
}
|
||||
|
||||
@@ -1,76 +1,97 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
start: () => void;
|
||||
cancel: () => void;
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
start: () => void;
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
function arrowPart(color: string, length: number): JSX.Element {
|
||||
let arrow = "";
|
||||
if(length <= 0) length = 0;
|
||||
else if(length > 13) length = 13;
|
||||
else {
|
||||
length--;
|
||||
arrow = ">";
|
||||
}
|
||||
return <span style={{color: color}}>{"=".repeat(length)}{arrow}{" ".repeat(13-arrow.length-length)}</span>
|
||||
let arrow = "";
|
||||
if (length <= 0) length = 0;
|
||||
else if (length > 13) length = 13;
|
||||
else {
|
||||
length--;
|
||||
arrow = ">";
|
||||
}
|
||||
return (
|
||||
<span style={{ color: color }}>
|
||||
{"=".repeat(length)}
|
||||
{arrow}
|
||||
{" ".repeat(13 - arrow.length - length)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function coloredArrow(difficulty: number): JSX.Element {
|
||||
if(difficulty === 0) {
|
||||
return <span style={{color: 'white'}}>{'>'}{" ".repeat(38)}</span>
|
||||
} else {
|
||||
return <>{arrowPart('white', difficulty*13)}{arrowPart('orange', (difficulty-1)*13)}{arrowPart('red', (difficulty-2)*13)}</>
|
||||
}
|
||||
if (difficulty === 0) {
|
||||
return (
|
||||
<span style={{ color: "white" }}>
|
||||
{">"}
|
||||
{" ".repeat(38)}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{arrowPart("white", difficulty * 13)}
|
||||
{arrowPart("orange", (difficulty - 1) * 13)}
|
||||
{arrowPart("red", (difficulty - 2) * 13)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function Intro(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltrating {props.Location}</h1>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<h2>Maximum level: {props.MaxLevel}</h2>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
|
||||
<pre>[{coloredArrow(props.Difficulty)}]</pre>
|
||||
<pre> ^ ^ ^ ^</pre>
|
||||
<pre> Trivial Normal Hard Impossible</pre>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<p>Infiltration is a series of short minigames that get
|
||||
progressively harder. You take damage for failing them. Reaching
|
||||
the maximum level rewards you with intel you can trade for money
|
||||
or reputation.</p>
|
||||
<br />
|
||||
<p>The minigames you play are randomly selected. It might take you
|
||||
few tries to get used to them.</p>
|
||||
<br />
|
||||
<p>No game require use of the mouse.</p>
|
||||
<br />
|
||||
<p>Spacebar is the default action/confirm button.</p>
|
||||
<br />
|
||||
<p>Everything that uses arrow can also use WASD</p>
|
||||
<br />
|
||||
<p>Sometimes the rest of the keyboard is used.</p>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.start} text={"Start"} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.cancel} text={"Cancel"} />
|
||||
</Grid>
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltrating {props.Location}</h1>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
||||
<Grid item xs={10}>
|
||||
<h2>Maximum level: {props.MaxLevel}</h2>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<pre>[{coloredArrow(props.Difficulty)}]</pre>
|
||||
<pre> ^ ^ ^ ^</pre>
|
||||
<pre> Trivial Normal Hard Impossible</pre>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<p>
|
||||
Infiltration is a series of short minigames that get progressively
|
||||
harder. You take damage for failing them. Reaching the maximum level
|
||||
rewards you with intel you can trade for money or reputation.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
The minigames you play are randomly selected. It might take you few
|
||||
tries to get used to them.
|
||||
</p>
|
||||
<br />
|
||||
<p>No game require use of the mouse.</p>
|
||||
<br />
|
||||
<p>Spacebar is the default action/confirm button.</p>
|
||||
<br />
|
||||
<p>Everything that uses arrow can also use WASD</p>
|
||||
<br />
|
||||
<p>Sometimes the rest of the keyboard is used.</p>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.start} text={"Start"} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.cancel} text={"Cancel"} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||
onFailure: (options?: { automated: boolean }) => void;
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||
onFailure: (options?: { automated: boolean }) => void;
|
||||
}
|
||||
|
||||
export function KeyHandler(props: IProps): React.ReactElement {
|
||||
let elem: any;
|
||||
useEffect(() => elem.focus());
|
||||
let elem: any;
|
||||
useEffect(() => elem.focus());
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
console.log("isTrusted?", event.isTrusted)
|
||||
if(!event.isTrusted) {
|
||||
console.log("untrusted event!")
|
||||
props.onFailure({ automated: true });
|
||||
return;
|
||||
}
|
||||
props.onKeyDown(event);
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
console.log("isTrusted?", event.isTrusted);
|
||||
if (!event.isTrusted) {
|
||||
console.log("untrusted event!");
|
||||
props.onFailure({ automated: true });
|
||||
return;
|
||||
}
|
||||
props.onKeyDown(event);
|
||||
}
|
||||
|
||||
// invisible autofocused element that eats all the keypress for the minigames.
|
||||
return (<div tabIndex={1} ref={c => elem = c} onKeyDown={onKeyDown} />)
|
||||
}
|
||||
// invisible autofocused element that eats all the keypress for the minigames.
|
||||
return <div tabIndex={1} ref={(c) => (elem = c)} onKeyDown={onKeyDown} />;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -7,123 +7,132 @@ import { interpolate } from "./Difficulty";
|
||||
import { getArrow } from "../utils";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
mines: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
mines: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 15000, width: 3, height: 3, mines: 4},
|
||||
Normal: {timer: 15000, width: 4, height: 4, mines: 7},
|
||||
Hard: {timer: 15000, width: 5, height: 5, mines: 11},
|
||||
Impossible: {timer: 15000, width: 6, height: 6, mines: 15},
|
||||
}
|
||||
Trivial: { timer: 15000, width: 3, height: 3, mines: 4 },
|
||||
Normal: { timer: 15000, width: 4, height: 4, mines: 7 },
|
||||
Hard: { timer: 15000, width: 5, height: 5, mines: 11 },
|
||||
Impossible: { timer: 15000, width: 6, height: 6, mines: 15 },
|
||||
};
|
||||
|
||||
export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, width: 0, height: 0, mines: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [minefield] = useState(generateMinefield(difficulty));
|
||||
const [answer, setAnswer] = useState(generateEmptyField(difficulty));
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
const [memoryPhase, setMemoryPhase] = useState(true);
|
||||
const difficulty: Difficulty = { timer: 0, width: 0, height: 0, mines: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [minefield] = useState(generateMinefield(difficulty));
|
||||
const [answer, setAnswer] = useState(generateEmptyField(difficulty));
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
const [memoryPhase, setMemoryPhase] = useState(true);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(memoryPhase) return;
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch(arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0]+move[0], pos[1]+move[1]];
|
||||
next[0] = (next[0]+minefield[0].length)%minefield[0].length;
|
||||
next[1] = (next[1]+minefield.length)%minefield.length;
|
||||
setPos(next);
|
||||
|
||||
if(event.keyCode == 32) {
|
||||
if(!minefield[pos[1]][pos[0]]) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setAnswer(old => {
|
||||
old[pos[1]][pos[0]] = true;
|
||||
if(fieldEquals(minefield, old)) props.onSuccess();
|
||||
return old;
|
||||
});
|
||||
}
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if (memoryPhase) return;
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch (arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0] + move[0], pos[1] + move[1]];
|
||||
next[0] = (next[0] + minefield[0].length) % minefield[0].length;
|
||||
next[1] = (next[1] + minefield.length) % minefield.length;
|
||||
setPos(next);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setMemoryPhase(false), 2000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
if (event.keyCode == 32) {
|
||||
if (!minefield[pos[1]][pos[0]]) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setAnswer((old) => {
|
||||
old[pos[1]][pos[0]] = true;
|
||||
if (fieldEquals(minefield, old)) props.onSuccess();
|
||||
return old;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>{memoryPhase?"Remember all the mines!": "Mark all the mines!"}</h1>
|
||||
{minefield.map((line, y) => <div key={y}><pre>{line.map((cell, x) => {
|
||||
if(memoryPhase) {
|
||||
if(minefield[y][x])
|
||||
return <span key={x}>[?] </span>
|
||||
return <span key={x}>[ ] </span>
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setMemoryPhase(false), 2000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>
|
||||
{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}
|
||||
</h1>
|
||||
{minefield.map((line, y) => (
|
||||
<div key={y}>
|
||||
<pre>
|
||||
{line.map((cell, x) => {
|
||||
if (memoryPhase) {
|
||||
if (minefield[y][x]) return <span key={x}>[?] </span>;
|
||||
return <span key={x}>[ ] </span>;
|
||||
} else {
|
||||
if(x == pos[0] && y == pos[1])
|
||||
return <span key={x}>[X] </span>
|
||||
if(answer[y][x])
|
||||
return <span key={x}>[.] </span>
|
||||
return <span key={x}>[ ] </span>
|
||||
if (x == pos[0] && y == pos[1])
|
||||
return <span key={x}>[X] </span>;
|
||||
if (answer[y][x]) return <span key={x}>[.] </span>;
|
||||
return <span key={x}>[ ] </span>;
|
||||
}
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
})}
|
||||
</pre>
|
||||
<br />
|
||||
</div>
|
||||
))}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function fieldEquals(a: boolean[][], b: boolean[][]): boolean {
|
||||
function count(field: boolean[][]): number {
|
||||
return field.flat().reduce((a, b) => a + (b?1:0), 0);
|
||||
}
|
||||
return count(a) === count(b);
|
||||
function count(field: boolean[][]): number {
|
||||
return field.flat().reduce((a, b) => a + (b ? 1 : 0), 0);
|
||||
}
|
||||
return count(a) === count(b);
|
||||
}
|
||||
|
||||
function generateEmptyField(difficulty: Difficulty): boolean[][] {
|
||||
const field = [];
|
||||
for(let i = 0; i < difficulty.height; i++) {
|
||||
field.push((new Array(Math.round(difficulty.width))).fill(false));
|
||||
}
|
||||
return field;
|
||||
const field = [];
|
||||
for (let i = 0; i < difficulty.height; i++) {
|
||||
field.push(new Array(Math.round(difficulty.width)).fill(false));
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
function generateMinefield(difficulty: Difficulty): boolean[][] {
|
||||
const field = generateEmptyField(difficulty);
|
||||
for(let i = 0; i < difficulty.mines; i++) {
|
||||
const x = Math.floor(Math.random()*field.length);
|
||||
const y = Math.floor(Math.random()*field[0].length);
|
||||
if (field[x][y]) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
field[x][y] = true;
|
||||
const field = generateEmptyField(difficulty);
|
||||
for (let i = 0; i < difficulty.mines; i++) {
|
||||
const x = Math.floor(Math.random() * field.length);
|
||||
const y = Math.floor(Math.random() * field[0].length);
|
||||
if (field[x][y]) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
return field;
|
||||
field[x][y] = true;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
@@ -1,45 +1,49 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
import { Intro } from "./Intro";
|
||||
import { Game } from "./Game";
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
export function Root(props: IProps): React.ReactElement {
|
||||
const [start, setStart] = useState(false);
|
||||
const [start, setStart] = useState(false);
|
||||
|
||||
function cancel(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
function cancel(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if (menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
|
||||
if(!start) {
|
||||
return (<Intro
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
Location={props.Location}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
start={() => setStart(true)}
|
||||
cancel={cancel}
|
||||
/>)
|
||||
}
|
||||
|
||||
return (<Game
|
||||
if (!start) {
|
||||
return (
|
||||
<Intro
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
StartingDifficulty={props.StartingDifficulty}
|
||||
Location={props.Location}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
/>);
|
||||
}
|
||||
start={() => setStart(true)}
|
||||
cancel={cancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Game
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
StartingDifficulty={props.StartingDifficulty}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,60 +1,64 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
window: number;
|
||||
[key: string]: number;
|
||||
window: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {window: 600},
|
||||
Normal: {window: 325},
|
||||
Hard: {window: 250},
|
||||
Impossible: {window: 150},
|
||||
}
|
||||
Trivial: { window: 600 },
|
||||
Normal: { window: 325 },
|
||||
Hard: { window: 250 },
|
||||
Impossible: { window: 150 },
|
||||
};
|
||||
|
||||
export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {window: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const [guarding, setGuarding] = useState(true);
|
||||
const difficulty: Difficulty = { window: 0 };
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const [guarding, setGuarding] = useState(true);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(event.keyCode !== 32) return;
|
||||
if(guarding) {
|
||||
props.onFailure();
|
||||
} else {
|
||||
props.onSuccess();
|
||||
}
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if (event.keyCode !== 32) return;
|
||||
if (guarding) {
|
||||
props.onFailure();
|
||||
} else {
|
||||
props.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let id2 = -1;
|
||||
const id = window.setTimeout(() => {
|
||||
setGuarding(false);
|
||||
id2 = window.setTimeout(()=>setGuarding(true), difficulty.window)
|
||||
}, Math.random()*3250+1500);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
if(id2 !== -1) clearInterval(id2);
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
let id2 = -1;
|
||||
const id = window.setTimeout(() => {
|
||||
setGuarding(false);
|
||||
id2 = window.setTimeout(() => setGuarding(true), difficulty.window);
|
||||
}, Math.random() * 3250 + 1500);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
if (id2 !== -1) clearInterval(id2);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
||||
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
||||
<p style={{ fontSize: "5em" }}>
|
||||
{guarding ? "!Guarding!" : "!ATTACKING!"}
|
||||
</p>
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,77 +1,112 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState } from "react";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
export function Victory(props: IProps): React.ReactElement {
|
||||
const [faction, setFaction] = useState('none');
|
||||
const [faction, setFaction] = useState("none");
|
||||
|
||||
function quitInfiltration(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(!menu) throw new Error('mainmenu-container somehow null');
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
function quitInfiltration(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if (!menu) throw new Error("mainmenu-container somehow null");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
|
||||
const levelBonus = props.MaxLevel*Math.pow(1.01, props.MaxLevel);
|
||||
const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel);
|
||||
|
||||
const repGain = Math.pow(props.Difficulty+1, 1.1)*
|
||||
Math.pow(props.StartingDifficulty, 1.2)*
|
||||
30*levelBonus*BitNodeMultipliers.InfiltrationRep;
|
||||
const repGain =
|
||||
Math.pow(props.Difficulty + 1, 1.1) *
|
||||
Math.pow(props.StartingDifficulty, 1.2) *
|
||||
30 *
|
||||
levelBonus *
|
||||
BitNodeMultipliers.InfiltrationRep;
|
||||
|
||||
const moneyGain = Math.pow(props.Difficulty+1, 2)*
|
||||
Math.pow(props.StartingDifficulty, 3)*
|
||||
3e3*levelBonus*BitNodeMultipliers.InfiltrationMoney;
|
||||
const moneyGain =
|
||||
Math.pow(props.Difficulty + 1, 2) *
|
||||
Math.pow(props.StartingDifficulty, 3) *
|
||||
3e3 *
|
||||
levelBonus *
|
||||
BitNodeMultipliers.InfiltrationMoney;
|
||||
|
||||
function sell(): void {
|
||||
props.Player.gainMoney(moneyGain);
|
||||
props.Player.recordMoneySource(moneyGain, 'infiltration');
|
||||
quitInfiltration();
|
||||
}
|
||||
function sell(): void {
|
||||
props.Player.gainMoney(moneyGain);
|
||||
props.Player.recordMoneySource(moneyGain, "infiltration");
|
||||
quitInfiltration();
|
||||
}
|
||||
|
||||
function trade(): void {
|
||||
if(faction === 'none') return;
|
||||
Factions[faction].playerReputation += repGain;
|
||||
quitInfiltration();
|
||||
}
|
||||
function trade(): void {
|
||||
if (faction === "none") return;
|
||||
Factions[faction].playerReputation += repGain;
|
||||
quitInfiltration();
|
||||
}
|
||||
|
||||
function changeDropdown(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setFaction(event.target.value);
|
||||
}
|
||||
function changeDropdown(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setFaction(event.target.value);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltration successful!</h1>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<h2>You can trade the confidential information you found for money or reputation.</h2>
|
||||
<select className={"dropdown"} onChange={changeDropdown}>
|
||||
<option key={'none'} value={'none'}>{'none'}</option>
|
||||
{props.Player.factions.filter(f => Factions[f].getInfo().offersWork()).map(f => <option key={f} value={f}>{f}</option>)}
|
||||
</select>
|
||||
<StdButton onClick={trade} text={<>{"Trade for "}{Reputation(repGain)}{" reputation"}</>} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={sell} text={<>{"Sell for "}<Money money={moneyGain} /></>} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={quitInfiltration} text={"Quit"} />
|
||||
</Grid>
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltration successful!</h1>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
||||
<Grid item xs={10}>
|
||||
<h2>
|
||||
You can trade the confidential information you found for money or
|
||||
reputation.
|
||||
</h2>
|
||||
<select className={"dropdown"} onChange={changeDropdown}>
|
||||
<option key={"none"} value={"none"}>
|
||||
{"none"}
|
||||
</option>
|
||||
{props.Player.factions
|
||||
.filter((f) => Factions[f].getInfo().offersWork())
|
||||
.map((f) => (
|
||||
<option key={f} value={f}>
|
||||
{f}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<StdButton
|
||||
onClick={trade}
|
||||
text={
|
||||
<>
|
||||
{"Trade for "}
|
||||
{Reputation(repGain)}
|
||||
{" reputation"}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton
|
||||
onClick={sell}
|
||||
text={
|
||||
<>
|
||||
{"Sell for "}
|
||||
<Money money={moneyGain} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={quitInfiltration} text={"Quit"} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React, { useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
@@ -7,165 +7,178 @@ import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
wiresmin: number;
|
||||
wiresmax: number;
|
||||
rules: number;
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
wiresmin: number;
|
||||
wiresmax: number;
|
||||
rules: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 9000, wiresmin: 4, wiresmax: 4, rules: 2},
|
||||
Normal: {timer: 7000, wiresmin: 6, wiresmax: 6, rules: 2},
|
||||
Hard: {timer: 5000, wiresmin: 8, wiresmax: 8, rules: 3},
|
||||
Impossible: {timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4},
|
||||
}
|
||||
Trivial: { timer: 9000, wiresmin: 4, wiresmax: 4, rules: 2 },
|
||||
Normal: { timer: 7000, wiresmin: 6, wiresmax: 6, rules: 2 },
|
||||
Hard: { timer: 5000, wiresmin: 8, wiresmax: 8, rules: 3 },
|
||||
Impossible: { timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4 },
|
||||
};
|
||||
|
||||
const types = ["|", ".", "/", "-", "█", "#"];
|
||||
|
||||
const types = [
|
||||
"|",
|
||||
".",
|
||||
"/",
|
||||
"-",
|
||||
"█",
|
||||
"#",
|
||||
]
|
||||
|
||||
const colors = [
|
||||
"red",
|
||||
"#FFC107",
|
||||
"blue",
|
||||
"white",
|
||||
]
|
||||
const colors = ["red", "#FFC107", "blue", "white"];
|
||||
|
||||
const colorNames: any = {
|
||||
"red": "red",
|
||||
"#FFC107": "yellow",
|
||||
"blue": "blue",
|
||||
"white": "white",
|
||||
}
|
||||
red: "red",
|
||||
"#FFC107": "yellow",
|
||||
blue: "blue",
|
||||
white: "white",
|
||||
};
|
||||
|
||||
interface Wire {
|
||||
tpe: string;
|
||||
colors: string[];
|
||||
tpe: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
interface Question {
|
||||
toString: () => string;
|
||||
shouldCut: (wire: Wire, index: number) => boolean;
|
||||
toString: () => string;
|
||||
shouldCut: (wire: Wire, index: number) => boolean;
|
||||
}
|
||||
|
||||
export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, wiresmin: 0, wiresmax: 0, rules: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [wires] = useState(generateWires(difficulty));
|
||||
const [cutWires, setCutWires] = useState((new Array(wires.length)).fill(false));
|
||||
const [questions] = useState(generateQuestion(wires, difficulty));
|
||||
const difficulty: Difficulty = {
|
||||
timer: 0,
|
||||
wiresmin: 0,
|
||||
wiresmax: 0,
|
||||
rules: 0,
|
||||
};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [wires] = useState(generateWires(difficulty));
|
||||
const [cutWires, setCutWires] = useState(new Array(wires.length).fill(false));
|
||||
const [questions] = useState(generateQuestion(wires, difficulty));
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const wireNum = parseInt(event.key);
|
||||
|
||||
if(wireNum < 1 || wireNum > wires.length || isNaN(wireNum)) return;
|
||||
setCutWires(old => {
|
||||
const next = [...old];
|
||||
next[wireNum-1] = true;
|
||||
if(!questions.some((q => q.shouldCut(wires[wireNum-1], wireNum-1)))) {
|
||||
props.onFailure();
|
||||
}
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const wireNum = parseInt(event.key);
|
||||
|
||||
// check if we won
|
||||
const wiresToBeCut = [];
|
||||
for(let j = 0; j < wires.length; j++) {
|
||||
let shouldBeCut = false;
|
||||
for(let i = 0; i < questions.length; i++) {
|
||||
shouldBeCut = shouldBeCut || questions[i].shouldCut(wires[j], j)
|
||||
}
|
||||
wiresToBeCut.push(shouldBeCut);
|
||||
}
|
||||
if(wiresToBeCut.every((b, i) => b === next[i])) {
|
||||
props.onSuccess();
|
||||
}
|
||||
if (wireNum < 1 || wireNum > wires.length || isNaN(wireNum)) return;
|
||||
setCutWires((old) => {
|
||||
const next = [...old];
|
||||
next[wireNum - 1] = true;
|
||||
if (
|
||||
!questions.some((q) => q.shouldCut(wires[wireNum - 1], wireNum - 1))
|
||||
) {
|
||||
props.onFailure();
|
||||
}
|
||||
|
||||
return next;
|
||||
});
|
||||
}
|
||||
// check if we won
|
||||
const wiresToBeCut = [];
|
||||
for (let j = 0; j < wires.length; j++) {
|
||||
let shouldBeCut = false;
|
||||
for (let i = 0; i < questions.length; i++) {
|
||||
shouldBeCut = shouldBeCut || questions[i].shouldCut(wires[j], j);
|
||||
}
|
||||
wiresToBeCut.push(shouldBeCut);
|
||||
}
|
||||
if (wiresToBeCut.every((b, i) => b === next[i])) {
|
||||
props.onSuccess();
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Cut the wires with the following properties! (keyboard 1 to 9)</h1>
|
||||
{questions.map((question, i) => <h3 key={i}>{question.toString()}</h3>)}
|
||||
<pre>{(new Array(wires.length)).fill(0).map((_, i) => <span key={i}> {i+1} </span>)}</pre>
|
||||
{(new Array(8)).fill(0).map((_, i) => <div key={i}>
|
||||
<pre>
|
||||
{wires.map((wire, j) => {
|
||||
if((i === 3 || i === 4) && cutWires[j]) return <span key={j}> </span>;
|
||||
return <span key={j} style={{color: wire.colors[i%wire.colors.length]}}>|{wire.tpe}| </span>
|
||||
})}
|
||||
</pre>
|
||||
</div>)}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>
|
||||
Cut the wires with the following properties! (keyboard 1 to 9)
|
||||
</h1>
|
||||
{questions.map((question, i) => (
|
||||
<h3 key={i}>{question.toString()}</h3>
|
||||
))}
|
||||
<pre>
|
||||
{new Array(wires.length).fill(0).map((_, i) => (
|
||||
<span key={i}> {i + 1} </span>
|
||||
))}
|
||||
</pre>
|
||||
{new Array(8).fill(0).map((_, i) => (
|
||||
<div key={i}>
|
||||
<pre>
|
||||
{wires.map((wire, j) => {
|
||||
if ((i === 3 || i === 4) && cutWires[j])
|
||||
return (
|
||||
<span key={j}> </span>
|
||||
);
|
||||
return (
|
||||
<span
|
||||
key={j}
|
||||
style={{ color: wire.colors[i % wire.colors.length] }}
|
||||
>
|
||||
|{wire.tpe}|
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function randomPositionQuestion(wires: Wire[]): Question {
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut wires number ${index+1}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire, i: number): boolean => {
|
||||
return index === i;
|
||||
},
|
||||
}
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut wires number ${index + 1}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire, i: number): boolean => {
|
||||
return index === i;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function randomColorQuestion(wires: Wire[]): Question {
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
const cutColor = wires[index].colors[0];
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut all wires colored ${colorNames[cutColor]}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire): boolean => {
|
||||
return wire.colors.includes(cutColor);
|
||||
},
|
||||
}
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
const cutColor = wires[index].colors[0];
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut all wires colored ${colorNames[cutColor]}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire): boolean => {
|
||||
return wire.colors.includes(cutColor);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function generateQuestion(wires: Wire[], difficulty: Difficulty): Question[] {
|
||||
const numQuestions = difficulty.rules;
|
||||
const questionGenerators = [
|
||||
randomPositionQuestion,
|
||||
randomColorQuestion,
|
||||
]
|
||||
const questions = [];
|
||||
for(let i = 0; i < numQuestions; i++) {
|
||||
questions.push(questionGenerators[i%2](wires));
|
||||
}
|
||||
return questions;
|
||||
const numQuestions = difficulty.rules;
|
||||
const questionGenerators = [randomPositionQuestion, randomColorQuestion];
|
||||
const questions = [];
|
||||
for (let i = 0; i < numQuestions; i++) {
|
||||
questions.push(questionGenerators[i % 2](wires));
|
||||
}
|
||||
return questions;
|
||||
}
|
||||
|
||||
function generateWires(difficulty: Difficulty): Wire[] {
|
||||
const wires = [];
|
||||
const numWires = random(difficulty.wiresmin, difficulty.wiresmax);
|
||||
for(let i = 0; i < numWires; i++) {
|
||||
const wireColors = [colors[Math.floor(Math.random()*colors.length)]];
|
||||
if(Math.random() < 0.15) {
|
||||
wireColors.push(colors[Math.floor(Math.random()*colors.length)]);
|
||||
}
|
||||
wires.push({
|
||||
tpe: types[Math.floor(Math.random()*types.length)],
|
||||
colors: wireColors,
|
||||
});
|
||||
const wires = [];
|
||||
const numWires = random(difficulty.wiresmin, difficulty.wiresmax);
|
||||
for (let i = 0; i < numWires; i++) {
|
||||
const wireColors = [colors[Math.floor(Math.random() * colors.length)]];
|
||||
if (Math.random() < 0.15) {
|
||||
wireColors.push(colors[Math.floor(Math.random() * colors.length)]);
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
wires.push({
|
||||
tpe: types[Math.floor(Math.random() * types.length)],
|
||||
colors: wireColors,
|
||||
});
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user