This commit is contained in:
danielyxie 2019-04-14 02:08:10 -07:00
parent 98a04e4932
commit 7417fb6ef8
38 changed files with 1377 additions and 842 deletions

@ -75,7 +75,7 @@
margin: 4px; margin: 4px;
padding: 2px; padding: 2px;
resize: none; resize: none;
width: 50%; width: 60%;
} }
#script-editor-status { #script-editor-status {

File diff suppressed because one or more lines are too long

2
dist/engine.css vendored

@ -893,7 +893,7 @@ button {
margin: 4px; margin: 4px;
padding: 2px; padding: 2px;
resize: none; resize: none;
width: 50%; } width: 60%; }
#script-editor-status { #script-editor-status {
float: left; float: left;

114
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -3,6 +3,18 @@
Changelog Changelog
========= =========
v0.46.2 - 4/14/2019
-------------------
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
** (Karma is a hidden stat and is lowered by committing crimes)
* Gang changes:
** Bug Fix: Gangs can no longer clash with themselve
** Bug Fix: Winning against another gang should properly reduce their power
* Bug Fix: Terminal 'wget' command now works properly
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
* Bug Fix: Fixed button for creating Corporations
v0.46.1 - 4/12/2019 v0.46.1 - 4/12/2019
------------------- -------------------
* Added a very rudimentary directory system to the Terminal * Added a very rudimentary directory system to the Terminal

@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
# The short X.Y version. # The short X.Y version.
version = '0.46' version = '0.46'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.46.0' release = '0.46.2'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

@ -8,6 +8,7 @@ recruitMember() Netscript Function
Attempt to recruit a new gang member. Attempt to recruit a new gang member.
Possible reasons for failure: Possible reasons for failure:
* Cannot currently recruit a new member * Cannot currently recruit a new member
* There already exists a member with the specified name * There already exists a member with the specified name

@ -3,7 +3,7 @@ hashCost() Netscript Function
.. warning:: This page contains spoilers for the game .. warning:: This page contains spoilers for the game
.. js:function:: hashCost(upgName, upgTarget) .. js:function:: hashCost(upgName)
:param string upgName: Name of upgrade to get the cost of. Must be an exact match :param string upgName: Name of upgrade to get the cost of. Must be an exact match

@ -115,7 +115,7 @@
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/> <input id="script-editor-filename" type="text" maxlength="100" tabindex="1"/>
</div> </div>
<div id="ace-editor"></div> <div id="ace-editor"></div>
@ -221,8 +221,6 @@
<!-- Single Faction info (when you select a faction from the Factions menu) --> <!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div> <div id="faction-container" class="generic-menupage-container"></div>
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
<!-- Augmentations --> <!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div> <div id="augmentations-container" class="generic-menupage-container"></div>

@ -114,5 +114,5 @@
"watch": "webpack --watch --mode production", "watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development" "watch:dev": "webpack --watch --mode development"
}, },
"version": "0.45.0" "version": "0.46.2"
} }

@ -10,6 +10,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
interface IConstructorParams { interface IConstructorParams {
info: string; info: string;
isSpecial?: boolean;
moneyCost: number; moneyCost: number;
name: string; name: string;
prereqs?: string[]; prereqs?: string[];
@ -62,6 +63,9 @@ export class Augmentation {
// Description of what this Aug is and what it does // Description of what this Aug is and what it does
info: string = ""; info: string = "";
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial: boolean = false;
// Augmentation level - for repeatable Augs like NeuroFlux Governor // Augmentation level - for repeatable Augs like NeuroFlux Governor
level: number = 0; level: number = 0;
@ -90,6 +94,10 @@ export class Augmentation {
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost; this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
this.startingCost = this.baseCost; this.startingCost = this.baseCost;
if (params.isSpecial) {
this.isSpecial = true;
}
this.level = 0; this.level = 0;
// Set multipliers // Set multipliers

@ -1677,19 +1677,6 @@ function initAugmentations() {
} }
AddToAugmentations(SNA); AddToAugmentations(SNA);
//For BitNode-2, add all Augmentations to crime/evil factions.
//Do this before adding special Augmentations that become available in later BitNodes
if (Player.bitNodeN === 2) {
console.log("Adding all augmentations to crime factions for Bit node 2");
Factions["Slum Snakes"].addAllAugmentations(Augmentations);
Factions["Tetrads"].addAllAugmentations(Augmentations);
Factions["The Syndicate"].addAllAugmentations(Augmentations);
Factions["The Dark Army"].addAllAugmentations(Augmentations);
Factions["Speakers for the Dead"].addAllAugmentations(Augmentations);
Factions["NiteSec"].addAllAugmentations(Augmentations);
Factions["The Black Hand"].addAllAugmentations(Augmentations);
}
//Special Bladeburner Augmentations //Special Bladeburner Augmentations
var BladeburnersFactionName = "Bladeburners"; var BladeburnersFactionName = "Bladeburners";
if (factionExists(BladeburnersFactionName)) { if (factionExists(BladeburnersFactionName)) {
@ -1708,6 +1695,7 @@ function initAugmentations() {
"Increases the player's dexterity by 5%.", "Increases the player's dexterity by 5%.",
bladeburner_success_chance_mult: 1.03, bladeburner_success_chance_mult: 1.03,
dexterity_mult: 1.05, dexterity_mult: 1.05,
isSpecial: true,
}); });
EsperEyewear.addToFactions([BladeburnersFactionName]); EsperEyewear.addToFactions([BladeburnersFactionName]);
resetAugmentation(EsperEyewear); resetAugmentation(EsperEyewear);
@ -1725,6 +1713,7 @@ function initAugmentations() {
bladeburner_success_chance_mult: 1.03, bladeburner_success_chance_mult: 1.03,
bladeburner_analysis_mult: 1.05, bladeburner_analysis_mult: 1.05,
bladeburner_stamina_gain_mult: 1.02, bladeburner_stamina_gain_mult: 1.02,
isSpecial: true,
}); });
EMS4Recombination.addToFactions([BladeburnersFactionName]); EMS4Recombination.addToFactions([BladeburnersFactionName]);
resetAugmentation(EMS4Recombination); resetAugmentation(EMS4Recombination);
@ -1743,6 +1732,7 @@ function initAugmentations() {
strength_mult: 1.05, strength_mult: 1.05,
dexterity_mult: 1.05, dexterity_mult: 1.05,
bladeburner_success_chance_mult: 1.04, bladeburner_success_chance_mult: 1.04,
isSpecial: true,
}); });
OrionShoulder.addToFactions([BladeburnersFactionName]); OrionShoulder.addToFactions([BladeburnersFactionName]);
resetAugmentation(OrionShoulder); resetAugmentation(OrionShoulder);
@ -1758,6 +1748,7 @@ function initAugmentations() {
"This augmentation:<br>" + "This augmentation:<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 6%.", "Increases the player's success chance in Bladeburner contracts/operations by 6%.",
bladeburner_success_chance_mult: 1.06, bladeburner_success_chance_mult: 1.06,
isSpecial: true,
}); });
HyperionV1.addToFactions([BladeburnersFactionName]); HyperionV1.addToFactions([BladeburnersFactionName]);
resetAugmentation(HyperionV1); resetAugmentation(HyperionV1);
@ -1772,6 +1763,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 8%.", "Increases the player's success chance in Bladeburner contracts/operations by 8%.",
prereqs:[AugmentationNames.HyperionV1], prereqs:[AugmentationNames.HyperionV1],
bladeburner_success_chance_mult: 1.08, bladeburner_success_chance_mult: 1.08,
isSpecial: true,
}); });
HyperionV2.addToFactions([BladeburnersFactionName]); HyperionV2.addToFactions([BladeburnersFactionName]);
resetAugmentation(HyperionV2); resetAugmentation(HyperionV2);
@ -1790,6 +1782,7 @@ function initAugmentations() {
dexterity_mult: 1.07, dexterity_mult: 1.07,
agility_mult: 1.07, agility_mult: 1.07,
bladeburner_stamina_gain_mult: 1.05, bladeburner_stamina_gain_mult: 1.05,
isSpecial: true,
}); });
GolemSerum.addToFactions([BladeburnersFactionName]); GolemSerum.addToFactions([BladeburnersFactionName]);
resetAugmentation(GolemSerum); resetAugmentation(GolemSerum);
@ -1805,6 +1798,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.1, dexterity_exp_mult: 1.1,
bladeburner_analysis_mult: 1.1, bladeburner_analysis_mult: 1.1,
bladeburner_success_chance_mult: 1.04, bladeburner_success_chance_mult: 1.04,
isSpecial: true,
}); });
VangelisVirus.addToFactions([BladeburnersFactionName]); VangelisVirus.addToFactions([BladeburnersFactionName]);
resetAugmentation(VangelisVirus); resetAugmentation(VangelisVirus);
@ -1824,6 +1818,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.1, dexterity_exp_mult: 1.1,
bladeburner_analysis_mult: 1.15, bladeburner_analysis_mult: 1.15,
bladeburner_success_chance_mult: 1.05, bladeburner_success_chance_mult: 1.05,
isSpecial: true,
}); });
VangelisVirus3.addToFactions([BladeburnersFactionName]); VangelisVirus3.addToFactions([BladeburnersFactionName]);
resetAugmentation(VangelisVirus3); resetAugmentation(VangelisVirus3);
@ -1842,6 +1837,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.05, dexterity_exp_mult: 1.05,
agility_exp_mult: 1.05, agility_exp_mult: 1.05,
bladeburner_max_stamina_mult: 1.1, bladeburner_max_stamina_mult: 1.1,
isSpecial: true,
}); });
INTERLINKED.addToFactions([BladeburnersFactionName]); INTERLINKED.addToFactions([BladeburnersFactionName]);
resetAugmentation(INTERLINKED); resetAugmentation(INTERLINKED);
@ -1859,6 +1855,7 @@ function initAugmentations() {
agility_mult: 1.05, agility_mult: 1.05,
bladeburner_max_stamina_mult: 1.05, bladeburner_max_stamina_mult: 1.05,
bladeburner_stamina_gain_mult: 1.05, bladeburner_stamina_gain_mult: 1.05,
isSpecial: true,
}); });
BladeRunner.addToFactions([BladeburnersFactionName]); BladeRunner.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeRunner); resetAugmentation(BladeRunner);
@ -1879,6 +1876,7 @@ function initAugmentations() {
agility_mult: 1.04, agility_mult: 1.04,
bladeburner_stamina_gain_mult: 1.02, bladeburner_stamina_gain_mult: 1.02,
bladeburner_success_chance_mult: 1.03, bladeburner_success_chance_mult: 1.03,
isSpecial: true,
}); });
BladeArmor.addToFactions([BladeburnersFactionName]); BladeArmor.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmor); resetAugmentation(BladeArmor);
@ -1895,6 +1893,7 @@ function initAugmentations() {
bladeburner_success_chance_mult: 1.05, bladeburner_success_chance_mult: 1.05,
bladeburner_stamina_gain_mult: 1.02, bladeburner_stamina_gain_mult: 1.02,
bladeburner_max_stamina_mult: 1.05, bladeburner_max_stamina_mult: 1.05,
isSpecial: true,
}); });
BladeArmorPowerCells.addToFactions([BladeburnersFactionName]); BladeArmorPowerCells.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorPowerCells); resetAugmentation(BladeArmorPowerCells);
@ -1909,6 +1908,7 @@ function initAugmentations() {
prereqs:[AugmentationNames.BladeArmor], prereqs:[AugmentationNames.BladeArmor],
defense_mult: 1.05, defense_mult: 1.05,
bladeburner_success_chance_mult: 1.06, bladeburner_success_chance_mult: 1.06,
isSpecial: true,
}); });
BladeArmorEnergyShielding.addToFactions([BladeburnersFactionName]); BladeArmorEnergyShielding.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorEnergyShielding); resetAugmentation(BladeArmorEnergyShielding);
@ -1922,6 +1922,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 8%.", "Increases the player's success chance in Bladeburner contracts/operations by 8%.",
prereqs:[AugmentationNames.BladeArmor], prereqs:[AugmentationNames.BladeArmor],
bladeburner_success_chance_mult: 1.08, bladeburner_success_chance_mult: 1.08,
isSpecial: true,
}); });
BladeArmorUnibeam.addToFactions([BladeburnersFactionName]); BladeArmorUnibeam.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorUnibeam); resetAugmentation(BladeArmorUnibeam);
@ -1936,6 +1937,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 10%.", "Increases the player's success chance in Bladeburner contracts/operations by 10%.",
prereqs:[AugmentationNames.BladeArmorUnibeam], prereqs:[AugmentationNames.BladeArmorUnibeam],
bladeburner_success_chance_mult: 1.1, bladeburner_success_chance_mult: 1.1,
isSpecial: true,
}); });
BladeArmorOmnibeam.addToFactions([BladeburnersFactionName]); BladeArmorOmnibeam.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorOmnibeam); resetAugmentation(BladeArmorOmnibeam);
@ -1951,6 +1953,7 @@ function initAugmentations() {
prereqs:[AugmentationNames.BladeArmor], prereqs:[AugmentationNames.BladeArmor],
bladeburner_analysis_mult: 1.15, bladeburner_analysis_mult: 1.15,
bladeburner_success_chance_mult: 1.02, bladeburner_success_chance_mult: 1.02,
isSpecial: true,
}); });
BladeArmorIPU.addToFactions([BladeburnersFactionName]); BladeArmorIPU.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorIPU); resetAugmentation(BladeArmorIPU);
@ -1963,7 +1966,8 @@ function initAugmentations() {
"extremely large radius. These specially-modified holograms were specially " + "extremely large radius. These specially-modified holograms were specially " +
"weaponized by Bladeburner units to be used against Synthoids.<br><br>" + "weaponized by Bladeburner units to be used against Synthoids.<br><br>" +
"This augmentation allows you to perform Bladeburner actions and other " + "This augmentation allows you to perform Bladeburner actions and other " +
"actions (such as working, commiting crimes, etc.) at the same time." "actions (such as working, commiting crimes, etc.) at the same time.",
isSpecial: true,
}); });
BladesSimulacrum.addToFactions([BladeburnersFactionName]); BladesSimulacrum.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladesSimulacrum); resetAugmentation(BladesSimulacrum);

