From 9e345b137566d60ffb807d3bdfa8545a2d6a8e60 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Wed, 16 Jun 2021 00:28:20 -0400 Subject: [PATCH] Mostly done converting Gang UI to React --- src/Gang.jsx | 762 +----------------- src/Gang/AllGangs.ts | 76 ++ src/Gang/ui/GangMemberAccordion.tsx | 15 - src/Gang/ui/GangMemberList.tsx | 51 ++ src/Gang/ui/GangMemberUpgradePopup.tsx | 175 ++++ src/Gang/ui/GangStats.tsx | 281 ++++--- src/Gang/ui/ManagementSubpage.tsx | 38 + src/Gang/ui/Panel1.tsx | 1 - src/Gang/ui/Panel2.tsx | 1 - src/Gang/ui/Panel3.tsx | 1 - src/Gang/ui/TerritorySubpage.tsx | 152 ++++ src/NetscriptFunctions.js | 6 +- .../Player/PlayerObjectGeneralMethods.jsx | 2 +- src/SaveObject.jsx | 2 +- src/engine.jsx | 4 +- src/ui/React/Accordion.tsx | 26 +- 16 files changed, 664 insertions(+), 929 deletions(-) create mode 100644 src/Gang/AllGangs.ts delete mode 100644 src/Gang/ui/GangMemberAccordion.tsx create mode 100644 src/Gang/ui/GangMemberList.tsx create mode 100644 src/Gang/ui/GangMemberUpgradePopup.tsx create mode 100644 src/Gang/ui/ManagementSubpage.tsx create mode 100644 src/Gang/ui/TerritorySubpage.tsx diff --git a/src/Gang.jsx b/src/Gang.jsx index 0d47dc13d..5f6bf9965 100644 --- a/src/Gang.jsx +++ b/src/Gang.jsx @@ -49,11 +49,10 @@ import { GangConstants } from "./Gang/data/Constants"; import { GangMemberTasks } from "./Gang/GangMemberTasks"; import { GangMemberTask } from "./Gang/GangMemberTask"; -import { Panel1 } from "./Gang/ui/Panel1"; -import { Panel2 } from "./Gang/ui/Panel2"; -import { Panel3 } from "./Gang/ui/Panel3"; -import { GangMemberAccordionContent } from "./Gang/ui/GangMemberAccordionContent"; -import { GangMemberAccordion } from "./Gang/ui/GangMemberAccordion"; +import { ManagementSubpage } from "./Gang/ui/ManagementSubpage"; +import { TerritorySubpage } from "./Gang/ui/TerritorySubpage"; +import { GangStats } from "./Gang/ui/GangStats"; +import { AllGangs } from "./Gang/AllGangs"; import React from "react"; import ReactDOM from "react-dom"; @@ -75,20 +74,6 @@ $(document).keydown(function(event) { } }); -// Delete upgrade box when clicking outside -$(document).mousedown(function(event) { - var contentId = "gang-member-upgrade-popup-box-content"; - if (UIElems.gangMemberUpgradeBoxOpened) { - if ( $(event.target).closest("#" + contentId).get(0) == null ) { - //Delete the box - removeElement(UIElems.gangMemberUpgradeBox); - UIElems.gangMemberUpgradeBox = null; - UIElems.gangMemberUpgradeBoxContent = null; - UIElems.gangMemberUpgradeBoxOpened = false; - UIElems.gangMemberUpgradeBoxElements = null; - } - } -}); const GangNames = [ "Slum Snakes", @@ -100,74 +85,6 @@ const GangNames = [ "The Black Hand", ]; -export let AllGangs = { - "Slum Snakes" : { - power: 1, - territory: 1/7, - }, - "Tetrads" : { - power: 1, - territory: 1/7, - }, - "The Syndicate" : { - power: 1, - territory: 1/7, - }, - "The Dark Army" : { - power: 1, - territory: 1/7, - }, - "Speakers for the Dead" : { - power: 1, - territory: 1/7, - }, - "NiteSec" : { - power: 1, - territory: 1/7, - }, - "The Black Hand" : { - power: 1, - territory: 1/7, - }, -} - -export function resetGangs() { - AllGangs = { - "Slum Snakes" : { - power: 1, - territory: 1/7, - }, - "Tetrads" : { - power: 1, - territory: 1/7, - }, - "The Syndicate" : { - power: 1, - territory: 1/7, - }, - "The Dark Army" : { - power: 1, - territory: 1/7, - }, - "Speakers for the Dead" : { - power: 1, - territory: 1/7, - }, - "NiteSec" : { - power: 1, - territory: 1/7, - }, - "The Black Hand" : { - power: 1, - territory: 1/7, - }, - } -} - -export function loadAllGangs(saveString) { - AllGangs = JSON.parse(saveString, Reviver); -} - /** * @param facName {string} Name of corresponding faction * @param hacking {bollean} Whether or not its a hacking gang @@ -412,10 +329,6 @@ Gang.prototype.recruitMember = function(name) { let member = new GangMember(name); this.members.push(member); - if (routing.isOn(Page.Gang)) { - this.createGangMemberDisplayElement(member); - this.updateGangContent(); - } return true; } @@ -488,10 +401,6 @@ Gang.prototype.killMember = function(memberObj) { dialogBoxCreate(`${memberObj.name} was killed in a gang clash! You lost ${lostRespect} respect`); } - // Update UI - if (routing.isOn(Page.Gang)) { - this.displayGangMemberList(); - } } Gang.prototype.ascendMember = function(memberObj, workerScript) { @@ -520,9 +429,6 @@ Gang.prototype.ascendMember = function(memberObj, workerScript) { } else { workerScript.log(`Ascended Gang member ${memberObj.name}`); } - if (routing.isOn(Page.Gang)) { - this.displayGangMemberList(); - } return res; } catch(e) { if (workerScript == null) { @@ -884,10 +790,6 @@ GangMember.prototype.buyUpgrade = function(upg, player, gang) { this.upgrades.push(upg.name); } upg.apply(this); - if (routing.isOn(Page.Gang) && UIElems.gangMemberUpgradeBoxOpened) { - var initFilterValue = UIElems.gangMemberUpgradeBoxFilter.value.toString(); - gang.createGangMemberUpgradeBox(player, initFilterValue); - } return true; } @@ -901,197 +803,6 @@ GangMember.fromJSON = function(value) { Reviver.constructors.GangMember = GangMember; -// Create a pop-up box that lets player purchase upgrades -Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") { - const boxId = "gang-member-upgrade-popup-box"; - if (UIElems.gangMemberUpgradeBoxOpened) { - // Already opened, refreshing - if (UIElems.gangMemberUpgradeBoxElements == null || UIElems.gangMemberUpgradeBox == null || UIElems.gangMemberUpgradeBoxContent == null) { - console.error("Refreshing Gang member upgrade box throws error because required elements are null"); - return; - } - - for (var i = 2; i < UIElems.gangMemberUpgradeBoxElements.length; ++i) { - removeElement(UIElems.gangMemberUpgradeBoxElements[i]); - } - UIElems.gangMemberUpgradeBoxElements = [UIElems.gangMemberUpgradeBoxFilter, UIElems.gangMemberUpgradeBoxDiscount]; - - var filter = UIElems.gangMemberUpgradeBoxFilter.value.toString(); - for (var i = 0; i < this.members.length; ++i) { - if (this.members[i].name.indexOf(filter) > -1 || this.members[i].task.indexOf(filter) > -1) { - var newPanel = this.members[i].createGangMemberUpgradePanel(this, player); - UIElems.gangMemberUpgradeBoxContent.appendChild(newPanel); - UIElems.gangMemberUpgradeBoxElements.push(newPanel); - } - } - } else { - // New popup - UIElems.gangMemberUpgradeBoxFilter = createElement("input", { - type:"text", placeholder:"Filter gang members", - class: "text-input", - value:initialFilter, - onkeyup:() => { - var filterValue = UIElems.gangMemberUpgradeBoxFilter.value.toString(); - this.createGangMemberUpgradeBox(player, filterValue); - }, - }); - - UIElems.gangMemberUpgradeBoxDiscount = createElement("p", { - innerText: "Discount: -" + numeralWrapper.formatPercentage(1 - 1 / this.getDiscount()), - marginLeft: "6px", - tooltip: "You get a discount on equipment and upgrades based on your gang's " + - "respect and power. More respect and power leads to more discounts.", - }); - - UIElems.gangMemberUpgradeBoxElements = [UIElems.gangMemberUpgradeBoxFilter, UIElems.gangMemberUpgradeBoxDiscount]; - - var filter = UIElems.gangMemberUpgradeBoxFilter.value.toString(); - for (var i = 0; i < this.members.length; ++i) { - if (this.members[i].name.indexOf(filter) > -1 || this.members[i].task.indexOf(filter) > -1) { - UIElems.gangMemberUpgradeBoxElements.push(this.members[i].createGangMemberUpgradePanel(this, player)); - } - } - - UIElems.gangMemberUpgradeBox = createPopup(boxId, UIElems.gangMemberUpgradeBoxElements); - UIElems.gangMemberUpgradeBoxContent = document.getElementById(boxId + "-content"); - UIElems.gangMemberUpgradeBoxOpened = true; - } -} - -// Create upgrade panels for each individual Gang Member -GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) { - var container = createElement("div", { - border:"1px solid white", - }); - - var header = createElement("h1", { - innerText: this.name + " (" + this.task + ")", - }); - container.appendChild(header); - - var text = createElement("pre", { - fontSize:"14px", display: "inline-block", width:"20%", - innerText: - "Hack: " + this.hack + " (x" + formatNumber(this.hack_mult * this.hack_asc_mult, 2) + ")\n" + - "Str: " + this.str + " (x" + formatNumber(this.str_mult * this.str_asc_mult, 2) + ")\n" + - "Def: " + this.def + " (x" + formatNumber(this.def_mult * this.def_asc_mult, 2) + ")\n" + - "Dex: " + this.dex + " (x" + formatNumber(this.dex_mult * this.dex_asc_mult, 2) + ")\n" + - "Agi: " + this.agi + " (x" + formatNumber(this.agi_mult * this.agi_asc_mult, 2) + ")\n" + - "Cha: " + this.cha + " (x" + formatNumber(this.cha_mult * this.cha_asc_mult, 2) + ")\n", - }); - - // Already purchased upgrades - const ownedUpgradesElements = []; - function pushOwnedUpgrade(upgName) { - const upg = GangMemberUpgrades[upgName]; - if (upg == null) { - console.error(`Could not find GangMemberUpgrade object for name ${upgName}`); - return; - } - ownedUpgradesElements.push(createElement("div", { - class: "gang-owned-upgrade", - innerText: upgName, - tooltip: upg.desc, - })); - } - for (const upgName of this.upgrades) { pushOwnedUpgrade(upgName); } - for (const upgName of this.augmentations) { pushOwnedUpgrade(upgName); } - - var ownedUpgrades = createElement("div", { - class: "gang-owned-upgrades-div", - innerText: "Purchased Upgrades:", - }); - for (const elem of ownedUpgradesElements) { ownedUpgrades.appendChild(elem); } - container.appendChild(text); - container.appendChild(ownedUpgrades); - container.appendChild(createElement("br", {})); - - // Upgrade buttons. Only show upgrades that can be afforded - const weaponUpgrades = []; - const armorUpgrades = []; - const vehicleUpgrades = []; - const rootkitUpgrades = []; - const augUpgrades = []; - - for (let upgName in GangMemberUpgrades) { - if (GangMemberUpgrades.hasOwnProperty(upgName)) { - let upg = GangMemberUpgrades[upgName]; - if (player.money.lt(upg.getCost(gangObj))) { continue; } - if (this.upgrades.includes(upgName) || this.augmentations.includes(upgName)) { continue; } - switch (upg.type) { - case "w": - weaponUpgrades.push(upg); - break; - case "a": - armorUpgrades.push(upg); - break; - case "v": - vehicleUpgrades.push(upg); - break; - case "r": - rootkitUpgrades.push(upg); - break; - case "g": - augUpgrades.push(upg); - break; - default: - console.error(`ERROR: Invalid Gang Member Upgrade Type: ${upg.type}`); - } - } - } - - // Create separate columns for each upgrade type - const weaponDiv = createElement("div", {width: "20%", display: "inline-block"}); - const armorDiv = createElement("div", {width: "20%", display: "inline-block"}); - const vehicleDiv = createElement("div", {width: "20%", display: "inline-block"}); - const rootkitDiv = createElement("div", {width: "20%", display: "inline-block"}); - const augDiv = createElement("div", {width: "20%", display: "inline-block"}); - - // Add a title/labe for each column - weaponDiv.appendChild(createElement("h2", {innerText: "Weapons"})); - armorDiv.appendChild(createElement("h2", {innerText: "Armor"})); - vehicleDiv.appendChild(createElement("h2", {innerText: "Vehicles"})); - rootkitDiv.appendChild(createElement("h2", {innerText: "Rootkits"})); - augDiv.appendChild(createElement("h2", {innerText: "Augmentations"})); - - // Add buttons to purchase each upgrade - const upgrades = [weaponUpgrades, armorUpgrades, vehicleUpgrades, rootkitUpgrades, augUpgrades]; - const divs = [weaponDiv, armorDiv, vehicleDiv, rootkitDiv, augDiv]; - for (let i = 0; i < upgrades.length; ++i) { - let upgradeArray = upgrades[i]; - let div = divs[i]; - for (let j = 0; j < upgradeArray.length; ++j) { - let upg = upgradeArray[j]; - (function (upg, div, memberObj, i, gang) { - let createElementParams = { - innerHTML: `${upg.name} - ${renderToStaticMarkup(Money(upg.getCost(gang)))}`, - class: "a-link-button", margin:"2px", padding:"2px", display:"block", - fontSize:"11px", - clickListener:() => { - memberObj.buyUpgrade(upg, player, gangObj); - return false; - }, - } - - // For the last two divs, tooltip should be on the left - if (i >= 3) { - createElementParams.tooltipleft = upg.desc; - } else { - createElementParams.tooltip = upg.desc; - } - div.appendChild(createElement("a", createElementParams)); - })(upg, div, this, i, gangObj); - } - } - - container.appendChild(weaponDiv); - container.appendChild(armorDiv); - container.appendChild(vehicleDiv); - container.appendChild(rootkitDiv); - container.appendChild(augDiv); - return container; -} - // Gang UI Dom Elements const UIElems = { // Main elems @@ -1105,33 +816,8 @@ const UIElems = { gangTerritorySubpage: null, // Gang Management Subpage Elements - gangDesc: null, - gangInfo: null, - gangRecruitMemberButton: null, - gangRecruitRequirementText: null, - gangExpandAllButton: null, - gangCollapseAllButton: null, gangMemberFilter: null, - gangManageEquipmentButton: null, - gangMemberList: null, gangMemberPanels: {}, - - // Gang Equipment Upgrade Elements - gangMemberUpgradeBoxOpened: false, - gangMemberUpgradeBox: null, - gangMemberUpgradeBoxContent: null, - gangMemberUpgradeBoxFilter: null, - gangMemberUpgradeBoxDiscount: null, - gangMemberUpgradeBoxElements: null, - - // Gang Territory Elements - gangTerritoryDescText: null, - gangTerritoryWarfareCheckbox: null, - gangTerritoryWarfareCheckboxLabel: null, - gangTerritoryWarfareClashChance: null, - gangTerritoryDeathNotifyCheckbox: null, - gangTerritoryDeathNotifyCheckboxLabel: null, - gangTerritoryInfoText: null, } export function unmount() { @@ -1174,7 +860,6 @@ Gang.prototype.displayGangContent = function(player) { UIElems.managementButton.classList.toggle("a-link-button"); UIElems.territoryButton.classList.toggle("a-link-button-inactive"); UIElems.territoryButton.classList.toggle("a-link-button"); - this.updateGangContent(); return false; }, }) @@ -1188,7 +873,6 @@ Gang.prototype.displayGangContent = function(player) { UIElems.managementButton.classList.toggle("a-link-button"); UIElems.territoryButton.classList.toggle("a-link-button-inactive"); UIElems.territoryButton.classList.toggle("a-link-button"); - this.updateGangContent(); return false; }, }); @@ -1196,459 +880,31 @@ Gang.prototype.displayGangContent = function(player) { UIElems.gangContainer.appendChild(UIElems.territoryButton); // Subpage for managing gang members - UIElems.gangManagementSubpage = createElement("div", { - display:"block", id:"gang-management-subpage", - }); - - var lowerWantedTask = ""; - if (this.isHackingGang) { - lowerWantedTask = "Ethical Hacking"; - } else { - lowerWantedTask = "Vigilante Justice"; - } - UIElems.gangDesc = createElement("p", {width:"70%", - innerHTML: - "This page is used to manage your gang members and get an overview of your " + - "gang's stats.

