build dev

This commit is contained in:
Olivier Gagnon 2021-09-18 04:01:07 -04:00
parent bdfa4be71f
commit e1a22016b5
14 changed files with 1124 additions and 1269 deletions

698
dist/engine.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1081
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -22,7 +22,7 @@ import { use } from "../../ui/Context";
import { CreateGangPopup } from "./CreateGangPopup";
type IProps = {
faction: Faction | null;
faction: Faction;
startHackingMissionFn: (faction: Faction) => void;
};
@ -65,7 +65,6 @@ const GangNames = [
export function FactionRoot(props: IProps): React.ReactElement {
const faction = props.faction;
if (faction === null) throw new Error("Trying to render the Faction page with null faction");
const player = use.Player();
const router = use.Router();

@ -2,12 +2,12 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import React, { useState } from "react";
import { Intro } from "./Intro";
import { Game } from "./Game";
import { LocationName } from "../../Locations/data/LocationNames";
import { Location } from "../../Locations/Location";
import { Locations } from "../../Locations/Locations";
import { use } from "../../ui/Context";
interface IProps {
location: LocationName;
location: Location;
}
function calcDifficulty(player: IPlayer, startingDifficulty: number): number {
const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma;
@ -22,9 +22,8 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
const router = use.Router();
const [start, setStart] = useState(false);
const loc = Locations[props.location];
if (loc.infiltrationData === undefined) throw new Error("Trying to do infiltration on invalid location.");
const startingDifficulty = loc.infiltrationData.startingSecurityLevel;
if (props.location.infiltrationData === undefined) throw new Error("Trying to do infiltration on invalid location.");
const startingDifficulty = props.location.infiltrationData.startingSecurityLevel;
const difficulty = calcDifficulty(player, startingDifficulty);
function cancel(): void {
@ -36,7 +35,7 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
<Intro
Location={props.location}
Difficulty={difficulty}
MaxLevel={loc.infiltrationData.maxClearanceLevel}
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
start={() => setStart(true)}
cancel={cancel}
/>
@ -47,7 +46,7 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
<Game
StartingDifficulty={startingDifficulty}
Difficulty={difficulty}
MaxLevel={loc.infiltrationData.maxClearanceLevel}
MaxLevel={props.location.infiltrationData.maxClearanceLevel}
/>
);
}

@ -1,9 +1,10 @@
import React from "react";
import { StdButton } from "../../ui/React/StdButton";
import { Location } from "../../Locations/Location";
import Grid from "@mui/material/Grid";
interface IProps {
Location: string;
Location: Location;
Difficulty: number;
MaxLevel: number;
start: () => void;

@ -6,114 +6,134 @@
import * as React from "react";
import { City } from "../City";
import { Cities } from "../Cities";
import { LocationName } from "../data/LocationNames";
import { Locations } from "../Locations";
import { Location } from "../Location";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
import { IRouter } from "../../ui/Router";
type IProps = {
city: City;
enterLocation: (to: LocationName) => void;
};
export class LocationCity extends React.Component<IProps, any> {
asciiCity(): React.ReactNode {
const LocationLetter = (location: LocationName): JSX.Element => {
if (location)
return (
<span
key={location}
className="tooltip"
style={{
color: "white",
whiteSpace: "nowrap",
margin: "0px",
padding: "0px",
cursor: "pointer",
}}
onClick={this.props.enterLocation.bind(this, location)}
>
<b>X</b>
</span>
);
return <span>*</span>;
};
const locationLettersRegex = /[A-Z]/g;
const letterMap: any = {
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7,
I: 8,
J: 9,
K: 10,
L: 11,
M: 12,
N: 13,
O: 14,
P: 15,
Q: 16,
R: 17,
S: 18,
T: 19,
U: 20,
V: 21,
W: 22,
X: 23,
Y: 24,
Z: 25,
};
const lineElems = (s: string): JSX.Element[] => {
const elems: any[] = [];
const matches: any[] = [];
let match: any;
while ((match = locationLettersRegex.exec(s)) !== null) {
matches.push(match);
}
if (matches.length === 0) {
elems.push(s);
return elems;
}
for (let i = 0; i < matches.length; i++) {
const startI = i === 0 ? 0 : matches[i - 1].index + 1;
const endI = matches[i].index;
elems.push(s.slice(startI, endI));
const locationI = letterMap[s[matches[i].index]];
elems.push(LocationLetter(this.props.city.locations[locationI]));
}
elems.push(s.slice(matches[matches.length - 1].index + 1));
return elems;
};
const elems: JSX.Element[] = [];
const lines = this.props.city.asciiArt.split("\n");
for (const i in lines) {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>);
}
return <div className="noselect">{elems}</div>;
}
listCity(): React.ReactNode {
const locationButtons = this.props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={this.props.enterLocation.bind(this, locName)} text={locName} />
</li>
);
});
return <ul>{locationButtons}</ul>;
}
render(): React.ReactNode {
return <>{Settings.DisableASCIIArt ? this.listCity() : this.asciiCity()}</>;
function toLocation(router: IRouter, location: Location): void {
if (location.name === LocationName.TravelAgency) {
router.toTravel();
} else if (location.name === LocationName.WorldStockExchange) {
router.toStockMarket();
} else {
router.toLocation(location);
}
}
function LocationLetter(location: Location): React.ReactElement {
const router = use.Router();
if (!location) return <span>*</span>;
return (
<span
key={location.name}
className="tooltip"
style={{
color: "white",
whiteSpace: "nowrap",
margin: "0px",
padding: "0px",
cursor: "pointer",
}}
onClick={() => toLocation(router, location)}
>
<b>X</b>
</span>
);
}
function ASCIICity(props: IProps): React.ReactElement {
const locationLettersRegex = /[A-Z]/g;
const letterMap: any = {
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7,
I: 8,
J: 9,
K: 10,
L: 11,
M: 12,
N: 13,
O: 14,
P: 15,
Q: 16,
R: 17,
S: 18,
T: 19,
U: 20,
V: 21,
W: 22,
X: 23,
Y: 24,
Z: 25,
};
const lineElems = (s: string): JSX.Element[] => {
const elems: any[] = [];
const matches: any[] = [];
let match: any;
while ((match = locationLettersRegex.exec(s)) !== null) {
matches.push(match);
}
if (matches.length === 0) {
elems.push(s);
return elems;
}
for (let i = 0; i < matches.length; i++) {
const startI = i === 0 ? 0 : matches[i - 1].index + 1;
const endI = matches[i].index;
elems.push(s.slice(startI, endI));
const locationI = letterMap[s[matches[i].index]];
elems.push(LocationLetter(Locations[props.city.locations[locationI]]));
}
elems.push(s.slice(matches[matches.length - 1].index + 1));
return elems;
};
const elems: JSX.Element[] = [];
const lines = props.city.asciiArt.split("\n");
for (const i in lines) {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>);
}
return <div className="noselect">{elems}</div>;
}
function ListCity(props: IProps): React.ReactElement {
const router = use.Router();
const locationButtons = props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={() => toLocation(router, Locations[locName])} text={locName} />
</li>
);
});
return <ul>{locationButtons}</ul>;
}
export function LocationCity(): React.ReactElement {
const player = use.Player();
const city = Cities[player.city];
return (
<div className="noselect">
<h2>{city.name}</h2>
{Settings.DisableASCIIArt ? <ListCity city={city} /> : <ASCIICity city={city} />}
</div>
);
}