@ -57,8 +57,9 @@ export function initBitNodes() {
"For every Faction NOT listed above, reputation gains are halved<br>" + "For every Faction NOT listed above, reputation gains are halved<br>" +
"You will no longer gain passive reputation with Factions<br><br>" + "You will no longer gain passive reputation with Factions<br><br>" +
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " + "Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File increases the player's crime success rate, " + "upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
"crime money, and charisma multipliers by:<br><br>" + "once your karma decreases to a certain value. " +
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" + "Level 1: 24%<br>" +
"Level 2: 36%<br>" + "Level 2: 36%<br>" +
"Level 3: 42%"); "Level 3: 42%");

@ -6,7 +6,7 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.46.1", Version: "0.46.2",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -276,10 +276,25 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.46.2 v0.46.2
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
** (Karma is a hidden stat and is lowered by committing crimes)
* Gang changes: * Gang changes:
** Bug Fix: Gangs can no longer clash with themselve ** Bug Fix: Gangs can no longer clash with themselve
** Bug Fix: Winning against another gang should properly reduce their power ** Bug Fix: Winning against another gang should properly reduce their power
* Bug Fix: Terminal 'wget' command now works properly * Bug Fix: Terminal 'wget' command now works properly
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
* Bug Fix: Fixed button for creating Corporations
v0.46.1
* Added a very rudimentary directory system to the Terminal
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
* 'Generate Coding Contract' hash upgrade is now more expensive
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
* The cost of selling hashes for money no longer increases each time
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
` `
} }