" + - "If a gang member is not earning much money or respect, the task that you " + - "have assigned to that member might be too difficult. Consider training that " + - "member's stats or choosing an easier task. The tasks closer to the " + - "top of the dropdown list are generally easier. Alternatively, the gang member's " + - "low production might be due to the fact that your wanted level is too high. " + - "Consider assigning a few members to the '" + lowerWantedTask + "' " + - "task to lower your wanted level.

" + - "Installing Augmentations does NOT reset your progress with your Gang. " + - "Furthermore, after installing Augmentations, you will " + - "automatically be a member of whatever Faction you created your gang with.

" + - "You can also manage your gang programmatically through Netscript using the Gang API", - }); - UIElems.gangManagementSubpage.appendChild(UIElems.gangDesc); - - UIElems.gangInfo = createElement("p", {id:"gang-info", width:"70%"}); - UIElems.gangManagementSubpage.appendChild(UIElems.gangInfo); - - UIElems.gangRecruitMemberButton = createElement("a", { - id: "gang-management-recruit-member-btn", class:"a-link-button-inactive", - innerHTML:"Recruit Gang Member", display:"inline-block", margin:"10px", - clickListener:() => { - const popupId = "recruit-gang-member-popup"; - - let yesBtn; - const txt = createElement("p", { - innerText:"Please enter a name for your new Gang member:", - }); - const br = createElement("br"); - const nameInput = createElement("input", { - onkeyup: (e) => { - if (e.keyCode === KEY.ENTER) { yesBtn.click(); } - }, - placeholder: "Name must be unique", - type: "text", - class:"text-input", - }); - yesBtn = createElement("a", { - class: "std-button", - clickListener: () => { - let name = nameInput.value; - if (name === "") { - dialogBoxCreate("You must enter a name for your Gang member!"); - return false; - } - if (!this.canRecruitMember()) { - dialogBoxCreate("You cannot recruit another Gang member!"); - return false; - } - - // At this point, the only way this can fail is if you already - // have a gang member with the same name - if (!this.recruitMember(name)) { - dialogBoxCreate("You already have a gang member with this name!"); - return false; - } - - removeElementById(popupId); - return false; - }, - innerText: "Recruit Gang Member", - }); - const noBtn = createElement("a", { - class: "std-button", - clickListener: () => { - removeElementById(popupId); - return false; - }, - innerText: "Cancel", - }); - createPopup(popupId, [txt, br, nameInput, yesBtn, noBtn]); - nameInput.focus(); - }, - }); - UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitMemberButton); - - // Text for how much reputation is required for recruiting next memberList - UIElems.gangRecruitRequirementText = createElement("p", { - color:"red", - id: "gang-recruit-requirement-text", - margin: "10px", - }); - UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitRequirementText); - - // Gang Member List management buttons (Expand/Collapse All, select a single member) - UIElems.gangManagementSubpage.appendChild(createElement("br", {})); - UIElems.gangExpandAllButton = createElement("a", { - class:"a-link-button", display:"inline-block", - innerHTML:"Expand All", - clickListener:() => { - var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header"); - for (var i = 0; i < allHeaders.length; ++i) { - var hdr = allHeaders[i]; - if (!hdr.classList.contains("active")) { - hdr.click(); - } - } - return false; - }, - }); - UIElems.gangCollapseAllButton = createElement("a", { - class:"a-link-button", display:"inline-block", - innerHTML:"Collapse All", - clickListener:() => { - var allHeaders = UIElems.gangManagementSubpage.getElementsByClassName("accordion-header"); - for (var i = 0; i < allHeaders.length; ++i) { - var hdr = allHeaders[i]; - if (hdr.classList.contains("active")) { - hdr.click(); - } - } - return false; - }, - }); - UIElems.gangMemberFilter = createElement("input", { - type:"text", placeholder:"Filter gang members", margin:"5px", padding:"5px", - class:"text-input", - onkeyup:() => { - this.displayGangMemberList(); - }, - }); - UIElems.gangManageEquipmentButton = createElement("a", { - class:"a-link-button", display:"inline-block", - innerHTML:"Manage Equipment", - clickListener: () => { - this.createGangMemberUpgradeBox(player); - }, - }); - UIElems.gangManagementSubpage.appendChild(UIElems.gangExpandAllButton); - UIElems.gangManagementSubpage.appendChild(UIElems.gangCollapseAllButton); - UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberFilter); - UIElems.gangManagementSubpage.appendChild(UIElems.gangManageEquipmentButton); - - // Gang Member list - UIElems.gangMemberList = createElement("ul", {id:"gang-member-list"}); - this.displayGangMemberList(); - UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberList); + UIElems.gangManagementSubpage = createElement("div"); + UIElems.gangContainer.appendChild(UIElems.gangManagementSubpage); + ReactDOM.render(, UIElems.gangManagementSubpage); // Subpage for seeing gang territory information UIElems.gangTerritorySubpage = createElement("div", { id:"gang-territory-subpage", display:"none", }); - // Info text for territory page - UIElems.gangTerritoryDescText = createElement("p", { - width:"70%", - innerHTML: - "This page shows how much territory your Gang controls. This statistic is listed as a percentage, " + - "which represents how much of the total territory you control.