@ -157,7 +157,7 @@ export function CompanyLocation(props: IProps): React.ReactElement {
if (!loc.infiltrationData)
throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`);
router.toInfiltration(props.locName);
router.toInfiltration(loc);
}
function work(e: React.MouseEvent<HTMLElement>): void {

@ -22,7 +22,6 @@ import { CityName } from "../data/CityNames";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { SpecialServerIps } from "../../Server/SpecialServerIps";
@ -30,92 +29,73 @@ import { getServer, isBackdoorInstalled } from "../../Server/ServerHelpers";
import { StdButton } from "../../ui/React/StdButton";
import { CorruptableText } from "../../ui/React/CorruptableText";
import { use } from "../../ui/Context";
type IProps = {
engine: IEngine;
router: IRouter;
loc: Location;
p: IPlayer;
returnToCity: () => void;
travel: (to: CityName) => void;
};
export class GenericLocation extends React.Component<IProps, any> {
/**
* Stores button styling that sets them all to block display
*/
btnStyle: any;
constructor(props: IProps) {
super(props);
this.btnStyle = { display: "block" };
}
export function GenericLocation({ loc }: IProps): React.ReactElement {
const router = use.Router();
const player = use.Player();
/**
* Determine what needs to be rendered for this location based on the locations
* type. Returns an array of React components that should be rendered
*/
getLocationSpecificContent(): React.ReactNode[] {
function getLocationSpecificContent(): React.ReactNode[] {
const content: React.ReactNode[] = [];
if (this.props.loc.types.includes(LocationType.Company)) {
content.push(<CompanyLocation key={"companylocation"} locName={this.props.loc.name} />);
if (loc.types.includes(LocationType.Company)) {
content.push(<CompanyLocation key={"companylocation"} locName={loc.name} />);
}
if (this.props.loc.types.includes(LocationType.Gym)) {
content.push(<GymLocation key={"gymlocation"} loc={this.props.loc} p={this.props.p} />);
if (loc.types.includes(LocationType.Gym)) {
content.push(<GymLocation key={"gymlocation"} loc={loc} p={player} />);
}
if (this.props.loc.types.includes(LocationType.Hospital)) {
content.push(<HospitalLocation key={"hospitallocation"} p={this.props.p} />);
if (loc.types.includes(LocationType.Hospital)) {
content.push(<HospitalLocation key={"hospitallocation"} p={player} />);
}
if (this.props.loc.types.includes(LocationType.Slums)) {
if (loc.types.includes(LocationType.Slums)) {
content.push(<SlumsLocation key={"slumslocation"} />);
}
if (this.props.loc.types.includes(LocationType.Special)) {
content.push(<SpecialLocation key={"speciallocation"} loc={this.props.loc} />);
if (loc.types.includes(LocationType.Special)) {
content.push(<SpecialLocation key={"speciallocation"} loc={loc} />);
}
if (this.props.loc.types.includes(LocationType.TechVendor)) {
content.push(<TechVendorLocation key={"techvendorlocation"} loc={this.props.loc} p={this.props.p} />);
if (loc.types.includes(LocationType.TechVendor)) {
content.push(<TechVendorLocation key={"techvendorlocation"} loc={loc} p={player} />);
}
if (this.props.loc.types.includes(LocationType.TravelAgency)) {
content.push(<TravelAgencyRoot key={"travelagencylocation"} p={this.props.p} router={this.props.router} />);
if (loc.types.includes(LocationType.TravelAgency)) {
content.push(<TravelAgencyRoot key={"travelagencylocation"} p={player} router={router} />);
}
if (this.props.loc.types.includes(LocationType.University)) {
content.push(<UniversityLocation key={"universitylocation"} loc={this.props.loc} />);
if (loc.types.includes(LocationType.University)) {
content.push(<UniversityLocation key={"universitylocation"} loc={loc} />);
}
if (this.props.loc.types.includes(LocationType.Casino)) {
content.push(<CasinoLocation key={"casinoLocation"} p={this.props.p} />);
if (loc.types.includes(LocationType.Casino)) {
content.push(<CasinoLocation key={"casinoLocation"} p={player} />);
}
return content;
}
render(): React.ReactNode {
const locContent: React.ReactNode[] = this.getLocationSpecificContent();
const ip = SpecialServerIps.getIp(this.props.loc.name);
const server = getServer(ip);
const backdoorInstalled = server !== null && isBackdoorInstalled(server);
const locContent: React.ReactNode[] = getLocationSpecificContent();
const ip = SpecialServerIps.getIp(loc.name);
const server = getServer(ip);
const backdoorInstalled = server !== null && isBackdoorInstalled(server);
return (
<div>
<StdButton onClick={this.props.returnToCity} style={this.btnStyle} text={"Return to World"} />
<h1 className="noselect">
{backdoorInstalled && !Settings.DisableTextEffects ? (
<CorruptableText content={this.props.loc.name} />
) : (
this.props.loc.name
)}
</h1>
{locContent}
</div>
);
}
return (
<div>
<StdButton onClick={() => router.toCity()} style={{ display: "block" }} text={"Return to World"} />
<h1 className="noselect">
{backdoorInstalled && !Settings.DisableTextEffects ? <CorruptableText content={loc.name} /> : loc.name}
</h1>
{locContent}
</div>
);
}

@ -1,160 +0,0 @@
/**
* Root React Component for displaying overall Location UI
*/
import * as React from "react";
import { LocationCity } from "./City";
import { GenericLocation } from "./GenericLocation";
import { Cities } from "../Cities";
import { Locations } from "../Locations";
import { LocationType } from "../LocationTypeEnum";
import { CityName } from "../data/CityNames";
import { LocationName } from "../data/LocationNames";
import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = {
initiallyInCity?: boolean;
engine: IEngine;
router: IRouter;
p: IPlayer;
};
type IState = {
city: CityName;
inCity: boolean;
location: LocationName;
};
export class LocationRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
city: props.p.city,
inCity: props.initiallyInCity == null ? true : props.initiallyInCity,
location: props.p.location,
};
this.enterLocation = this.enterLocation.bind(this);
this.returnToCity = this.returnToCity.bind(this);
this.travel = this.travel.bind(this);
}
enterLocation(to: LocationName): void {
if (to == LocationName.TravelAgency) {
this.props.router.toTravel();
return;
}
this.props.p.gotoLocation(to);
this.setState({
inCity: false,
location: to,
});
}
/**
* Click listener for a button that lets the player go from a specific location
* back to the city
*/
returnToCity(): void {
this.setState({
inCity: true,
});
}
/**
* Render UI for a city
*/
renderCity(): React.ReactNode {
const city = Cities[this.state.city];
if (city == null) {
throw new Error(`Invalid city when rendering UI: ${this.state.city}`);
}
return (
<div className="noselect">
<h2>{this.state.city}</h2>
<LocationCity city={city} enterLocation={this.enterLocation} />
</div>
);
}
/**
* Render UI for a specific location
*/
renderLocation(): React.ReactNode {
const loc = Locations[this.state.location];
if (loc == null) {
throw new Error(`Invalid location when rendering UI: ${this.state.location}`);
}
if (loc.types.includes(LocationType.StockMarket)) {
setTimeout(() => this.props.router.toStockMarket(), 50);
return <></>;
}
return (
<GenericLocation
engine={this.props.engine}
router={this.props.router}
loc={loc}
p={this.props.p}
returnToCity={this.returnToCity}
travel={this.travel}
/>
);
}
/**
* Travel to a different city
* @param {CityName} to - Destination city
*/
travel(to: CityName): void {
const p = this.props.p;
const cost = CONSTANTS.TravelCost;
if (!p.canAfford(cost)) {
dialogBoxCreate(`You cannot afford to travel to ${to}`);
return;
}
p.loseMoney(cost);
p.travel(to);
dialogBoxCreate(<span className="noselect">You are now in {to}!</span>);
// Dynamically update main menu
if (p.firstTimeTraveled === false) {
p.firstTimeTraveled = true;
const travelTab = document.getElementById("travel-tab");
const worldHeader = document.getElementById("world-menu-header");
if (travelTab != null && worldHeader !== null) {
travelTab.style.display = "list-item";
worldHeader.click();
worldHeader.click();
}
}
if (this.props.p.travel(to)) {
this.setState({
inCity: true,
city: to,
});
}
}
render(): React.ReactNode {
if (this.state.inCity) {
return this.renderCity();
} else {
return this.renderLocation();
}
}
}

@ -555,7 +555,8 @@ export function SidebarRoot(props: IProps): React.ReactElement {
button
key={"City"}
className={clsx({
[classes.active]: props.page === Page.City,
[classes.active]:
props.page === Page.City || props.page === Page.Resleeves || props.page === Page.Location,
})}
onClick={clickCity}
>

@ -7,6 +7,9 @@ import { installAugmentations } from "../Augmentation/AugmentationHelpers";
import { saveObject } from "../SaveObject";
import { onExport } from "../ExportBonus";
import { LocationName } from "../Locations/data/LocationNames";
import { Location } from "../Locations/Location";
import { Locations } from "../Locations/Locations";
import { Faction } from "../Faction/Faction";
import { prestigeAugmentation } from "../Prestige";
import { dialogBoxCreate } from "../../utils/DialogBox";
@ -40,7 +43,8 @@ import { WorkInProgressRoot } from "./WorkInProgressRoot";
import { GameOptionsRoot } from "../ui/React/GameOptionsRoot";
import { SleeveRoot } from "../PersonObjects/Sleeve/ui/SleeveRoot";
import { HacknetRoot } from "../Hacknet/ui/HacknetRoot";
import { LocationRoot } from "../Locations/ui/Root";
import { GenericLocation } from "../Locations/ui/GenericLocation";
import { LocationCity } from "../Locations/ui/City";
import { ProgramsRoot } from "../Programs/ui/ProgramsRoot";
import { Root as ScriptEditorRoot } from "../ScriptEditor/ui/Root";
import { MilestonesRoot } from "../Milestones/ui/MilestonesRoot";
@ -162,6 +166,9 @@ export let Router: IRouter = {
toBladeburnerCinematic: () => {
throw new Error("Router called before initialization");
},
toLocation: () => {
throw new Error("Router called before initialization");
},
};
function determineStartPage(player: IPlayer): Page {
@ -171,15 +178,19 @@ function determineStartPage(player: IPlayer): Page {
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
const classes = useStyles();
const contentRef = useRef<HTMLDivElement>(null);
const [faction, setFaction] = useState<Faction | null>(
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : null,
);
const [page, setPage] = useState(determineStartPage(player));
const contentRef = useRef<HTMLDivElement>(null);
const [faction, setFaction] = useState<Faction>(
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
);
if (faction === undefined && page === Page.Faction)
throw new Error("Trying to go to a page without the proper setup");
const [flume, setFlume] = useState<boolean>(false);
const [quick, setQuick] = useState<boolean>(false);
const [location, setLocation] = useState<LocationName>(LocationName.Sector12);
const [location, setLocation] = useState<Location>(undefined as unknown as Location);
if (location === undefined && (page === Page.Infiltration || page === Page.Location || page === Page.Job))
throw new Error("Trying to go to a page without the proper setup");
const [cinematicText, setCinematicText] = useState("");
@ -212,12 +223,10 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
toTerminal: () => setPage(Page.Terminal),
toTutorial: () => setPage(Page.Tutorial),
toJob: () => {
player.gotoLocation(player.companyName as LocationName);
setLocation(Locations[player.companyName]);
setPage(Page.Job);
},
toCity: () => {
// TODO This conversion is bad.
player.gotoLocation(player.city as unknown as LocationName);
setPage(Page.City);
},
toTravel: () => {
@ -229,7 +238,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
setQuick(quick);
setPage(Page.BitVerse);
},
toInfiltration: (location: LocationName) => {
toInfiltration: (location: Location) => {
setLocation(location);
setPage(Page.Infiltration);
},
@ -238,6 +247,10 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
setPage(Page.BladeburnerCinematic);
setCinematicText(cinematicText);
},
toLocation: (location: Location) => {
setLocation(location);
setPage(Page.Location);
},
};
useEffect(() => {
@ -317,9 +330,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
stockMarket={StockMarket}
/>
) : page === Page.City ? (
<LocationRoot initiallyInCity={true} engine={engine} p={player} router={Router} />
<LocationCity />
) : page === Page.Job ? (
<LocationRoot initiallyInCity={false} engine={engine} p={player} router={Router} />
<GenericLocation loc={location} />
) : page === Page.Location ? (
<GenericLocation loc={location} />
) : page === Page.Options ? (
<GameOptionsRoot
player={player}

@ -1,5 +1,7 @@
import { Faction } from "../Faction/Faction";
import { LocationName } from "../Locations/data/LocationNames";
import { Location } from "../Locations/Location";
/**
* The full-screen page the player is currently be on.
* These pages are mutually exclusive.
@ -31,6 +33,7 @@ export enum Page {
Tutorial,
Work,
BladeburnerCinematic,
Location,
}
/**
@ -57,7 +60,7 @@ export interface IRouter {
toGameOptions(): void;
toGang(): void;
toHacknetNodes(): void;
toInfiltration(location: LocationName): void;
toInfiltration(location: Location): void;
toJob(): void;
toMilestones(): void;
toResleeves(): void;
@ -69,4 +72,5 @@ export interface IRouter {
toTutorial(): void;
toWork(): void;
toBladeburnerCinematic(): void;
toLocation(location: Location): void;
}

@ -10,6 +10,9 @@ import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFuncti
import { Factions } from "../Faction/Factions";
import { Company } from "../Company/Company";
import { Companies } from "../Company/Companies";
import { Locations } from "../Locations/Locations";
import { LocationName } from "../Locations/data/LocationNames";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -308,7 +311,13 @@ export function WorkInProgressRoot(): React.ReactElement {
<br />
<pre>{progressBar}</pre>
<button className="work-button" onClick={() => player.finishCrime(true)}>
<button
className="work-button"
onClick={() => {
router.toLocation(Locations[LocationName.Slums]);
player.finishCrime(true);
}}
>
Cancel crime
</button>
</div>