@ -40,7 +40,6 @@ const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const tonsPP = 1e27; const tonsPP = 1e27;
const tonsP = 1e12; const tonsP = 1e12;
class ValueAdjusterComponent extends Component { class ValueAdjusterComponent extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -48,7 +47,7 @@ class ValueAdjusterComponent extends Component {
this.setValue = this.setValue.bind(this); this.setValue = this.setValue.bind(this);
} }
setValue(event) { setValue(event) {
this.setState({ value: event.target.value }); this.setState({ value: parseFloat(event.target.value) });
} }
render() { render() {
const { title, add, subtract, reset } = this.props; const { title, add, subtract, reset } = this.props;
@ -131,7 +130,6 @@ class DevMenuComponent extends Component {
this.setState({ codingcontract: event.target.value }); this.setState({ codingcontract: event.target.value });
} }
addMoney(n) { addMoney(n) {
return function() { return function() {
Player.gainMoney(n); Player.gainMoney(n);
@ -193,6 +191,12 @@ class DevMenuComponent extends Component {
} }
} }
modifyKarma(modifier) {
return function(amt) {
Player.karma += (amt * modifier);
}
}
tonsOfExp() { tonsOfExp() {
Player.gainHackingExp(tonsPP); Player.gainHackingExp(tonsPP);
Player.gainStrengthExp(tonsPP); Player.gainStrengthExp(tonsPP);
@ -244,6 +248,12 @@ class DevMenuComponent extends Component {
} }
} }
resetKarma() {
return function() {
Player.karma = 0;
}
}
enableIntelligence() { enableIntelligence() {
if(Player.intelligence === 0) { if(Player.intelligence === 0) {
Player.intelligence = 1; Player.intelligence = 1;
@ -812,6 +822,19 @@ class DevMenuComponent extends Component {
<button className="std-button" onClick={this.disableIntelligence}>Disable</button> <button className="std-button" onClick={this.disableIntelligence}>Disable</button>
</td> </td>
</tr> </tr>
<tr>
<td>
<span className="text text-center">Karma:</span>
</td>
<td>
<ValueAdjusterComponent
title="karma"
add={this.modifyKarma(1)}
subtract={this.modifyKarma(-1)}
reset={this.resetKarma()}
/>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

@ -97,16 +97,6 @@ export class Faction {
return [favorGain, rep]; return [favorGain, rep];
} }
//Adds all Augmentations to this faction.
addAllAugmentations(augs: object): void {
this.augmentations.length = 0;
for (const name in augs) {
if (augs.hasOwnProperty(name)) {
this.augmentations.push(name);
}
}
}
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */

7
src/Faction/FactionHelpers.d.ts vendored Normal file

@ -0,0 +1,7 @@
import { Augmentation } from "../Augmentation/Augmentation";
import { Faction } from "../Faction/Faction";
export declare function getNextNeurofluxLevel(): number;
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
export declare function purchaseAugmentationBoxCreate(aug: Augmentation, fac: Faction): void;
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;

@ -1,694 +0,0 @@
import { Augmentations } from "../Augmentation/Augmentations";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { FactionInfos } from "./FactionInfo";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player";
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { createSleevePurchasesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "../ui/numeralFormat";
import {dialogBoxCreate} from "../../utils/DialogBox";
import {factionInvitationBoxCreate} from "../../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../../utils/uiHelpers/createElement";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../../utils/JSONReviver";
import {formatNumber} from "../../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../../utils/YesNoBox";
function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
} else {
factionInvitationBoxCreate(faction);
}
}
function joinFaction(faction) {
faction.isMember = true;
Player.factions.push(faction.name);
const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction
for(const i in factionInfo.enemies) {
const enemy = factionInfo.enemies[i];
if (Factions[enemy] instanceof Faction) {
Factions[enemy].isBanned = true;
}
}
}
//Displays the HTML content for a specific faction
function displayFactionContent(factionName) {
var faction = Factions[factionName];
if (faction == null) {
throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
}
if (!faction.isMember) {
throw new Error("Not a member of this faction, cannot display faction information");
}
var factionInfo = faction.getInfo();
removeChildrenFromElement(Engine.Display.factionContent);
var elements = [];
//Header and faction info
elements.push(createElement("h1", {
innerText:factionName
}));
elements.push(createElement("pre", {
innerHTML:"<i>" + factionInfo.infoText + "</i>"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
//Faction reputation and favor
var favorGain = faction.getFavorGain();
if (favorGain.length != 2) {favorGain = 0;}
favorGain = favorGain[0];
elements.push(createElement("p", {
innerText: "Reputation: " + formatNumber(faction.playerReputation, 4),
tooltip:"You will earn " + formatNumber(favorGain, 0) +
" faction favor upon resetting after installing an Augmentation"
}))
elements.push(createElement("p", {
innerText:"---------------",
}));
elements.push(createElement("p", {
innerText:"Faction Favor: " + formatNumber(faction.favor, 0),
tooltip:"Faction favor increases the rate at which " +
"you earn reputation for this faction by 1% per favor. Faction favor " +
"is gained whenever you reset after installing an Augmentation. The amount of " +
"favor you gain depends on how much reputation you have with the faction"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
//Faction Work Description Text
elements.push(createElement("pre", {
id:"faction-work-description-text",
innerText:"Perform work/carry out assignments for your faction to help further its cause! By doing so " +
"you will earn reputation for your faction. You will also gain reputation passively over time, " +
"although at a very slow rate. Earning reputation will allow you to purchase Augmentations " +
"through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot " +
"use your terminal or create scripts when you are performing a task!"
}));
elements.push(createElement("br"));
//Hacking Mission Option
var hackMissionDiv = createElement("div", { class:"faction-work-div" });
var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackMissionDiv.appendChild(hackMissionDivWrapper);
hackMissionDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Hacking Mission",
clickListener:()=>{
Engine.loadMissionContent();
var mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
return false;
}
}));
hackMissionDivWrapper.appendChild(createElement("p", {
innerText:"Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)"
}));
elements.push(hackMissionDiv);
//Hacking Contracts Option
var hackDiv = createElement("div", { class:"faction-work-div", });
var hackDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackDiv.appendChild(hackDivWrapper);
hackDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Hacking Contracts",
clickListener:()=>{
Player.startFactionHackWork(faction);
return false;
}
}));
hackDivWrapper.appendChild(createElement("p", {
innerText:"Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your hacking skill. " +
"You will gain hacking exp."
}));
elements.push(hackDiv);
//Field Work Option
var fieldWorkDiv = createElement("div", { class:"faction-work-div" });
var fieldWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
fieldWorkDiv.appendChild(fieldWorkDivWrapper);
fieldWorkDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Field Work",
clickListener:()=>{
Player.startFactionFieldWork(faction);
return false;
}
}));
fieldWorkDivWrapper.appendChild(createElement("p", {
innerText:"Carry out field missions for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on all of your stats. " +
"You will gain exp for all stats."
}));
elements.push(fieldWorkDiv);
//Security Work Option
var securityWorkDiv = createElement("div", { class:"faction-work-div" });
var securityWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
securityWorkDiv.appendChild(securityWorkDivWrapper);
securityWorkDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Security Work",
clickListener:()=>{
Player.startFactionSecurityWork(faction);
return false;
}
}));
securityWorkDivWrapper.appendChild(createElement("p", {
innerText:"Serve in a security detail for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your combat stats. " +
"You will gain exp for all combat stats."
}));
elements.push(securityWorkDiv);
//Donate for reputation
var donateDiv = createElement("div", { class:"faction-work-div" });
var donateDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
donateDiv.appendChild(donateDivWrapper);
var donateRepGain = createElement("p", {
innerText:"This donation will result in 0.000 reputation gain"
});
var donateAmountInput = createElement("input", {
class: "text-input", placeholder:"Donation amount",
inputListener:()=>{
let amt = 0;
if(donateAmountInput.value !== "") {
amt = parseFloat(donateAmountInput.value);
}
if (isNaN(amt)) {
donateRepGain.innerText = "Invalid donate amount entered!";
} else {
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
donateRepGain.innerText = "This donation will result in " +
formatNumber(repGain, 3) + " reputation gain";
}
},
});
donateDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Donate Money",
clickListener:()=>{
var amt = parseFloat(donateAmountInput.value);
if (isNaN(amt) || amt < 0) {
dialogBoxCreate("Invalid amount entered!");
} else if (Player.money.lt(amt)) {
dialogBoxCreate("You cannot afford to donate this much money!");
} else {
Player.loseMoney(amt);
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain;
dialogBoxCreate("You just donated " + numeralWrapper.format(amt, "$0.000a") + " to " +
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
displayFactionContent(factionName);
}
}
}));
donateDivWrapper.appendChild(donateAmountInput);
donateDivWrapper.appendChild(donateRepGain);
elements.push(donateDiv);
//Purchase Augmentations
const purchaseAugmentationsDiv = createElement("div", { class: "faction-work-div", display: "inline" });
const purchaseAugmentationsDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
purchaseAugmentationsDiv.appendChild(purchaseAugmentationsDivWrapper);
purchaseAugmentationsDivWrapper.appendChild(createElement("a", {
class:"std-button",
innerText:"Purchase Augmentations",
margin: "5px",
clickListener:()=>{
Engine.hideAllContent();
Engine.Display.factionAugmentationsContent.style.display = "block";
displayFactionAugmentations(factionName);
return false;
}
}));
purchaseAugmentationsDivWrapper.appendChild(createElement("pre", {
innerHTML: "<br>As your reputation with this faction rises, you will " +
"unlock Augmentations, which you can purchase to enhance " +
"your abilities.<br><br>"
}));
elements.push(purchaseAugmentationsDiv);
//Gang (BitNode-2)
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
factionName == "NiteSec" || factionName == "The Black Hand")) {
//Set everything else to invisible
hackMissionDiv.style.display = "none";
hackDiv.style.display = "none";
fieldWorkDiv.style.display = "none";
securityWorkDiv.style.display = "none";
donateDiv.style.display = "none";
//Create the 'Manage Gang' button
var gangDiv = createElement("div", {
id:"faction-gang-div", class:"faction-work-div", display:"inline"
});
var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
gangDiv.appendChild(gangDivWrapper);
gangDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Manage Gang",
clickListener: () => {
if (!Player.inGang()) {
// Determine whether this is a hacking gang
let hacking = false;
if (factionName === "NiteSec" || factionName === "The Black Hand") { hacking = true; }
// Configure Yes/No buttons for the pop-up
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Create Gang";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", () => {
Player.startGang(factionName, hacking);
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
Engine.loadGangContent();
yesNoBoxClose();
});
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
// Pop-up text
let gangTypeText = "";
if (hacking) {
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.<br><br>";
} else {
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
}
yesNoBoxCreate(`Would you like to create a new Gang with ${factionName}?<br><br>` +
"Note that this will prevent you from creating a Gang with any other Faction until " +
"this BitNode is destroyed.<br><br>" +
gangTypeText +
"Other than hacking vs combat, there are NO differences between the Factions you can " +
"create a Gang with, and each of these Factions have all Augmentations available.");
} else {
Engine.loadGangContent();
}
}
}));
gangDivWrapper.appendChild(createElement("p", {
innerText:"Create and manage a gang for this Faction. " +
"Gangs will earn you money and faction reputation."
}));
//Manage Gang button goes before Faction work stuff
elements.splice(7, 1, gangDiv);
if (Player.inGang() && Player.gang.facName != factionName) {
//If the player has a gang but its not for this faction
gangDiv.style.display = "none";
}
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
}
return;
}
// Purchase Sleeves from Covenant
if (factionName === "The Covenant" && Player.bitNodeN >= 10 && SourceFileFlags[10]) {
const covenantPurchaseSleevesDiv = createElement("div", { class: "faction-work-div", display: "inline" });
const covenantPurchaseSleevesDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
covenantPurchaseSleevesDiv.appendChild(covenantPurchaseSleevesDivWrapper);
covenantPurchaseSleevesDivWrapper.appendChild(createElement("a", {
class: "std-button",
innerText: "Purchase Duplicate Sleeves",
clickListener: () => {
createSleevePurchasesFromCovenantPopup(Player);
}
}));
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
innerText: "Purchase Duplicate Sleeves. These are permanent! You can purchase up to 5 total.",
}));
elements.push(covenantPurchaseSleevesDiv);
}
// Determine if actions should be possible
donateDiv.style.display = faction.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none";
hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none";
hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none";
fieldWorkDiv.style.display = factionInfo.offerFieldWork ? "inline" : "none";
securityWorkDiv.style.display = factionInfo.offerSecurityWork ? "inline" : "none";
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
}
}
function displayFactionAugmentations(factionName) {
var faction = Factions[factionName];
if (faction == null) {
throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
}
removeChildrenFromElement(Engine.Display.factionAugmentationsContent);
var elements = [];
//Back button
elements.push(createElement("a", {
innerText:"Back", class:"a-link-button",
clickListener:()=>{
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
}
}));
//Header text
elements.push(createElement("h1", {innerText:"Faction Augmentations"}));
elements.push(createElement("p", {
id:"faction-augmentations-page-desc",
innerHTML:"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
"Augmentations are powerful upgrades that will enhance your abilities."
}));
elements.push(createElement("br"));
elements.push(createElement("br"));
//Augmentations List
var augmentationsList = createElement("ul");
//Sort buttons
const sortByCostBtn = createElement("a", {
innerText:"Sort by Cost", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Cost;
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseCost - aug2.baseCost;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
const sortByRepBtn = createElement("a", {
innerText:"Sort by Reputation", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Reputation;
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseRepRequirement - aug2.baseRepRequirement;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
const defaultSortBtn = createElement("a", {
innerText:"Sort by Default Order", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Default;
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, faction.augmentations, faction);
}
});
elements.push(sortByCostBtn);
elements.push(sortByRepBtn);
elements.push(defaultSortBtn);
switch(Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost:
sortByCostBtn.click();
break;
case PurchaseAugmentationsOrderSetting.Reputation:
sortByRepBtn.click();
break;
default:
defaultSortBtn.click();
break;
}
elements.push(augmentationsList);
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionAugmentationsContent.appendChild(elements[i]);
}
}
//Takes in an array of Augmentation Names, constructs DOM elements
//to list them on the faction page, and appends them to the given
//DOM element
// @augmentationsList DOM List to append Aug DOM elements to
// @augs Array of Aug names
// @faction Faction for which to display Augmentations
function createFactionAugmentationDisplayElements(augmentationsList, augs, faction) {
const factionInfo = faction.getInfo();
for (var i = 0; i < augs.length; ++i) {
(function () {
var aug = Augmentations[augs[i]];
if (aug == null) {
throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
}
var owned = false;
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name == aug.name) {
owned = true;
break;
}
}
for (var j = 0; j < Player.augmentations.length; ++j) {
if (Player.augmentations[j].name == aug.name) {
owned = true;
break;
}
}
var item = createElement("li");
var span = createElement("span", { display:"inline-block", margin: "4px", padding: "4px" });
var aDiv = createElement("div", {tooltip:aug.info});
var aElem = createElement("a", {
innerText:aug.name, display:"inline",
clickListener:()=>{
if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(aug, faction);
} else {
purchaseAugmentation(aug, faction);
}
return false;
}
});
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
aElem.innerText += " - Level " + (getNextNeurofluxLevel());
}
var pElem = createElement("p", {
display:"inline",
})
var req = aug.baseRepRequirement * factionInfo.augmentationRepRequirementMult;
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + aug.prereqs.join(",") + " as prerequisite(s))";
pElem.style.color = "red";
} else if (aug.name != AugmentationNames.NeuroFluxGovernor && (aug.owned || owned)) {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button");
pElem.innerHTML = "UNLOCKED - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
} else {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
pElem.style.color = "red";
}
aDiv.appendChild(aElem);
span.appendChild(aDiv);
span.appendChild(pElem);
item.appendChild(span);
augmentationsList.appendChild(item);
}()); //Immediate invocation closure
}
}
function purchaseAugmentationBoxCreate(aug, fac) {
const factionInfo = fac.getInfo();
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Purchase";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function() {
purchaseAugmentation(aug, fac);
});
noBtn.addEventListener("click", function() {
yesNoBoxClose();
});
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
aug.info + "<br><br>" +
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
}
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
function hasAugmentationPrereqs(aug) {
var hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) {
for (var i = 0; i < aug.prereqs.length; ++i) {
var prereqAug = Augmentations[aug.prereqs[i]];
if (prereqAug == null) {
console.log("ERROR: Invalid prereq Augmentation: " + aug.prereqs[i]);
continue;
}
if (prereqAug.owned === false) {
hasPrereqs = false;
//Check if the aug is purchased
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === prereqAug.name) {
hasPrereqs = true;
break;
}
}
}
}
}
return hasPrereqs;
}
function purchaseAugmentation(aug, fac, sing=false) {
const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
"purchase this one.";
if (sing) {return txt;} else {dialogBoxCreate(txt);}
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let txt = "You don't have enough money to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (fac.playerReputation < aug.baseRepRequirement) {
let txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
if (Player.firstAugPurchased === false) {
Player.firstAugPurchased = true;
document.getElementById("augmentations-tab").style.display = "list-item";
document.getElementById("character-menu-header").click();
document.getElementById("character-menu-header").click();
}
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel();
}
Player.queuedAugmentations.push(queuedAugmentation);
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
//If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
var nextLevel = getNextNeurofluxLevel();
--nextLevel;
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
for (var name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
if (sing) {
return "You purchased " + aug.name;
} else {
if(!Settings.SuppressBuyAugmentationConfirmation){
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.");
}
}
displayFactionAugmentations(fac.name);
} else {
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
"Please report this to the game developer with an explanation of how to " +
"reproduce this.");
}
yesNoBoxClose();
}
function getNextNeurofluxLevel() {
// Get current Neuroflux level based on Player's augmentations
let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}
return currLevel + 1;
}
function processPassiveFactionRepGain(numCycles) {
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
for (var name in Factions) {
if (Factions.hasOwnProperty(name)) {
var faction = Factions[name];
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
//maybe later make this based on
//a player's 'status' like how powerful they are and how much money they have
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
}
}
}
export {getNextNeurofluxLevel, inviteToFaction,
joinFaction, displayFactionContent, processPassiveFactionRepGain,
purchaseAugmentation};