" + - "Every ~20 seconds, your gang has a chance to 'clash' with other gangs. Your chance " + - "to win a clash depends on your gang's power, which is listed in the display below. " + - "Your gang's power slowly accumulates over time. The accumulation rate is determined by the stats " + - "of all Gang members you have assigned to the 'Territory Warfare' task. Gang members that are not " + - "assigned to this task do not contribute to your gang's power. Your gang also loses a small amount " + - "of power whenever you lose a clash

" + - "NOTE: Gang members assigned to 'Territory Warfare' can be killed during clashes. This can happen regardless of whether you win " + - "or lose the clash. A gang member being killed results in both respect and power loss for your gang.

" + - "The amount of territory you have affects all aspects of your Gang members' production, including " + - "money, respect, and wanted level. It is very beneficial to have high territory control.

", - }); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryDescText); - - // Checkbox for Engaging in Territory Warfare - UIElems.gangTerritoryWarfareCheckbox = createElement("input", { - display: "inline-block", - id: "gang-management-territory-warfare-checkbox", - changeListener: () => { - this.territoryWarfareEngaged = UIElems.gangTerritoryWarfareCheckbox.checked; - }, - margin: "2px", - type: "checkbox", - }); - UIElems.gangTerritoryWarfareCheckbox.checked = this.territoryWarfareEngaged; - - UIElems.gangTerritoryWarfareCheckboxLabel = createElement("label", { - color: "white", - display: "inline-block", - for: "gang-management-territory-warfare-checkbox", - innerText: "Engage in Territory Warfare", - tooltip: "Engaging in Territory Warfare sets your clash chance to 100%. " + - "Disengaging will cause your clash chance to gradually decrease until " + - "it reaches 0%", - }); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareCheckbox); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareCheckboxLabel); - - // Territory Clash chance - UIElems.gangTerritorySubpage.appendChild(createElement("br")); - UIElems.gangTerritoryWarfareClashChance = createElement("p", {display: "inline-block"}); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareClashChance); - - UIElems.gangTerritorySubpage.appendChild(createElement("div", { - class: "help-tip", - display: "inline-block", - innerText: "?", - clickListener: () => { - dialogBoxCreate("This percentage represents the chance you have of 'clashing' with " + - "with another gang. If you do not wish to gain/lose territory, " + - "then keep this percentage at 0% by not engaging in territory " + - "warfare.") - }, - })); - - // Checkbox for whether player wants to be notified of gang member death - UIElems.gangTerritoryDeathNotifyCheckbox = createElement("input", { - display: "inline-block", - id: "gang-management-notify-member-death-checkbox", - changeListener: () => { - this.notifyMemberDeath = UIElems.gangTerritoryDeathNotifyCheckbox.checked; - }, - margin: "2px", - type: "checkbox", - }); - UIElems.gangTerritoryDeathNotifyCheckbox.checked = this.notifyMemberDeath; - - UIElems.gangTerritoryDeathNotifyCheckboxLabel = createElement("label", { - color: "white", - display: "inline-block", - for: "gang-management-notify-member-death-checkbox", - innerText: "Notify about Gang Member Deaths", - tooltip: "If this is enabled, then you will receive a pop-up notifying you " + - "whenever one of your Gang Members dies in a territory clash.", - }); - UIElems.gangTerritorySubpage.appendChild(createElement("br")); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryDeathNotifyCheckbox); - UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryDeathNotifyCheckboxLabel); - - // Territory info (percentages of territory owned for each gang) - UIElems.gangTerritorySubpage.appendChild(createElement("br")); - var territoryBorder = createElement("fieldset", { - display:"block", - margin: "6px", - width:"50%", - }); - - UIElems.gangTerritoryInfoText = createElement("p"); - - territoryBorder.appendChild(UIElems.gangTerritoryInfoText); - UIElems.gangTerritorySubpage.appendChild(territoryBorder); + ReactDOM.render(, UIElems.gangTerritorySubpage); UIElems.gangContainer.appendChild(UIElems.gangTerritorySubpage); - UIElems.gangContainer.appendChild(UIElems.gangManagementSubpage); + document.getElementById("entire-game-container").appendChild(UIElems.gangContainer); } UIElems.gangContainer.style.display = "block"; - this.updateGangContent(); -} - -Gang.prototype.displayGangMemberList = function() { - removeChildrenFromElement(UIElems.gangMemberList); - UIElems.gangMemberPanels = {}; - const members = this.members; - const filter = UIElems.gangMemberFilter.value.toString(); - for (var i = 0; i < members.length; ++i) { - if (members[i].name.indexOf(filter) > -1 || members[i].task.indexOf(filter) > -1) { - this.createGangMemberDisplayElement(members[i]); - } - } -} - -Gang.prototype.updateGangContent = function() { - if (!UIElems.gangContentCreated) { return; } - - if (UIElems.gangMemberUpgradeBoxOpened) { - UIElems.gangMemberUpgradeBoxDiscount.childNodes[0].nodeValue = - "Discount: -" + numeralWrapper.formatPercentage(1 - 1 / this.getDiscount()); - } - - if (UIElems.gangTerritorySubpage.style.display === "block") { - // Territory Warfare Clash Chance - UIElems.gangTerritoryWarfareClashChance.innerText = - `Territory Clash Chance: ${numeralWrapper.formatPercentage(this.territoryClashChance, 3)}`; - - // Engaged in Territory Warfare checkbox - UIElems.gangTerritoryWarfareCheckbox.checked = this.territoryWarfareEngaged; - - // Update territory information - UIElems.gangTerritoryInfoText.innerHTML = ""; - const playerPower = AllGangs[this.facName].power; - let gangNames = Object.keys(AllGangs).filter(g => g != this.facName); - gangNames.unshift(this.facName); - for (const gangname of gangNames) { - if (AllGangs.hasOwnProperty(gangname)) { - const gangTerritoryInfo = AllGangs[gangname]; - let territory = gangTerritoryInfo.territory * 100; - - //Fix some rounding issues graphically - let displayNumber; - if (territory <= 0) { - displayNumber = formatNumber(0, 2); - } else if (territory >= 100) { - displayNumber = formatNumber(100, 2); - } else { - displayNumber = formatNumber(territory, 2); - } - - if (gangname === this.facName) { - let newHTML = `${gangname}
Power: ${formatNumber(gangTerritoryInfo.power, 6)}
`; - newHTML += `Territory: ${displayNumber}%

`; - UIElems.gangTerritoryInfoText.innerHTML += newHTML; - } else { - const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower); - let newHTML = `${gangname}
Power: ${formatNumber(gangTerritoryInfo.power, 6)}
`; - newHTML += `Territory: ${displayNumber}%
`; - newHTML += `Chance to win clash with this gang: ${numeralWrapper.formatPercentage(clashVictoryChance, 3)}

`; - UIElems.gangTerritoryInfoText.innerHTML += newHTML; - } - } - } - } else { - // TODO(hydroflame): you're working here - - // Update information for overall gang - if (UIElems.gangInfo instanceof Element) { - var faction = Factions[this.facName]; - var rep; - if (!(faction instanceof Faction)) { - rep = "ERROR"; - } else { - rep = faction.playerReputation; - } - removeChildrenFromElement(UIElems.gangInfo); - UIElems.gangInfo.appendChild(createElement("p", { // Respect - display: "inline-block", - innerText: "Respect: " + numeralWrapper.formatRespect(this.respect) + - " (" + numeralWrapper.formatRespect(5*this.respectGainRate) + " / sec)", - tooltip: "Represents the amount of respect your gang has from other gangs and criminal " + - "organizations. Your respect affects the amount of money " + - "your gang members will earn, and also determines how much " + - "reputation you are earning with your gang's corresponding Faction.", - })); - UIElems.gangInfo.appendChild(createElement("br")); - - UIElems.gangInfo.appendChild(createElement("p", { // Wanted level - display: "inline-block", - innerText: "Wanted Level: " + numeralWrapper.formatWanted(this.wanted) + - " (" + numeralWrapper.formatWanted(5*this.wantedGainRate) + " / sec)", - tooltip: "Represents how much the gang is wanted by law enforcement. The higher " + - "your gang's wanted level, the harder it will be for your gang members " + - "to make money and earn respect. Note that the minimum wanted level is 1.", - })); - UIElems.gangInfo.appendChild(createElement("br")); - - var wantedPenalty = this.getWantedPenalty(); - wantedPenalty = (1 - wantedPenalty) * 100; - UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier - display: "inline-block", - innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`, - tooltip: "Penalty for respect and money gain rates due to Wanted Level", - })); - UIElems.gangInfo.appendChild(createElement("br")); - - const d0 = createElement("div"); - ReactDOM.render(

Money gain rate: {MoneyRate(5 * this.moneyGainRate)}

, d0); - UIElems.gangInfo.appendChild(d0); - UIElems.gangInfo.appendChild(createElement("br")); - - // Fix some rounding issues graphically - var territoryMult = AllGangs[this.facName].territory * 100; - let displayNumber; - if (territoryMult <= 0) { - displayNumber = formatNumber(0, 2); - } else if (territoryMult >= 100) { - displayNumber = formatNumber(100, 2); - } else { - displayNumber = formatNumber(territoryMult, 2); - } - UIElems.gangInfo.appendChild(createElement("p", { // Territory multiplier - display: "inline-block", - innerText: `Territory: ${formatNumber(displayNumber, 3)}%`, - tooltip: "The percentage of total territory your Gang controls", - })); - UIElems.gangInfo.appendChild(createElement("br")); - - const d1 = createElement("div"); - ReactDOM.render(

Faction reputation: {Reputation(rep)}