@ -0,0 +1,240 @@
import React from "react";
import ReactDOM from "react-dom";
import { FactionRoot } from "./ui/Root";
import { Augmentations } from "../Augmentation/Augmentations";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player";
import { Settings } from "../Settings/Settings";
import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { factionInvitationBoxCreate } from "../../utils/FactionInvitationBox";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions";
import {
yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton,
yesNoBoxClose
} from "../../utils/YesNoBox";
export function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
} else {
factionInvitationBoxCreate(faction);
}
}
export function joinFaction(faction) {
faction.isMember = true;
Player.factions.push(faction.name);
const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction
for(const i in factionInfo.enemies) {
const enemy = factionInfo.enemies[i];
if (Factions[enemy] instanceof Faction) {
Factions[enemy].isBanned = true;
}
}
}
export function startHackingMission(faction) {
const mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
}
//Displays the HTML content for a specific faction
export function displayFactionContent(factionName, initiallyOnAugmentationsPage=false) {
const faction = Factions[factionName];
if (faction == null) {
throw new Error(`Invalid factionName passed into displayFactionContent(): ${factionName}`);
}
if (!faction.isMember) {
throw new Error(`Not a member of this faction. Cannot display faction information`);
}
ReactDOM.render(
<FactionRoot
engine={Engine}
initiallyOnAugmentationsPage={initiallyOnAugmentationsPage}
faction={faction}
p={Player}
startHackingMissionFn={startHackingMission}
/>,
Engine.Display.factionContent
)
}
export function purchaseAugmentationBoxCreate(aug, fac) {
const factionInfo = fac.getInfo();
const yesBtn = yesNoBoxGetYesButton();
yesBtn.innerHTML = "Purchase";
yesBtn.addEventListener("click", function() {
purchaseAugmentation(aug, fac);
});
const noBtn = yesNoBoxGetNoButton();
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", function() {
yesNoBoxClose();
});
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
aug.info + "<br><br>" +
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
}
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
export function hasAugmentationPrereqs(aug) {
let hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) {
for (let i = 0; i < aug.prereqs.length; ++i) {
const prereqAug = Augmentations[aug.prereqs[i]];
if (prereqAug == null) {
console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
continue;
}
if (prereqAug.owned === false) {
hasPrereqs = false;
// Check if the aug is purchased
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === prereqAug.name) {
hasPrereqs = true;
break;
}
}
}
}
}
return hasPrereqs;
}
export function purchaseAugmentation(aug, fac, sing=false) {
const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
"purchase this one.";
if (sing) {return txt;} else {dialogBoxCreate(txt);}
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let txt = "You don't have enough money to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (fac.playerReputation < aug.baseRepRequirement) {
let txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
if (Player.firstAugPurchased === false) {
Player.firstAugPurchased = true;
document.getElementById("augmentations-tab").style.display = "list-item";
document.getElementById("character-menu-header").click();
document.getElementById("character-menu-header").click();
}
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel();
}
Player.queuedAugmentations.push(queuedAugmentation);
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
// If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
var nextLevel = getNextNeurofluxLevel();
--nextLevel;
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
for (var name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
if (sing) {
return "You purchased " + aug.name;
} else {
if(!Settings.SuppressBuyAugmentationConfirmation){
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.");
}
}
// Force a rerender of the Augmentations page
displayFactionContent(fac.name, true);
} else {
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
"Please report this to the game developer with an explanation of how to " +
"reproduce this.");
}
yesNoBoxClose();
}
export function getNextNeurofluxLevel() {
// Get current Neuroflux level based on Player's augmentations
let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}
return currLevel + 1;
}
export function processPassiveFactionRepGain(numCycles) {
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
for (var name in Factions) {
if (Factions.hasOwnProperty(name)) {
var faction = Factions[name];
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
//maybe later make this based on
//a player's 'status' like how powerful they are and how much money they have
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
}
}
}

@ -0,0 +1,163 @@
/**
* Root React Component for displaying a faction's "Purchase Augmentations" page
*/
import * as React from "react";
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
faction: Faction;
p: IPlayer;
routeToMainPage: () => void;
}
type IState = {
rerenderFlag: boolean;
sortOrder: PurchaseAugmentationsOrderSetting;
}
const infoStyleMarkup = {
width: "70%",
}
export class AugmentationsPage extends React.Component<IProps, IState> {
// Flag for whether the player has a gang with this faction
isPlayersGang: boolean;
constructor(props: IProps) {
super(props);
this.isPlayersGang = props.p.inGang() && (props.p.getGangName() === props.faction.name);
this.state = {
rerenderFlag: false,
sortOrder: PurchaseAugmentationsOrderSetting.Default,
}
this.rerender = this.rerender.bind(this);
}
getAugs() {
if (this.isPlayersGang) {
const augs: string[] = [];
for (const augName in Augmentations) {
const aug = Augmentations[augName];
if (!aug.isSpecial) {
augs.push(augName);
}
}
return augs;
} else {
return this.props.faction.augmentations.slice();
}
}
getAugsSorted() {
switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: {
return this.getAugsSortedByCost();
}
case PurchaseAugmentationsOrderSetting.Reputation: {
return this.getAugsSortedByReputation();
}
default:
return this.getAugsSortedByDefault();
}
}
getAugsSortedByCost() {
const augs = this.getAugs();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseCost - aug2.baseCost;
});
return augs;
}
getAugsSortedByReputation() {
const augs = this.getAugs();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseRepRequirement - aug2.baseRepRequirement;
});
return augs;
}
getAugsSortedByDefault() {
return this.getAugs();
}
switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting) {
Settings.PurchaseAugmentationsOrder = newOrder;
this.rerender();
}
rerender() {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
}
});
}
render() {
const augs = this.getAugsSorted();
const augList = augs.map((aug) => {
return (
<PurchaseableAugmentation
augName={aug}
faction={this.props.faction}
key={aug}
p={this.props.p}
rerender={this.rerender}
/>
)
});
return (
<div>
<StdButton
onClick={this.props.routeToMainPage}
text={"Back"}
/>
<h1>Faction Augmentations</h1>
<p style={infoStyleMarkup}>
These are all of the Augmentations that are available to purchase
from {this.props.faction.name}. Augmentations are powerful upgrades
that will enhance your abilities.
</p>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}
text={"Sort by Cost"}
/>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
text={"Sort by Reputation"}
/>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
text={"Sort by Default Order"}
/>
<br />
{augList}
</div>
)
}
}

@ -0,0 +1,101 @@
/**
* React component for a donate option on the Faction UI
*/
import * as React from "react";
import { CONSTANTS } from "../../Constants";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = {
faction: Faction;
p: IPlayer;
rerender: () => void;
}
type IState = {
donateAmt: number;
statusTxt: string;
}
const inputStyleMarkup = {
margin: "5px",
}
export class DonateOption extends React.Component<IProps, IState> {
// Style markup for block elements. Stored as property
blockStyle: object = { display: "block" };
constructor(props: IProps) {
super(props);
this.state = {
donateAmt: 0,
statusTxt: "",
}
this.calculateRepGain = this.calculateRepGain.bind(this);
this.donate = this.donate.bind(this);
this.handleChange = this.handleChange.bind(this);
}
// Returns rep gain for the current donation amount
calculateRepGain(): number {
return this.state.donateAmt / CONSTANTS.DonateMoneyToRepDivisor * this.props.p.faction_rep_mult;
}
donate(): void {
const fac = this.props.faction;
const amt = this.state.donateAmt;
if (isNaN(amt) || amt <= 0) {
dialogBoxCreate(`Invalid amount entered!`);
} else if (!this.props.p.canAfford(amt)) {
dialogBoxCreate(`You cannot afford to donate this much money!`);
} else {
this.props.p.loseMoney(amt);
const repGain = this.calculateRepGain();
this.props.faction.playerReputation += repGain;
dialogBoxCreate(`You just donated ${numeralWrapper.formatMoney(amt)} to ${fac.name} to gain ` +
`${numeralWrapper.format(repGain, "0,0.000")} reputation`);
this.props.rerender();
}
}
handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
const amt = parseFloat(e.target.value);
if (isNaN(amt)) {
this.setState({
donateAmt: 0,
statusTxt: "Invalid donate amount entered!",
});
} else {
const repGain = this.calculateRepGain();
this.setState({
donateAmt: amt,
statusTxt: `This donation will result in ${numeralWrapper.format(repGain, "0,0.000")} reputation gain`,
})
}
}
render() {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<input onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
<StdButton
onClick={this.donate}
text={"Donate Money"}
/>
<p style={this.blockStyle}>{this.state.statusTxt}</p>
</div>
</div>
)
}
}

72
src/Faction/ui/Info.tsx Normal file

@ -0,0 +1,72 @@
/**
* React component for general information about the faction. This includes the
* factions "motto", reputation, favor, and gameplay instructions
*/
import * as React from "react";
import { Faction } from "../../Faction/Faction";
import { FactionInfo } from "../../Faction/FactionInfo";
import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph";
import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip";
type IProps = {
faction: Faction;
factionInfo: FactionInfo;
}
type IInnerHTMLMarkup = {
__html: string;
}
const blockStyleMarkup = {
display: "block",
}
const infoStyleMarkup = {
display: "block",
width: "70%",
}
export class Info extends React.Component<IProps, any> {
render() {
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
const favorGain = this.props.faction.getFavorGain()[0];
const favorTooltip = "Faction favor increases the rate at which you earn reputation for " +
"this faction by 1% per favor. Faction favor is gained whenever you " +
"reset after installing an Augmentation. The amount of " +
"favor you gain depends on how much reputation you have with the faction"
const infoText: IInnerHTMLMarkup = {
__html: this.props.factionInfo.infoText,
}
return (
<div>
<i className={"text"} style={infoStyleMarkup} dangerouslySetInnerHTML={infoText}></i>
<p style={blockStyleMarkup}>-------------------------</p>
<AutoupdatingParagraph
intervalTime={5e3}
text={`Reputation: ${formattedRep}`}
tooltip={`You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`}
/>
<p style={blockStyleMarkup}>-------------------------</p>
<ParagraphWithTooltip
text={`Faction Favor: ${numeralWrapper.format(this.props.faction.favor, "0,0")}`}
tooltip={favorTooltip}
/>
<p style={blockStyleMarkup}>-------------------------</p>
<p style={infoStyleMarkup}>
Perform work/carry out assignments for your faction to help further its cause!
By doing so you will earn reputation for your faction. You will also gain
reputation passively over time, although at a very slow rate. Earning
reputation will allow you to purchase Augmentations through this faction, which
are powerful upgrades that enhance your abilities. Note that you cannot use your
terminal or create scripts when you are performing a task!
</p>
</div>
)
}
}