, d1); - UIElems.gangInfo.appendChild(d1); - UIElems.gangInfo.appendChild(createElement("br")); - - const CyclesPerSecond = 1000 / Engine._idleSpeed; - if (this.storedCycles / CyclesPerSecond*1000 > 5000) { - UIElems.gangInfo.appendChild(createElement("p", { // Stored Cycles - innerText: `Bonus time: ${convertTimeMsToTimeElapsedString(this.storedCycles / CyclesPerSecond*1000)}`, - display: "inline-block", - tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). " + - "Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed", - })); - UIElems.gangInfo.appendChild(createElement("br")); - } - } else { - console.error("gang-info DOM element DNE"); - } - - // Toggle the 'Recruit member button' if valid - const numMembers = this.members.length; - const respectCost = this.getRespectNeededToRecruitMember(); - - const btn = UIElems.gangRecruitMemberButton; - if (numMembers >= GangConstants.MaximumGangMembers) { - btn.className = "a-link-button-inactive"; - UIElems.gangRecruitRequirementText.style.display = "inline-block"; - UIElems.gangRecruitRequirementText.innerHTML = "You have reached the maximum amount of gang members"; - } else if (this.canRecruitMember()) { - btn.className = "a-link-button"; - UIElems.gangRecruitRequirementText.style.display = "none"; - } else { - btn.className = "a-link-button-inactive"; - UIElems.gangRecruitRequirementText.style.display = "inline-block"; - UIElems.gangRecruitRequirementText.innerHTML = `${formatNumber(respectCost, 2)} respect needed to recruit next member`; - } - - // TODO(hydroflame): TO HERE - } -} - -// Takes in a GangMember object -Gang.prototype.createGangMemberDisplayElement = function(memberObj) { - if (!UIElems.gangContentCreated) { return; } - const name = memberObj.name; - const id = `${name}-gang-member-accordion`; - if(document.getElementById(id)) return; - - // Clear/Update the UIElems map to keep track of this gang member's panel - UIElems.gangMemberPanels[name] = {}; - const li = createElement("li", {id: id}); - ReactDOM.render(, li); - UIElems.gangMemberPanels[name] = li; - UIElems.gangMemberList.appendChild(li); } Gang.prototype.clearUI = function() { if (UIElems.gangContainer instanceof Element) { removeElement(UIElems.gangContainer); } - if (UIElems.gangMemberUpgradeBox instanceof Element) { removeElement(UIElems.gangMemberUpgradeBox); } - for (const prop in UIElems) { UIElems[prop] = null; } UIElems.gangContentCreated = false; - UIElems.gangMemberUpgradeBoxOpened = false; UIElems.gangMemberPanels = {}; } diff --git a/src/Gang/AllGangs.ts b/src/Gang/AllGangs.ts new file mode 100644 index 000000000..c6b078ad0 --- /dev/null +++ b/src/Gang/AllGangs.ts @@ -0,0 +1,76 @@ +import { Reviver } from "../../utils/JSONReviver"; + +interface GangTerritory { + power: number; + territory: number; +} + +export let AllGangs: { + [key: string]: GangTerritory; +} = { + "Slum Snakes" : { + power: 1, + territory: 1/7, + }, + "Tetrads" : { + power: 1, + territory: 1/7, + }, + "The Syndicate" : { + power: 1, + territory: 1/7, + }, + "The Dark Army" : { + power: 1, + territory: 1/7, + }, + "Speakers for the Dead" : { + power: 1, + territory: 1/7, + }, + "NiteSec" : { + power: 1, + territory: 1/7, + }, + "The Black Hand" : { + power: 1, + territory: 1/7, + }, +} + +export function resetGangs() { + AllGangs = { + "Slum Snakes" : { + power: 1, + territory: 1/7, + }, + "Tetrads" : { + power: 1, + territory: 1/7, + }, + "The Syndicate" : { + power: 1, + territory: 1/7, + }, + "The Dark Army" : { + power: 1, + territory: 1/7, + }, + "Speakers for the Dead" : { + power: 1, + territory: 1/7, + }, + "NiteSec" : { + power: 1, + territory: 1/7, + }, + "The Black Hand" : { + power: 1, + territory: 1/7, + }, + } +} + +export function loadAllGangs(saveString: string) { + AllGangs = JSON.parse(saveString, Reviver); +} \ No newline at end of file diff --git a/src/Gang/ui/GangMemberAccordion.tsx b/src/Gang/ui/GangMemberAccordion.tsx deleted file mode 100644 index af388ecd0..000000000 --- a/src/Gang/ui/GangMemberAccordion.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from "react"; -import { GangMemberAccordionContent } from "./GangMemberAccordionContent" -import { Accordion } from "../../ui/React/Accordion"; - -interface IProps { - gang: any; - member: any; -} - -export function GangMemberAccordion(props: IProps): React.ReactElement { - return ({props.member.name}} - panelContent={} - />); -} diff --git a/src/Gang/ui/GangMemberList.tsx b/src/Gang/ui/GangMemberList.tsx new file mode 100644 index 000000000..40b145037 --- /dev/null +++ b/src/Gang/ui/GangMemberList.tsx @@ -0,0 +1,51 @@ +import React, { useState, useEffect } from "react"; +import { Accordion } from "../../ui/React/Accordion"; +import { GangMemberAccordionContent } from "./GangMemberAccordionContent" +import { GangMemberUpgradePopup } from "./GangMemberUpgradePopup" +import { createPopup } from "../../ui/React/createPopup"; +import { IPlayer } from "../../PersonObjects/IPlayer"; + +interface IProps { + gang: any; + player: IPlayer; +} + +export function GangMemberList(props: IProps): React.ReactElement { + const [rerender, setRerender] = useState(false); + const [filter, setFilter] = useState(""); + + useEffect(() => { + const id = setInterval(() => setRerender(old => !old), 1000); + return () => clearInterval(id); + }, []); + + function openUpgradePopup(): void { + const popupId = `gang-upgrade-popup`; + createPopup(popupId, GangMemberUpgradePopup, { + gang: props.gang, + player: props.player, + popupId: popupId, + }); + } + + function onChange(event: React.ChangeEvent): void { + setFilter(event.target.value); + } + + function members(): any { + return props.gang.members.filter((member: any) => member.name.indexOf(filter) > -1 || member.task.indexOf(filter) > -1) + } + + return (<> + + Manage Equipment +
    + {members().map((member: any, i : number) =>
  • + {member.name}} + panelContent={} /> +
  • )} +
+ ); +} \ No newline at end of file diff --git a/src/Gang/ui/GangMemberUpgradePopup.tsx b/src/Gang/ui/GangMemberUpgradePopup.tsx new file mode 100644 index 000000000..4d96242fc --- /dev/null +++ b/src/Gang/ui/GangMemberUpgradePopup.tsx @@ -0,0 +1,175 @@ +import React, { useState, useEffect } from "react"; +import { formatNumber } from "../../../utils/StringHelperFunctions"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { GangMemberUpgrades } from "../GangMemberUpgrades"; +import { GangMemberUpgrade } from "../GangMemberUpgrade"; +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { Money } from "../../ui/React/Money"; +import { removePopup } from "../../ui/React/createPopup"; + +interface IPanelProps { + member: any; + gang: any; + player: IPlayer; +} + +function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement { + const [rerender, setRerender] = useState(false); + // Upgrade buttons. Only show upgrades that can be afforded + const weaponUpgrades: GangMemberUpgrade[] = []; + const armorUpgrades: GangMemberUpgrade[] = []; + const vehicleUpgrades: GangMemberUpgrade[] = []; + const rootkitUpgrades: GangMemberUpgrade[] = []; + const augUpgrades: GangMemberUpgrade[] = []; + + for (const upgName in GangMemberUpgrades) { + if (GangMemberUpgrades.hasOwnProperty(upgName)) { + const upg = GangMemberUpgrades[upgName]; + if (props.player.money.lt(upg.getCost(props.gang))) continue; + if (props.member.upgrades.includes(upgName) || props.member.augmentations.includes(upgName)) continue; + switch (upg.type) { + case "w": + weaponUpgrades.push(upg); + break; + case "a": + armorUpgrades.push(upg); + break; + case "v": + vehicleUpgrades.push(upg); + break; + case "r": + rootkitUpgrades.push(upg); + break; + case "g": + augUpgrades.push(upg); + break; + default: + console.error(`ERROR: Invalid Gang Member Upgrade Type: ${upg.type}`); + } + } + } + + function purchased(name: string): React.ReactElement { + const upg = GangMemberUpgrades[name] + return (
+ {upg.name} + +
); + } + + function upgradeButton(upg: GangMemberUpgrade, left: boolean = false): React.ReactElement { + function onClick(): void { + props.member.buyUpgrade(upg, props.player, props.gang); + setRerender(old => !old); + } + return ( + {upg.name} - {Money(upg.getCost(props.gang))} + + ); + } + + return (
+

{props.member.name}({props.member.task})

+
+Hack: {props.member.hack} (x{formatNumber(props.member.hack_mult * props.member.hack_asc_mult, 2)})
+Str: {props.member.str} (x{formatNumber(props.member.str_mult * props.member.str_asc_mult, 2)})
+Def: {props.member.def} (x{formatNumber(props.member.def_mult * props.member.def_asc_mult, 2)})
+Dex: {props.member.dex} (x{formatNumber(props.member.dex_mult * props.member.dex_asc_mult, 2)})
+Agi: {props.member.agi} (x{formatNumber(props.member.agi_mult * props.member.agi_asc_mult, 2)})
+Cha: {props.member.cha} (x{formatNumber(props.member.cha_mult * props.member.cha_asc_mult, 2)}) +
+
+ Purchased Upgrades: {props.member.upgrades.map((upg: any) => purchased(upg))} + {props.member.augmentations.map((upg: any) => purchased(upg))} +
+
+

Weapons

+ {weaponUpgrades.map(upg => upgradeButton(upg))} +
+
+

Armor

+ {armorUpgrades.map(upg => upgradeButton(upg))} +
+
+

Vehicles

+ {vehicleUpgrades.map(upg => upgradeButton(upg))} +
+
+

Rootkits

+ {rootkitUpgrades.map(upg => upgradeButton(upg, true))} +
+
+

Augmentations

+ {augUpgrades.map(upg => upgradeButton(upg, true))} +
+
); +} + +interface IProps { + gang: any; + player: IPlayer; + popupId: string; +} + +export function GangMemberUpgradePopup(props: IProps): React.ReactElement { + const [rerender, setRerender] = useState(false); + const [filter, setFilter] = useState(""); + + function closePopup(): void { + removePopup(props.popupId); + } + + useEffect(() => { + window.addEventListener('keydown', closePopup); + const id = setInterval(() => setRerender(old => !old), 1000); + return () => { + clearInterval(id); + window.removeEventListener('keydown', closePopup); + } + }, []); + + return (<> + setFilter(event.target.value)} /> +

+ Discount: -{numeralWrapper.formatPercentage(1 - 1 / props.gang.getDiscount())} + You get a discount on equipment and upgrades based on your gang's respect and power. More respect and power leads to more discounts. +

+ {props.gang.members.map((member: any) => )} + ); +} + +/* + +// Add buttons to purchase each upgrade +const upgrades = [weaponUpgrades, armorUpgrades, vehicleUpgrades, rootkitUpgrades, augUpgrades]; +const divs = [weaponDiv, armorDiv, vehicleDiv, rootkitDiv, augDiv]; +for (let i = 0; i < upgrades.length; ++i) { + let upgradeArray = upgrades[i]; + let div = divs[i]; + for (let j = 0; j < upgradeArray.length; ++j) { + let upg = upgradeArray[j]; + (function (upg, div, memberObj, i, gang) { + let createElementParams = { + innerHTML: `${upg.name} - ${renderToStaticMarkup(Money(upg.getCost(gang)))}`, + class: "a-link-button", margin:"2px", padding:"2px", display:"block", + fontSize:"11px", + clickListener:() => { + memberObj.buyUpgrade(upg, player, gangObj); + return false; + }, + } + + // For the last two divs, tooltip should be on the left + if (i >= 3) { + createElementParams.tooltipleft = upg.desc; + } else { + createElementParams.tooltip = upg.desc; + } + div.appendChild(createElement("a", createElementParams)); + })(upg, div, this, i, gangObj); + } +} + +createPopup(boxId, UIElems.gangMemberUpgradeBoxElements) + +*/ \ No newline at end of file diff --git a/src/Gang/ui/GangStats.tsx b/src/Gang/ui/GangStats.tsx index 348685008..0035e57e7 100644 --- a/src/Gang/ui/GangStats.tsx +++ b/src/Gang/ui/GangStats.tsx @@ -1,4 +1,123 @@ import React, { useState, useEffect } from "react"; +import { Factions } from "../../Faction/Factions"; + +import { + formatNumber, + convertTimeMsToTimeElapsedString, +} from "../../../utils/StringHelperFunctions"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { MoneyRate } from "../../ui/React/MoneyRate"; +import { Reputation } from "../../ui/React/Reputation"; +import { AllGangs } from "../AllGangs"; +import { GangConstants } from "../data/Constants"; +import { createPopup, removePopup } from "../../ui/React/createPopup"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; + +interface IRecruitPopupProps { + gang: any; + popupId: string; +} + +function recruitPopup(props: IRecruitPopupProps): React.ReactElement { + const [name, setName] = useState(""); + + function recruit(): void { + if (name === "") { + dialogBoxCreate("You must enter a name for your Gang member!"); + return; + } + if (!props.gang.canRecruitMember()) { + dialogBoxCreate("You cannot recruit another Gang member!"); + return; + } + + // At this point, the only way this can fail is if you already + // have a gang member with the same name + if (!props.gang.recruitMember(name)) { + dialogBoxCreate("You already have a gang member with this name!"); + return; + } + + removePopup(props.popupId); + } + + function cancel(): void { + removePopup(props.popupId); + } + + function onKeyUp(event: any): void { + if(event.keyCode === 13) recruit(); + if(event.keyCode === 27) cancel(); + } + + function onChange(event: any): void { + setName(event.target.value); + } + + return (<> +

Enter a name for your new Gang member:


+ + Recruit Gang Member + Cancel + ); +} + +interface IProps { + gang: any; +} + +function Recruitment(props: IProps): React.ReactElement { + // Toggle the 'Recruit member button' if valid + const numMembers = props.gang.members.length; + const respectCost = props.gang.getRespectNeededToRecruitMember(); + + if (numMembers >= GangConstants.MaximumGangMembers) { + return (<>); + } else if (props.gang.canRecruitMember()) { + function onClick() { + const popupId = "recruit-gang-member-popup"; + createPopup(popupId, recruitPopup, { + gang: props.gang, + popupId: popupId, + }); + } + return (<> + + Recruit Gang Member + + ); + } + return (<> + + Recruit Gang Member + +

+ {formatNumber(respectCost, 2)} respect needed to recruit next member +

+ ); +} + +function BonusTime(props: IProps): React.ReactElement { + const CyclesPerSecond = 1000 / 200; + if (props.gang.storedCycles / CyclesPerSecond*1000 <= 5000) return <>; + return (<> +

+ Bonus time: {convertTimeMsToTimeElapsedString(props.gang.storedCycles / CyclesPerSecond*1000)} + + You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed + +

+
+ ); +} export function GangStats(props: IProps): React.ReactElement { const [rerender, setRerender] = useState(false); @@ -8,157 +127,57 @@ export function GangStats(props: IProps): React.ReactElement { return () => clearInterval(id); }, []); + const territoryMult = AllGangs[props.gang.facName].territory * 100; + let territoryStr; + if (territoryMult <= 0) { + territoryStr = formatNumber(0, 2); + } else if (territoryMult >= 100) { + territoryStr = formatNumber(100, 2); + } else { + territoryStr = formatNumber(territoryMult, 2); + } - return (

-

- Respect: 108.82214 (0.23534 / sec) - + return (<> +

+ Respect: {numeralWrapper.formatRespect(props.gang.respect)} ({numeralWrapper.formatRespect(5*props.gang.respectGainRate)} / sec) + Represents the amount of respect your gang has from other gangs and criminal organizations. Your respect affects the amount of money your gang members will earn, and also determines how much reputation you are earning with your gang's corresponding Faction.


-

- Wanted Level: 1.37503 (0.00002 / sec) - +

+ Wanted Level: {numeralWrapper.formatWanted(props.gang.wanted)} ({numeralWrapper.formatWanted(5*props.gang.wantedGainRate)} / sec) + Represents how much the gang is wanted by law enforcement. The higher your gang's wanted level, the harder it will be for your gang members to make money and earn respect. Note that the minimum wanted level is 1.


-

- Wanted Level Penalty: -1.25% - +

+ Wanted Level Penalty: -{formatNumber((1 - props.gang.getWantedPenalty()) * 100, 2)}% + Penalty for respect and money gain rates due to Wanted Level


-

- Money gain rate: - - $2.571k / sec - +

+ Money gain rate: {MoneyRate(5 * props.gang.moneyGainRate)}


-

- Territory: 14.29% - +

+ Territory: {territoryStr}% + The percentage of total territory your Gang controls


-
-

- Faction reputation: - - 28.677 - -

-
-
-

- Bonus time: 1 hours 30 minutes 58 seconds - - You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed - +

+ Faction reputation: {Reputation(Factions[props.gang.facName].playerReputation)}


-

); -} - - - -/* - -var faction = Factions[this.facName]; -var rep; -if (!(faction instanceof Faction)) { - rep = "ERROR"; -} else { - rep = faction.playerReputation; -} -UIElems.gangInfo.appendChild(createElement("p", { - display: "inline-block", - innerText: "Respect: " + numeralWrapper.formatRespect(this.respect) + - " (" + numeralWrapper.formatRespect(5*this.respectGainRate) + " / sec)", - tooltip: "Represents the amount of respect your gang has from other gangs and criminal " + - "organizations. Your respect affects the amount of money " + - "your gang members will earn, and also determines how much " + - "reputation you are earning with your gang's corresponding Faction.", -})); -UIElems.gangInfo.appendChild(createElement("br")); - -UIElems.gangInfo.appendChild(createElement("p", { - display: "inline-block", - innerText: "Wanted Level: " + numeralWrapper.formatWanted(this.wanted) + - " (" + numeralWrapper.formatWanted(5*this.wantedGainRate) + " / sec)", - tooltip: "Represents how much the gang is wanted by law enforcement. The higher " + - "your gang's wanted level, the harder it will be for your gang members " + - "to make money and earn respect. Note that the minimum wanted level is 1.", -})); -UIElems.gangInfo.appendChild(createElement("br")); - -var wantedPenalty = this.getWantedPenalty(); -wantedPenalty = (1 - wantedPenalty) * 100; -UIElems.gangInfo.appendChild(createElement("p", { - display: "inline-block", - innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`, - tooltip: "Penalty for respect and money gain rates due to Wanted Level", -})); -UIElems.gangInfo.appendChild(createElement("br")); - -const d0 = createElement("div"); -ReactDOM.render(

Money gain rate: {MoneyRate(5 * this.moneyGainRate)}

, d0); -UIElems.gangInfo.appendChild(d0); -UIElems.gangInfo.appendChild(createElement("br")); - -var territoryMult = AllGangs[this.facName].territory * 100; -let displayNumber; -if (territoryMult <= 0) { - displayNumber = formatNumber(0, 2); -} else if (territoryMult >= 100) { - displayNumber = formatNumber(100, 2); -} else { - displayNumber = formatNumber(territoryMult, 2); -} -UIElems.gangInfo.appendChild(createElement("p", { - display: "inline-block", - innerText: `Territory: ${formatNumber(displayNumber, 3)}%`, - tooltip: "The percentage of total territory your Gang controls", -})); -UIElems.gangInfo.appendChild(createElement("br")); - -const d1 = createElement("div"); -ReactDOM.render(

Faction reputation: {Reputation(rep)}

, d1); -UIElems.gangInfo.appendChild(d1); -UIElems.gangInfo.appendChild(createElement("br")); - -const CyclesPerSecond = 1000 / Engine._idleSpeed; -if (this.storedCycles / CyclesPerSecond*1000 > 5000) { - UIElems.gangInfo.appendChild(createElement("p", { - innerText: `Bonus time: ${convertTimeMsToTimeElapsedString(this.storedCycles / CyclesPerSecond*1000)}`, - display: "inline-block", - tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by the browser). " + - "Bonus time makes the Gang mechanic progress faster, up to 5x the normal speed", - })); - UIElems.gangInfo.appendChild(createElement("br")); -} - -const numMembers = this.members.length; -const respectCost = this.getRespectNeededToRecruitMember(); - -const btn = UIElems.gangRecruitMemberButton; -if (numMembers >= GangConstants.MaximumGangMembers) { - btn.className = "a-link-button-inactive"; - UIElems.gangRecruitRequirementText.style.display = "inline-block"; - UIElems.gangRecruitRequirementText.innerHTML = "You have reached the maximum amount of gang members"; -} else if (this.canRecruitMember()) { - btn.className = "a-link-button"; - UIElems.gangRecruitRequirementText.style.display = "none"; -} else { - btn.className = "a-link-button-inactive"; - UIElems.gangRecruitRequirementText.style.display = "inline-block"; - UIElems.gangRecruitRequirementText.innerHTML = `${formatNumber(respectCost, 2)} respect needed to recruit next member`; -} - -*/ \ No newline at end of file + +
+ + ); +} \ No newline at end of file diff --git a/src/Gang/ui/ManagementSubpage.tsx b/src/Gang/ui/ManagementSubpage.tsx new file mode 100644 index 000000000..703b6a106 --- /dev/null +++ b/src/Gang/ui/ManagementSubpage.tsx @@ -0,0 +1,38 @@ +import React, { useState, useEffect } from "react"; +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { GangStats } from "./GangStats"; +import { GangMemberList } from "./GangMemberList"; + +interface IProps { + gang: any; + player: IPlayer; +} + +export function ManagementSubpage(props: IProps): React.ReactElement { + return (
+

+ This page is used to manage your gang members and get an overview of your gang's stats. +
+
+ If a gang member is not earning much money or respect, the task that you + have assigned to that member might be too difficult. Consider training that + member's stats or choosing an easier task. The tasks closer to the + top of the dropdown list are generally easier. Alternatively, the gang member's + low production might be due to the fact that your wanted level is too high. + Consider assigning a few members to the '{props.gang.isHackingGang?"Ethical Hacking":"Vigilante Justice"}' + task to lower your wanted level. +
+
+ Installing Augmentations does NOT reset your progress with your Gang. + Furthermore, after installing Augmentations, you will + automatically be a member of whatever Faction you created your gang with. +
+
+ You can also manage your gang programmatically through Netscript using the Gang API +

+
+ +
+ +
); +} diff --git a/src/Gang/ui/Panel1.tsx b/src/Gang/ui/Panel1.tsx index 872943d2a..d9df20011 100644 --- a/src/Gang/ui/Panel1.tsx +++ b/src/Gang/ui/Panel1.tsx @@ -13,7 +13,6 @@ interface IAscendProps { function ascendPopup(props: IAscendProps): React.ReactElement { function confirm() { props.gang.ascendMember(props.member); - props.gang.updateGangMemberDisplayElement(props.member); removePopup(props.popupId); return false; } diff --git a/src/Gang/ui/Panel2.tsx b/src/Gang/ui/Panel2.tsx index cb3c3c60d..01f48bf9b 100644 --- a/src/Gang/ui/Panel2.tsx +++ b/src/Gang/ui/Panel2.tsx @@ -20,7 +20,6 @@ export function Panel2(props: IProps): React.ReactElement { function onChange(event: React.ChangeEvent): void { const task = event.target.value; props.member.assignToTask(task); - props.gang.updateGangContent(); setCurrentTask(task); } diff --git a/src/Gang/ui/Panel3.tsx b/src/Gang/ui/Panel3.tsx index 8f792811b..9ebab90dc 100644 --- a/src/Gang/ui/Panel3.tsx +++ b/src/Gang/ui/Panel3.tsx @@ -11,7 +11,6 @@ export function Panel3(props: IProps): React.ReactElement { useEffect(() => { const id = setInterval(() => { setRerender(old => !old); - console.log('render'); }, 1000); return () => clearInterval(id); }, []); diff --git a/src/Gang/ui/TerritorySubpage.tsx b/src/Gang/ui/TerritorySubpage.tsx new file mode 100644 index 000000000..0077aace0 --- /dev/null +++ b/src/Gang/ui/TerritorySubpage.tsx @@ -0,0 +1,152 @@ +import React, { useState, useEffect } from "react"; +import { numeralWrapper } from "../../ui/numeralFormat"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { formatNumber } from "../../../utils/StringHelperFunctions"; +import { AllGangs } from "../AllGangs"; + +interface IProps { + gang: any; +} + +export function TerritorySubpage(props: IProps): React.ReactElement { + const [rerender, setRerender] = useState(false); + + useEffect(() => { + const id = setInterval(() => setRerender(old => !old), 1000); + return () => clearInterval(id); + }, []); + + function openWarfareHelp(): void { + dialogBoxCreate("This percentage represents the chance you have of 'clashing' with " + + "with another gang. If you do not wish to gain/lose territory, " + + "then keep this percentage at 0% by not engaging in territory " + + "warfare.") + } + + function formatTerritoryP(n: number): string { + const v = n * 100; + let displayNumber; + if (n <= 0) { + return formatNumber(0, 2); + } else if (n >= 100) { + return formatNumber(100, 2); + } else { + return formatNumber(n, 2); + } + } + + const playerPower = AllGangs[props.gang.facName].power; + function otherGangTerritory(name: string): React.ReactElement { + const power = AllGangs[name].power + const clashVictoryChance = playerPower / (power + playerPower); + return ( + {name}
+ Power: {formatNumber(power, 6)}
+ Territory: {formatTerritoryP(AllGangs[name].territory)}%
+ Chance to win clash with this gang: {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
+
+
); + } + + const gangNames = Object.keys(AllGangs).filter(g => g != props.gang.facName); + + return (
+

+ This page shows how much territory your Gang controls. This + statistic is listed as a percentage, which represents how much of + the total territory you control. +
+
+ Every ~20 seconds, your gang has a chance to 'clash' with other + gangs. Your chance to win a clash depends on your gang's power, + which is listed in the display below. Your gang's power slowly + accumulates over time. The accumulation rate is determined by the + stats of all Gang members you have assigned to the 'Territory + Warfare' task. Gang members that are not assigned to this task do + not contribute to your gang's power. Your gang also loses a small + amount of power whenever you lose a clash. +
+
+ NOTE: Gang members assigned to 'Territory Warfare' can be killed + during clashes. This can happen regardless of whether you win or + lose the clash. A gang member being killed results in both respect + and power loss for your gang. +
+
+ The amount of territory you have affects all aspects of your Gang + members' production, including money, respect, and wanted level. It + is very beneficial to have high territory control. +
+
+

+ props.gang.territoryWarfareEngaged = event.target.checked}/> + +
+

+ Territory Clash Chance: {numeralWrapper.formatPercentage(props.gang.territoryClashChance, 3)} +

+
?
+
+ + props.gang.notifyMemberDeath = event.target.checked}/> + +
+
+

+ {props.gang.facName}
+ Power: {formatNumber(AllGangs[props.gang.facName].power, 6)}
+ Territory: {formatTerritoryP(AllGangs[props.gang.facName].territory)}%
+
+ {gangNames.map(name => otherGangTerritory(name))} +

+
+
); +} + +/* + +let gangNames = Object.keys(AllGangs).filter(g => g != this.facName); +gangNames.unshift(this.facName); +for (const gangname of gangNames) { + if (AllGangs.hasOwnProperty(gangname)) { + const gangTerritoryInfo = AllGangs[gangname]; + let territory = gangTerritoryInfo.territory * 100; + + //Fix some rounding issues graphically + let displayNumber; + if (territory <= 0) { + displayNumber = formatNumber(0, 2); + } else if (territory >= 100) { + displayNumber = formatNumber(100, 2); + } else { + displayNumber = formatNumber(territory, 2); + } + + if (gangname === this.facName) { + let newHTML = `${gangname}
Power: ${formatNumber(gangTerritoryInfo.power, 6)}
`; + newHTML += `Territory: ${displayNumber}%

`; + UIElems.gangTerritoryInfoText.innerHTML += newHTML; + } else { + const clashVictoryChance = playerPower / (gangTerritoryInfo.power + playerPower); + let newHTML = `${gangname}
Power: ${formatNumber(gangTerritoryInfo.power, 6)}
`; + newHTML += `Territory: ${displayNumber}%
`; + newHTML += `Chance to win clash with this gang: ${numeralWrapper.formatPercentage(clashVictoryChance, 3)}

`; + UIElems.gangTerritoryInfoText.innerHTML += newHTML; + } + } +} + +*/ \ No newline at end of file diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index d05380478..4af133c69 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -29,10 +29,8 @@ import { calculateWeakenTime, } from "./Hacking"; import { calculateServerGrowth } from "./Server/formulas/grow"; -import { - AllGangs, - Gang, -} from "./Gang"; +import { Gang } from "./Gang"; +import { AllGangs } from "./Gang/AllGangs"; import { GangMemberTasks } from "./Gang/GangMemberTasks"; import { GangMemberUpgrades } from "./Gang/GangMemberUpgrades"; import { Factions, factionExists } from "./Faction/Factions"; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx index 3df0f9627..f3b76e9d9 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx @@ -18,7 +18,7 @@ import { Engine } from "../../engine"; import { Faction } from "../../Faction/Faction"; import { Factions } from "../../Faction/Factions"; import { displayFactionContent } from "../../Faction/FactionHelpers"; -import { resetGangs } from "../../Gang"; +import { resetGangs } from "../../Gang/AllGangs"; import { hasHacknetServers } from "../../Hacknet/HacknetHelpers"; import { Cities } from "../../Locations/Cities"; import { Locations } from "../../Locations/Locations"; diff --git a/src/SaveObject.jsx b/src/SaveObject.jsx index fc17fa8a4..8d860d4d3 100755 --- a/src/SaveObject.jsx +++ b/src/SaveObject.jsx @@ -10,7 +10,7 @@ import { Engine } from "./engine"; import { Factions, loadFactions } from "./Faction/Factions"; import { loadFconf } from "./Fconf/Fconf"; import { FconfSettings } from "./Fconf/FconfSettings"; -import { loadAllGangs, AllGangs } from "./Gang"; +import { loadAllGangs, AllGangs } from "./Gang/AllGangs"; import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers"; import { Player, loadPlayer } from "./Player"; import { AllServers, loadAllServers } from "./Server/AllServers"; diff --git a/src/engine.jsx b/src/engine.jsx index 5ad2bb153..01d0b9d3e 100644 --- a/src/engine.jsx +++ b/src/engine.jsx @@ -868,9 +868,7 @@ const Engine = { } if (Engine.Counters.updateDisplaysLong <= 0) { - if (routing.isOn(Page.Gang) && Player.inGang()) { - Player.gang.updateGangContent(); - } else if (routing.isOn(Page.ScriptEditor)) { + if (routing.isOn(Page.ScriptEditor)) { updateScriptEditorContent(); } Engine.Counters.updateDisplaysLong = 15; diff --git a/src/ui/React/Accordion.tsx b/src/ui/React/Accordion.tsx index d45055cfb..d0e033ad2 100644 --- a/src/ui/React/Accordion.tsx +++ b/src/ui/React/Accordion.tsx @@ -23,27 +23,14 @@ export class Accordion extends React.Component { this.handleHeaderClick = this.handleHeaderClick.bind(this); this.state = { - panelOpened: props.panelInitiallyOpened ? true : false, + panelOpened: props.panelInitiallyOpened ? props.panelInitiallyOpened : false, } } handleHeaderClick(e: React.MouseEvent): void { - const elem = e.currentTarget; - elem.classList.toggle("active"); - - const panel: HTMLElement = elem.nextElementSibling as HTMLElement; - const active = elem.classList.contains("active"); - if (active) { - panel.style.display = "block"; - this.setState({ - panelOpened: true, - }); - } else { - panel.style.display = "none"; - this.setState({ - panelOpened: false, - }); - } + this.setState({ + panelOpened: !this.state.panelOpened, + }); } render(): React.ReactNode { @@ -52,6 +39,8 @@ export class Accordion extends React.Component { className = this.props.headerClass; } + if(this.state.panelOpened) className += " active" + return ( <>