30
src/Faction/ui/Option.tsx Normal file

@ -0,0 +1,30 @@
/**
* React component for a selectable option on the Faction UI. These
* options including working for the faction, hacking missions, purchasing
* augmentations, etc.
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
buttonText: string;
infoText: string;
onClick: (e: React.MouseEvent<HTMLElement>) => void;
}
export class Option extends React.Component<IProps, any> {
render() {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<StdButton
onClick={this.props.onClick}
text={this.props.buttonText}
/>
<p>{this.props.infoText}</p>
</div>
</div>
)
}
}

@ -0,0 +1,151 @@
/**
* React component for displaying a single augmentation for purchase through
* the faction UI
*/
import * as React from "react";
import {
getNextNeurofluxLevel,
hasAugmentationPrereqs,
purchaseAugmentation,
purchaseAugmentationBoxCreate,
} from "../FactionHelpers";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat";
import { IMap } from "../../types";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
augName: string;
faction: Faction;
p: IPlayer;
rerender: () => void;
}
const spanStyleMarkup = {
margin: "4px",
padding: "4px",
}
const inlineStyleMarkup = {
display: "inline-block",
}
export class PurchaseableAugmentation extends React.Component<IProps, any> {
aug: Augmentation | null;
constructor(props: IProps) {
super(props);
this.aug = Augmentations[this.props.augName];
this.handleClick = this.handleClick.bind(this);
}
getMoneyCost(): number {
return this.aug!.baseCost * this.props.faction.getInfo().augmentationPriceMult;
}
getRepCost(): number {
return this.aug!.baseRepRequirement * this.props.faction.getInfo().augmentationRepRequirementMult;
}
handleClick() {
if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(this.aug!, this.props.faction);
} else {
purchaseAugmentation(this.aug!, this.props.faction);
}
}
// Whether the player has the prerequisite Augmentations
hasPrereqs(): boolean {
return hasAugmentationPrereqs(this.aug!);
}
// Whether the player has enough rep for this Augmentation
hasReputation(): boolean {
return this.props.faction.playerReputation >= this.getRepCost();
}
// Whether the player has this augmentations (purchased OR installed)
owned(): boolean {
let owned = false;
for (const queuedAug of this.props.p.queuedAugmentations) {
if (queuedAug.name === this.props.augName) {
owned = true;
break;
}
}
for (const installedAug of this.props.p.augmentations) {
if (installedAug.name === this.props.augName) {
owned = true;
break;
}
}
return owned;
}
render() {
if (this.aug == null) {
console.error(`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${this.props.augName}`);
return null;
}
const moneyCost = this.getMoneyCost();
const repCost = this.getRepCost();
// Determine UI properties
let disabled: boolean = false;
let statusTxt: string = "";
let color: string = "";
if (!this.hasPrereqs()) {
disabled = true;
statusTxt = `LOCKED (Requires ${this.aug.prereqs.join(",")} as prerequisite(s))`;
color = "red";
} else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) {
disabled = true;
statusTxt = "ALREADY OWNED";
} else if (this.hasReputation()) {
statusTxt = `UNLOCKED - ${numeralWrapper.formatMoney(moneyCost)}`;
} else {
disabled = true;
statusTxt = `LOCKED (Requires ${numeralWrapper.format(repCost, "0,0.0")} faction reputation - ${numeralWrapper.formatMoney(moneyCost)})`;
color = "red";
}
const txtStyle: IMap<string> = {
display: "inline-block",
}
if (color !== "") { txtStyle.color = color; }
// Determine button txt
let btnTxt = this.aug.name;
if (this.aug.name === AugmentationNames.NeuroFluxGovernor) {
btnTxt += ` - Level ${getNextNeurofluxLevel()}`;
}
return (
<li>
<span style={spanStyleMarkup}>
<StdButton
disabled={disabled}
onClick={this.handleClick}
style={inlineStyleMarkup}
text={btnTxt}
/>
<p style={txtStyle}>{statusTxt}</p>
</span>
</li>
)
}
}

298
src/Faction/ui/Root.tsx Normal file

@ -0,0 +1,298 @@
/**
* Root React Component for displaying a Faction's UI.
* This is the component for displaying a single faction's UI, not the list of all
* accessible factions
*/
import * as React from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption";
import { Info } from "./Info";
import { Option } from "./Option";
import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import {
yesNoBoxClose,
yesNoBoxCreate,
yesNoBoxGetNoButton,
yesNoBoxGetYesButton,
} from "../../../utils/YesNoBox";
type IProps = {
engine: IEngine;
initiallyOnAugmentationsPage?: boolean;
faction: Faction;
p: IPlayer;
startHackingMissionFn: (faction: Faction) => void;
}
type IState = {
rerenderFlag: boolean;
purchasingAugs: boolean;
}
// Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " +
"faction reputation";
const hackingMissionInfo = "Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)";
const hackingContractsInfo = "Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your hacking skill. " +
"You will gain hacking exp.";
const fieldWorkInfo = "Carry out field missions for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on all of your stats. " +
"You will gain exp for all stats.";
const securityWorkInfo = "Serve in a security detail for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your combat stats. " +
"You will gain exp for all combat stats.";
const augmentationsInfo = "As your reputation with this faction rises, you will " +
"unlock Augmentations, which you can purchase to enhance " +
"your abilities.";
const sleevePurchasesInfo = "Purchase Duplicate Sleeves and upgrades. These are permanent!";
const GangNames = [
"Slum Snakes",
"Tetrads",
"The Syndicate",
"The Dark Army",
"Speakers for the Dead",
"NiteSec",
"The Black Hand"
];
export class FactionRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
purchasingAugs: props.initiallyOnAugmentationsPage ? props.initiallyOnAugmentationsPage : false,
}
this.manageGang = this.manageGang.bind(this);
this.rerender = this.rerender.bind(this);
this.routeToMain = this.routeToMain.bind(this);
this.routeToPurchaseAugs = this.routeToPurchaseAugs.bind(this);
this.sleevePurchases = this.sleevePurchases.bind(this);
this.startFieldWork = this.startFieldWork.bind(this);
this.startHackingContracts = this.startHackingContracts.bind(this);
this.startHackingMission = this.startHackingMission.bind(this);
this.startSecurityWork = this.startSecurityWork.bind(this);
}
manageGang() {
// If player already has a gang, just go to the gang UI
if (this.props.p.inGang()) {
return this.props.engine.loadGangContent();
}
// Otherwise, we have to create the gang
const facName = this.props.faction.name;
let isHacking = false;
if (facName === "NiteSec" || facName === "The Black Hand") {
isHacking = true;
}
// A Yes/No popup box will allow the player to confirm gang creation
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) { return; }
yesBtn.innerHTML = "Create Gang";
yesBtn.addEventListener("click", () => {
this.props.p.startGang(facName, isHacking);
const worldMenuHeader = document.getElementById("world-menu-header");
if (worldMenuHeader instanceof HTMLElement) {
worldMenuHeader.click(); worldMenuHeader.click();
}
this.props.engine.loadGangContent();
yesNoBoxClose();
});
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
// Pop-up text
let gangTypeText = "";
if (isHacking) {
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.<br><br>";
} else {
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
}
yesNoBoxCreate(`Would you like to create a new Gang with ${facName}?<br><br>` +
"Note that this will prevent you from creating a Gang with any other Faction until " +
"this BitNode is destroyed. It also resets your reputation with this faction.<br><br>" +
gangTypeText +
"Other than hacking vs combat, there are NO differences between the Factions you can " +
"create a Gang with, and each of these Factions have all Augmentations available.");
}
rerender() {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
}
});
}
// Route to the main faction page
routeToMain() {
this.setState({ purchasingAugs: false });
}
// Route to the purchase augmentation UI for this faction
routeToPurchaseAugs() {
this.setState({ purchasingAugs: true });
}
sleevePurchases() {
createSleevePurchasesFromCovenantPopup(this.props.p);
}
startFieldWork() {
this.props.p.startFactionFieldWork(this.props.faction);
}
startHackingContracts() {
this.props.p.startFactionHackWork(this.props.faction);
}
startHackingMission() {
const fac = this.props.faction;
this.props.engine.loadMissionContent();
this.props.startHackingMissionFn(fac);
}
startSecurityWork() {
this.props.p.startFactionSecurityWork(this.props.faction);
}
render() {
return this.state.purchasingAugs ? this.renderAugmentationsPage() : this.renderMainPage();
}
renderMainPage() {
const p = this.props.p;
const faction = this.props.faction;
const factionInfo = faction.getInfo();
// We have a special flag for whether the player this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = p.inGang() && (p.getGangName() === faction.name);
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
// should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = (faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10]);
let canAccessGang = (p.canAccessGang() && GangNames.includes(faction.name));
if (p.inGang() && (p.getGangName() !== faction.name)) {
canAccessGang = false;
}
return (
<div>
<h1>{faction.name}</h1>
<Info
faction={faction}
factionInfo={factionInfo}
/>
{
canAccessGang &&
<Option
buttonText={"Manage Gang"}
infoText={gangInfo}
onClick={this.manageGang}
/>
}
{
(!isPlayersGang && factionInfo.offerHackingMission) &&
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={this.startHackingMission}
/>
}
{
(!isPlayersGang && factionInfo.offerHackingWork) &&
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={this.startHackingContracts}
/>
}
{
(!isPlayersGang && factionInfo.offerFieldWork) &&
<Option
buttonText={"Field Work"}
infoText={fieldWorkInfo}
onClick={this.startFieldWork}
/>
}
{
(!isPlayersGang && factionInfo.offerSecurityWork) &&
<Option
buttonText={"Security Work"}
infoText={securityWorkInfo}
onClick={this.startSecurityWork}
/>
}
{
(!isPlayersGang && canDonate) &&
<DonateOption
faction={this.props.faction}
p={this.props.p}
rerender={this.rerender}
/>
}
<Option
buttonText={"Purchase Augmentations"}
infoText={augmentationsInfo}
onClick={this.routeToPurchaseAugs}
/>
{
canPurchaseSleeves &&
<Option
buttonText={"Purchase Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={this.sleevePurchases}
/>
}
</div>
)
}
renderAugmentationsPage() {
return (
<div>
<AugmentationsPage
faction={this.props.faction}
p={this.props.p}
routeToMainPage={this.routeToMain}
/>
</div>
)
}
}

@ -3,8 +3,13 @@
* to TypeScript at the moment * to TypeScript at the moment
*/ */
export interface IEngine { export interface IEngine {
hideAllContent: () => void;
loadBladeburnerContent: () => void; loadBladeburnerContent: () => void;
loadFactionContent: () => void;
loadFactionsContent: () => void;
loadGangContent: () => void;
loadInfiltrationContent: () => void; loadInfiltrationContent: () => void;
loadMissionContent: () => void;
loadResleevingContent: () => void; loadResleevingContent: () => void;
loadStockMarketContent: () => void; loadStockMarketContent: () => void;
} }

@ -108,7 +108,7 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) {
* Create a popup that lets the player start a Corporation * Create a popup that lets the player start a Corporation
*/ */
export function createStartCorporationPopup(p: IPlayer) { export function createStartCorporationPopup(p: IPlayer) {
if (!p.canAccessCorporation() || p.hasCorporation) { return; } if (!p.canAccessCorporation() || p.hasCorporation()) { return; }
const popupId = "create-corporation-popup"; const popupId = "create-corporation-popup";
const txt = createElement("p", { const txt = createElement("p", {

@ -12,6 +12,7 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugment
import { Company } from "../Company/Company"; import { Company } from "../Company/Company";
import { CompanyPosition } from "../Company/CompanyPosition"; import { CompanyPosition } from "../Company/CompanyPosition";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
import { Faction } from "../Faction/Faction";
import { HashManager } from "../Hacknet/HashManager"; import { HashManager } from "../Hacknet/HashManager";
import { HacknetNode } from "../Hacknet/HacknetNode"; import { HacknetNode } from "../Hacknet/HacknetNode";
import { LocationName } from "../Locations/data/LocationNames"; import { LocationName } from "../Locations/data/LocationNames";
@ -114,6 +115,7 @@ export interface IPlayer {
applyForWaiterJob(sing?: boolean): boolean | void; applyForWaiterJob(sing?: boolean): boolean | void;
canAccessBladeburner(): boolean; canAccessBladeburner(): boolean;
canAccessCorporation(): boolean; canAccessCorporation(): boolean;
canAccessGang(): boolean;
canAccessResleeving(): boolean; canAccessResleeving(): boolean;
canAfford(cost: number): boolean; canAfford(cost: number): boolean;
gainHackingExp(exp: number): void; gainHackingExp(exp: number): void;
@ -124,6 +126,7 @@ export interface IPlayer {
gainCharismaExp(exp: number): void; gainCharismaExp(exp: number): void;
gainMoney(money: number): void; gainMoney(money: number): void;
getCurrentServer(): Server; getCurrentServer(): Server;
getGangName(): string;
getHomeComputer(): Server; getHomeComputer(): Server;
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition; getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition;
getUpgradeHomeRamCost(): number; getUpgradeHomeRamCost(): number;
@ -151,6 +154,10 @@ export interface IPlayer {
money: number, money: number,
time: number, time: number,
singParams: any): void; singParams: any): void;
startFactionFieldWork(faction: Faction): void;
startFactionHackWork(faction: Faction): void;
startFactionSecurityWork(faction: Faction): void;
startGang(facName: string, isHacking: boolean): void;
startWork(companyName: string): void; startWork(companyName: string): void;
startWorkPartTime(companyName: string): void; startWorkPartTime(companyName: string): void;
travel(to: CityName): boolean; travel(to: CityName): boolean;

@ -1,17 +1,20 @@
import * as generalMethods from "./PlayerObjectGeneralMethods"; import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
import * as serverMethods from "./PlayerObjectServerMethods"; import * as corporationMethods from "./PlayerObjectCorporationMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods"; import * as gangMethods from "./PlayerObjectGangMethods";
import * as corporationMethods from "./PlayerObjectCorporationMethods"; import * as generalMethods from "./PlayerObjectGeneralMethods";
import * as serverMethods from "./PlayerObjectServerMethods";
import { HashManager } from "../../Hacknet/HashManager"; import { HashManager } from "../../Hacknet/HashManager";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { Reviver, import {
Generic_toJSON, Reviver,
Generic_fromJSON } from "../../../utils/JSONReviver"; Generic_toJSON,
Generic_fromJSON
} from "../../../utils/JSONReviver";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
export function PlayerObject() { export function PlayerObject() {
//Skills and stats //Skills and stats
@ -199,7 +202,15 @@ export function PlayerObject() {
this.scriptProdSinceLastAug = 0; this.scriptProdSinceLastAug = 0;
}; };
Object.assign(PlayerObject.prototype, generalMethods, serverMethods, bladeburnerMethods, corporationMethods); // Apply player methods to the prototype using Object.assign()
Object.assign(
PlayerObject.prototype,
generalMethods,
serverMethods,
bladeburnerMethods,
corporationMethods,
gangMethods
);
PlayerObject.prototype.toJSON = function() { PlayerObject.prototype.toJSON = function() {
return Generic_toJSON("PlayerObject", this); return Generic_toJSON("PlayerObject", this);

@ -0,0 +1,34 @@
import { Factions } from "../../Faction/Factions";
import { Gang } from "../../Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
// Amount of negative karma needed to manage a gang in BitNodes other than 2
const GangKarmaRequirement = -54000;
export function canAccessGang() {
if (this.bitNodeN === 2) { return true; }
if (SourceFileFlags[2] <= 0) { return false; }
return (this.karma <= GangKarmaRequirement);
}
export function getGangName() {
return this.gang.facName;
}
export function inGang() {
if (this.gang == null || this.gang == undefined) { return false; }
return (this.gang instanceof Gang);
}
export function startGang(factionName, hacking) {
this.gang = new Gang(factionName, hacking);
const fac = Factions[factionName];
if (fac == null) {
throw new Error(`Invalid faction name when creating gang: ${factionName}`);
}
fac.playerReputation = 0;
}

@ -20,7 +20,7 @@ import {Engine} from "../../engine";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { displayFactionContent } from "../../Faction/FactionHelpers"; import { displayFactionContent } from "../../Faction/FactionHelpers";
import {Gang, resetGangs} from "../../Gang"; import { resetGangs } from "../../Gang";
import { hasHacknetServers } from "../../Hacknet/HacknetHelpers"; import { hasHacknetServers } from "../../Hacknet/HacknetHelpers";
import { HashManager } from "../../Hacknet/HashManager"; import { HashManager } from "../../Hacknet/HashManager";
import { Cities } from "../../Locations/Cities"; import { Cities } from "../../Locations/Cities";
@ -150,8 +150,9 @@ export function prestigeAugmentation() {
this.moneySourceA.reset(); this.moneySourceA.reset();
this.hacknetNodes.length = 0; this.hacknetNodes.length = 0;
this.hashManager.prestige(this);
//Re-calculate skills and reset HP // Re-calculate skills and reset HP
this.updateSkillLevels(); this.updateSkillLevels();
this.hp = this.max_hp; this.hp = this.max_hp;
} }
@ -239,18 +240,19 @@ export function prestigeSourceFile() {
this.lastUpdate = new Date().getTime(); this.lastUpdate = new Date().getTime();
this.hacknetNodes.length = 0; this.hacknetNodes.length = 0;
this.hashManager.prestige(this);
//Gang // Gang
this.gang = null; this.gang = null;
resetGangs(); resetGangs();
//Reset Stock market // Reset Stock market
this.hasWseAccount = false; this.hasWseAccount = false;
this.hasTixApiAccess = false; this.hasTixApiAccess = false;
this.has4SData = false; this.has4SData = false;
this.has4SDataTixApi = false; this.has4SDataTixApi = false;
//BitNode 3: Corporatocracy // BitNode 3: Corporatocracy
this.corporation = 0; this.corporation = 0;
// Statistics trackers // Statistics trackers
@ -2174,18 +2176,6 @@ export function checkForFactionInvitations() {
return invitedFactions; return invitedFactions;
} }
/*************** Gang ****************/
//Returns true if Player is in a gang and false otherwise
export function inGang() {
if (this.gang == null || this.gang == undefined) {return false;}
return (this.gang instanceof Gang);
}
export function startGang(factionName, hacking) {
this.gang = new Gang(factionName, hacking);
}
/************* BitNodes **************/ /************* BitNodes **************/
export function setBitNodeNumber(n) { export function setBitNodeNumber(n) {
this.bitNodeN = n; this.bitNodeN = n;

@ -138,9 +138,9 @@ function prestigeAugmentation() {
// Messages // Messages
initMessages(); initMessages();
// Gang, in BitNode 2 // Gang
if (Player.bitNodeN == 2 && Player.inGang()) { if (Player.inGang()) {
var faction = Factions[Player.gang.facName]; const faction = Factions[Player.gang.facName];
if (faction instanceof Faction) { if (faction instanceof Faction) {
joinFaction(faction); joinFaction(faction);
} }

@ -95,7 +95,7 @@ BitburnerSaveObject.prototype.getSaveString = function() {
this.SettingsSave = JSON.stringify(Settings); this.SettingsSave = JSON.stringify(Settings);
this.FconfSettingsSave = JSON.stringify(FconfSettings); this.FconfSettingsSave = JSON.stringify(FconfSettings);
this.VersionSave = JSON.stringify(CONSTANTS.Version); this.VersionSave = JSON.stringify(CONSTANTS.Version);
if (Player.bitNodeN == 2 && Player.inGang()) { if (Player.inGang()) {
this.AllGangsSave = JSON.stringify(AllGangs); this.AllGangsSave = JSON.stringify(AllGangs);
} }
var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this)))); var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
@ -270,7 +270,7 @@ function loadGame(saveString) {
} else { } else {
createNewUpdateText(); createNewUpdateText();
} }
if (Player.bitNodeN == 2 && Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) { if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
try { try {
loadAllGangs(saveObj.AllGangsSave); loadAllGangs(saveObj.AllGangsSave);
} catch(e) { } catch(e) {
@ -360,7 +360,7 @@ function loadImportedGame(saveObj, saveString) {
} }
} else { } else {
} }
if (tempPlayer.bitNodeN == 2 && tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) { if (tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) {
try { try {
loadAllGangs(tempSaveObj.AllGangsSave); loadAllGangs(tempSaveObj.AllGangsSave);
} catch(e) { } catch(e) {
@ -449,7 +449,7 @@ function loadImportedGame(saveObj, saveString) {
} else { } else {
createNewUpdateText(); createNewUpdateText();
} }
if (Player.bitNodeN == 2 && Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) { if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
try { try {
loadAllGangs(saveObj.AllGangsSave); loadAllGangs(saveObj.AllGangsSave);
} catch(e) { } catch(e) {
@ -546,7 +546,7 @@ BitburnerSaveObject.prototype.exportGame = function() {
this.StockMarketSave = JSON.stringify(StockMarket); this.StockMarketSave = JSON.stringify(StockMarket);
this.SettingsSave = JSON.stringify(Settings); this.SettingsSave = JSON.stringify(Settings);
this.VersionSave = JSON.stringify(CONSTANTS.Version); this.VersionSave = JSON.stringify(CONSTANTS.Version);
if (Player.bitNodeN == 2 && Player.inGang()) { if (Player.inGang()) {
this.AllGangsSave = JSON.stringify(AllGangs); this.AllGangsSave = JSON.stringify(AllGangs);
} }

@ -24,8 +24,9 @@ function initSourceFiles() {
"Level 1: 16%<br>" + "Level 1: 16%<br>" +
"Level 2: 24%<br>" + "Level 2: 24%<br>" +
"Level 3: 28%"); "Level 3: 28%");
SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File increases the player's crime success rate, crime money, and charisma " + SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File allows you to form gangs in other BitNodes " +
"multipliers by:<br><br>" + "once your karma decreases to a certain value. It also increases the player's " +
"crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" + "Level 1: 24%<br>" +
"Level 2: 36%<br>" + "Level 2: 36%<br>" +
"Level 3: 42%"); "Level 3: 42%");

@ -262,7 +262,6 @@ const Engine = {
createProgramContent: null, createProgramContent: null,
factionsContent: null, factionsContent: null,
factionContent: null, factionContent: null,
factionAugmentationsContent: null,
augmentationsContent: null, augmentationsContent: null,
tutorialContent: null, tutorialContent: null,
infiltrationContent: null, infiltrationContent: null,
@ -530,8 +529,8 @@ const Engine = {
clearHacknetNodesUI(); clearHacknetNodesUI();
Engine.Display.createProgramContent.style.display = "none"; Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent.style.display = "none"; Engine.Display.factionsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
Engine.Display.factionContent.style.display = "none"; Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent.style.display = "none"; Engine.Display.augmentationsContent.style.display = "none";
Engine.Display.tutorialContent.style.display = "none"; Engine.Display.tutorialContent.style.display = "none";
Engine.Display.locationContent.style.display = "none"; Engine.Display.locationContent.style.display = "none";
@ -749,7 +748,7 @@ const Engine = {
} }
// Gang, if applicable // Gang, if applicable
if (Player.bitNodeN == 2 && Player.inGang()) { if (Player.inGang()) {
Player.gang.process(numCycles, Player); Player.gang.process(numCycles, Player);
} }
@ -1133,7 +1132,7 @@ const Engine = {
} }
// Gang progress for BitNode 2 // Gang progress for BitNode 2
if (Player.bitNodeN != null && Player.bitNodeN === 2 && Player.inGang()) { if (Player.inGang()) {
Player.gang.process(numCyclesOffline, Player); Player.gang.process(numCyclesOffline, Player);
} }
@ -1281,13 +1280,9 @@ const Engine = {
Engine.Display.factionsContent = document.getElementById("factions-container"); Engine.Display.factionsContent = document.getElementById("factions-container");
Engine.Display.factionsContent.style.display = "none"; Engine.Display.factionsContent.style.display = "none";
Engine.Display.factionContent = document.getElementById("faction-container"); Engine.Display.factionContent = document.getElementById("faction-container");
Engine.Display.factionContent.style.display = "none"; Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent = document.getElementById("faction-augmentations-container");
Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent = document.getElementById("augmentations-container"); Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
Engine.Display.augmentationsContent.style.display = "none"; Engine.Display.augmentationsContent.style.display = "none";

@ -117,7 +117,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p> <p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" /> <input id="script-editor-filename" type="text" maxlength="100" tabindex="1" />
</div> </div>
<div id="ace-editor"></div> <div id="ace-editor"></div>
@ -223,8 +223,6 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<!-- Single Faction info (when you select a faction from the Factions menu) --> <!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div> <div id="faction-container" class="generic-menupage-container"></div>
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
<!-- Augmentations --> <!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div> <div id="augmentations-container" class="generic-menupage-container"></div>

@ -0,0 +1,74 @@
/**
* Basic paragraph (p Element) that automatically re-renders itself every X seconds
*
* NOT recommended for usage - only if you really have to
*/
import * as React from "react";
interface IProps {
intervalTime?: number;
style?: object;
text: string;
tooltip?: string;
}
interface IState {
i: number;
}
type IInnerHTMLMarkup = {
__html: string;
}
export class AutoupdatingParagraph extends React.Component<IProps, IState> {
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval: number = 0;
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
}
}
componentDidMount() {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = setInterval(() => this.tick(), time);
}
componentWillUnmount() {
clearInterval(this.interval);
}
tick() {
this.setState(prevState => ({
i: prevState.i + 1
}));
}
render() {
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
const className = "tooltip";
// Tooltip will be set using inner HTML
let tooltipMarkup: IInnerHTMLMarkup | null;
if (hasTooltip) {
tooltipMarkup = {
__html: this.props.tooltip!
}
}
return (
<p className={className} style={this.props.style}>
{this.props.text}
{
hasTooltip &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
}
</p>
)
}
}

@ -12,7 +12,7 @@ export interface IParagraphWithTooltipProps {
export class ParagraphWithTooltip extends React.Component<IParagraphWithTooltipProps, any> { export class ParagraphWithTooltip extends React.Component<IParagraphWithTooltipProps, any> {
render() { render() {
return ( return (
<p className={"tooltip"}> <p className={"tooltip"} style={this.props.style}>
{this.props.text} {this.props.text}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
{this.props.tooltip} {this.props.tooltip}