diff --git a/src/DevMenu.js b/src/DevMenu.js
index 3390f294d..d8b10ef85 100644
--- a/src/DevMenu.js
+++ b/src/DevMenu.js
@@ -3,6 +3,7 @@ import {Programs} from "./CreateProgram"
import {Factions} from "./Faction";
import {Player} from "./Player";
import {AllServers} from "./Server";
+import {hackWorldDaemon} from "./RedPill";
import {Terminal} from "./Terminal";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {createElement} from "../utils/uiHelpers/createElement";
@@ -69,7 +70,7 @@ export function createDevMenu() {
const statsHackingExpInput = createElement("input", {
class: "text-input",
- display: "block",
+ margin: "5px",
placeholder: "+/- hacking exp",
type: "number",
});
@@ -80,14 +81,13 @@ export function createDevMenu() {
Player.gainHackingExp(exp);
Player.updateSkillLevels();
},
- display: "block",
innerText: "Add Hacking Exp",
});
const statsStrengthExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- strength exp",
type: "number",
});
const statsStrengthExpButton = createElement("button", {
@@ -97,14 +97,13 @@ export function createDevMenu() {
Player.gainStrengthExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Strength Exp",
});
const statsDefenseExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- defense exp",
type: "number",
});
const statsDefenseExpButton = createElement("button", {
@@ -114,14 +113,13 @@ export function createDevMenu() {
Player.gainDefenseExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Defense Exp",
});
const statsDexterityExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- dexterity exp",
type: "number",
});
const statsDexterityExpButton = createElement("button", {
@@ -131,31 +129,29 @@ export function createDevMenu() {
Player.gainDexterityExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Dexterity Exp",
});
const statsAgilityExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- agility exp",
type: "number",
});
const statsAgilityExpButton = createElement("button", {
class: "std-button",
clickListener: () => {
- const exp = parseInt(statsAgilityExpButton.value);
+ const exp = parseInt(statsAgilityExpInput.value);
Player.gainAgilityExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Agility Exp",
});
const statsCharismaExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- charisma exp",
type: "number",
});
const statsCharismaExpButton = createElement("button", {
@@ -165,14 +161,13 @@ export function createDevMenu() {
Player.gainCharismaExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Charisma Exp",
});
const statsIntelligenceExpInput = createElement("input", {
class: "text-input",
- display: "block",
- placeholder: "+/- hacking exp",
+ margin: "5px",
+ placeholder: "+/- intelligence exp",
type: "number",
});
const statsIntelligenceExpButton = createElement("button", {
@@ -182,8 +177,7 @@ export function createDevMenu() {
Player.gainIntelligenceExp(exp);
Player.updateSkillLevels();
},
- display: "block",
- innerText: "Add Hacking Exp",
+ innerText: "Add Intelligence Exp",
});
const statsEnableIntelligenceButton = createElement("button", {
@@ -205,7 +199,10 @@ export function createDevMenu() {
// Factions
const factionsHeader = createElement("h2", {innerText: "Factions"});
- const factionsDropdown = createElement("select", {class: "dropdown"});
+ const factionsDropdown = createElement("select", {
+ class: "dropdown",
+ margin: "5px",
+ });
for (const i in Factions) {
factionsDropdown.options[factionsDropdown.options.length] = new Option(Factions[i].name, Factions[i].name);
}
@@ -222,7 +219,10 @@ export function createDevMenu() {
// Augmentations / Source Files
const augmentationsHeader = createElement("h2", {innerText: "Augmentations"});
- const augmentationsDropdown = createElement("select", {class: "dropdown"});
+ const augmentationsDropdown = createElement("select", {
+ class: "dropdown",
+ margin: "5px",
+ });
for (const i in AugmentationNames) {
const augName = AugmentationNames[i];
augmentationsDropdown.options[augmentationsDropdown.options.length] = new Option(augName, augName);
@@ -239,7 +239,10 @@ export function createDevMenu() {
// Programs
const programsHeader = createElement("h2", {innerText: "Programs"});
- const programsAddDropdown = createElement("select", {class: "dropdown"});
+ const programsAddDropdown = createElement("select", {
+ class: "dropdown",
+ margin: "5px",
+ });
for (const i in Programs) {
const progName = Programs[i].name;
programsAddDropdown.options[programsAddDropdown.options.length] = new Option(progName, progName);
@@ -351,24 +354,32 @@ export function createDevMenu() {
devMenuContainer.appendChild(statsHeader);
devMenuContainer.appendChild(statsHackingExpInput);
devMenuContainer.appendChild(statsHackingExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsStrengthExpInput);
devMenuContainer.appendChild(statsStrengthExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsDefenseExpInput);
devMenuContainer.appendChild(statsDefenseExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsDexterityExpInput);
devMenuContainer.appendChild(statsDexterityExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsAgilityExpInput);
devMenuContainer.appendChild(statsAgilityExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsCharismaExpInput);
devMenuContainer.appendChild(statsCharismaExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsIntelligenceExpInput);
devMenuContainer.appendChild(statsIntelligenceExpButton);
+ devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(statsEnableIntelligenceButton);
devMenuContainer.appendChild(statsDisableIntelligenceButton);
devMenuContainer.appendChild(factionsHeader);
devMenuContainer.appendChild(factionsDropdown);
devMenuContainer.appendChild(factionsAddButton);
devMenuContainer.appendChild(augmentationsHeader);
+ devMenuContainer.appendChild(augmentationsDropdown);
devMenuContainer.appendChild(augmentationsQueueButton);
devMenuContainer.appendChild(programsHeader);
devMenuContainer.appendChild(programsAddDropdown);
diff --git a/src/Gang.js b/src/Gang.js
index e0e9dbe1b..ddead62da 100644
--- a/src/Gang.js
+++ b/src/Gang.js
@@ -42,7 +42,7 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
const GangRespectToReputationRatio = 2; // Respect is divided by this to get rep gain
const MaximumGangMembers = 50;
const GangRecruitCostMultiplier = 2;
-const GangTerritoryUpdateTimer = 150;
+const CyclesPerTerritoryAndPowerUpdate = 100;
const AscensionMultiplierRatio = 10 / 100; // Portion of upgrade multiplier that is kept after ascending
// Switch between territory and management screen with 1 and 2
@@ -147,62 +147,9 @@ export function loadAllGangs(saveString) {
AllGangs = JSON.parse(saveString, Reviver);
}
-//Power is an estimate of a gang's ability to gain/defend territory
-let gangStoredPowerCycles = 0;
-function processAllGangPowerGains(numCycles=1) {
- if (!Player.inGang()) {return;}
- gangStoredPowerCycles += numCycles;
- if (gangStoredPowerCycles < 150) {return;}
- var playerGangName = Player.gang.facName;
- for (var name in AllGangs) {
- if (AllGangs.hasOwnProperty(name)) {
- if (name == playerGangName) {
- AllGangs[name].power += Player.gang.calculatePower();
- } else {
- var gain = Math.random() * 0.02; //TODO Adjust as necessary
- AllGangs[name].power += (gain);
- }
- }
- }
-
- gangStoredPowerCycles -= 150;
-}
-
-let gangStoredTerritoryCycles = 0;
-function processAllGangTerritory(numCycles=1) {
- if (!Player.inGang()) {return;}
- gangStoredTerritoryCycles += numCycles;
- if (gangStoredTerritoryCycles < GangTerritoryUpdateTimer) {return;}
-
- for (var i = 0; i < GangNames.length; ++i) {
- var other = getRandomInt(0, GangNames.length-1);
- while(other == i) {
- other = getRandomInt(0, GangNames.length-1);
- }
- var thisPwr = AllGangs[GangNames[i]].power;
- var otherPwr = AllGangs[GangNames[other]].power;
- var thisChance = thisPwr / (thisPwr + otherPwr);
-
- if (Math.random() < thisChance) {
- if (AllGangs[GangNames[other]].territory <= 0) {
- return;
- }
- AllGangs[GangNames[i]].territory += 0.0001;
- AllGangs[GangNames[other]].territory -= 0.0001;
- } else {
- if (AllGangs[GangNames[i]].territory <= 0) {
- return;
- }
- AllGangs[GangNames[i]].territory -= 0.0001;
- AllGangs[GangNames[other]].territory += 0.0001;
- }
- }
-
- gangStoredTerritoryCycles -= GangTerritoryUpdateTimer;
-}
-
-/* faction - Name of corresponding faction
- hacking - Boolean indicating whether its a hacking gang or not
+/**
+ * @param facName - Name of corresponding faction
+ * @param hacking - Boolean indicating whether or not its a hacking gang
*/
export function Gang(facName, hacking=false) {
this.facName = facName;
@@ -217,9 +164,16 @@ export function Gang(facName, hacking=false) {
this.wantedGainRate = 0;
this.moneyGainRate = 0;
- //When processing gains, this stores the number of cycles until some
- //limit is reached, and then calculates and applies the gains only at that limit
+ // When processing gains, this stores the number of cycles until some
+ // limit is reached, and then calculates and applies the gains only at that limit
this.storedCycles = 0;
+
+ // Separate variable to keep track of cycles for Territry + Power gang, which
+ // happens on a slower "clock" than normal processing
+ this.storedTerritoryAndPowerCycles = 0;
+
+ this.territoryClashChance = 0;
+ this.territoryWarfareEngaged = false;
}
Gang.prototype.process = function(numCycles=1) {
@@ -237,13 +191,11 @@ Gang.prototype.process = function(numCycles=1) {
try {
this.processGains(cycles);
this.processExperienceGains(cycles);
- processAllGangPowerGains(cycles);
- processAllGangTerritory(cycles);
+ this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch(e) {
exceptionAlert(`Exception caught when processing Gang: ${e}`);
}
-
}
Gang.prototype.processGains = function(numCycles=1) {
@@ -302,6 +254,64 @@ Gang.prototype.processGains = function(numCycles=1) {
}
}
+Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
+ this.storedTerritoryAndPowerCycles += numCycles;
+ if (this.storedTerritoryAndPowerCycles < CyclesPerTerritoryAndPowerUpdate) { return; }
+
+ // Process power first
+ var gangName = this.facName;
+ for (const name in AllGangs) {
+ if (AllGangs.hasOwnProperty(name)) {
+ if (name == gangName) {
+ AllGangs[name].power += this.calculatePower();
+ } else {
+ var gain = Math.random() * 0.02; //TODO Adjust as necessary
+ AllGangs[name].power += (gain);
+ }
+ }
+ }
+
+ // Determine if territory should be processed
+ if (!this.territoryWarfareEngaged) { return; }
+
+ // Then process territory
+ for (var i = 0; i < GangNames.length; ++i) {
+ const others = GangNames.filter((e) => {
+ return e !== i;
+ });
+ const other = getRandomInt(0, others.length - 1);
+
+ const thisGang = GangNames[i];
+ const otherGang = others[other];
+
+ // If either of the gangs involved in this clash is the player, determine
+ // whether to skip or process it using the clash chance
+ if (thisGang === gangName || otherGang === gangName) {
+ if (!(Math.random() <= this.territoryClashChance)) { continue; }
+ }
+
+ const thisPwr = AllGangs[thisGang].power;
+ const otherPwr = AllGangs[otherGang].power;
+ const thisChance = thisPwr / (thisPwr + otherPwr);
+
+ if (Math.random() < thisChance) {
+ if (AllGangs[otherGang].territory <= 0) {
+ return;
+ }
+ AllGangs[thisGang].territory += 0.0001;
+ AllGangs[otherGang].territory -= 0.0001;
+ } else {
+ if (AllGangs[thisGang].territory <= 0) {
+ return;
+ }
+ AllGangs[thisGang].territory -= 0.0001;
+ AllGangs[otherGang].territory += 0.0001;
+ }
+ }
+
+ this.storedTerritoryAndPowerCycles -= CyclesPerTerritoryAndPowerUpdate;
+}
+
Gang.prototype.canRecruitMember = function() {
if (this.members.length >= MaximumGangMembers) { return false; }
return (this.respect >= this.getRespectNeededToRecruitMember());
@@ -947,7 +957,7 @@ const UIElems = {
gangMemberFilter: null,
gangManageEquipmentButton: null,
gangMemberList: null,
- gangMemberPanels: null,
+ gangMemberPanels: {},
// Gang Equipment Upgrade Elements
gangMemberUpgradeBoxOpened: false,
@@ -958,6 +968,9 @@ const UIElems = {
// Gang Territory Elements
gangTerritoryDescText: null,
+ gangTerritoryWarfareCheckbox: null,
+ gangTerritoryWarfareCheckboxLabel: null,
+ gangTerritoryWarfareClashChance: null,
gangTerritoryInfoText: null,
}
@@ -1178,7 +1191,7 @@ Gang.prototype.displayGangContent = function() {
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.
" +
- "Territory gain and loss is processed automatically and is updated every ~30 seconds. Your chances " +
+ "Territory gain and loss is processed automatically and is updated every ~20 seconds. Your chances " +
"to gain and lose territory depend on your Gang's power, which is listed in the display below. " +
"Your gang's power 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 " +
@@ -1188,9 +1201,36 @@ Gang.prototype.displayGangContent = function() {
});
UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryDescText);
- var territoryBorder = createElement("fieldset", {width:"50%", display:"inline-block"});
+ // 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.gangTerritoryInfoText = createElement("p", {id:"gang-territory-info"});
+ UIElems.gangTerritoryWarfareCheckboxLabel = createElement("label", {
+ color: "white",
+ for: "gang-management-territory-warfare-checkbox",
+ innerText: "Engage in Territory Warfare",
+ tooltip: "Test",
+ });
+ UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareCheckbox);
+ UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareCheckboxLabel);
+
+ // Territory Clash chance
+ UIElems.gangTerritoryWarfareClashChance = createElement("p");
+ UIElems.gangTerritorySubpage.appendChild(UIElems.gangTerritoryWarfareClashChance);
+
+ // Territory info (percentages of territory owned for each gang)
+ UIElems.gangTerritorySubpage.appendChild(createElement("br"));
+ var territoryBorder = createElement("fieldset", {width:"50%", display:"block"});
+
+ UIElems.gangTerritoryInfoText = createElement("p");
territoryBorder.appendChild(UIElems.gangTerritoryInfoText);
UIElems.gangTerritorySubpage.appendChild(territoryBorder);
@@ -1205,6 +1245,7 @@ Gang.prototype.displayGangContent = function() {
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) {
@@ -1217,13 +1258,17 @@ Gang.prototype.displayGangMemberList = function() {
Gang.prototype.updateGangContent = function() {
if (!UIElems.gangContentCreated) { return; }
- if(UIElems.gangTerritorySubpage.style.display === "block") {
- //Update territory information
+ if (UIElems.gangTerritorySubpage.style.display === "block") {
+ // Territory Warfare Clash Chance
+ UIElems.gangTerritoryWarfareClashChance.innerText =
+ `Territory Clash Chance: ${numeralWrapper.format(this.gangTerritoryWarfareClashChance, '0.000%')}`;
+
+ // Update territory information
UIElems.gangTerritoryInfoText.innerHTML = "";
for (var gangname in AllGangs) {
if (AllGangs.hasOwnProperty(gangname)) {
var gangTerritoryInfo = AllGangs[gangname];
- let territory = gangTerritoryInfo.territory*100;
+ let territory = gangTerritoryInfo.territory * 100;
//Fix some rounding issues graphically
let displayNumber;
@@ -1356,6 +1401,10 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
if (!UIElems.gangContentCreated) { return; }
const name = memberObj.name;
+ // Clear/Update the UIElems map to keep track of this gang member's panel
+ UIElems.gangMemberPanels[name] = {};
+
+ // Create the accordion
var accordion = createAccordionElement({
id: name + "gang-member",
hdrText: name,
@@ -1364,6 +1413,8 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
const hdr = accordion[1];
const gangMemberDiv = accordion[2];
+ UIElems.gangMemberPanels[name]["panel"] = gangMemberDiv;
+
// Gang member content divided into 3 panels:
// Panel 1 - Shows member's stats & Ascension stuff
const statsDiv = createElement("div", {
@@ -1376,6 +1427,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
`Ag: x${numeralWrapper.format(memberObj.agi_mult * memberObj.agi_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.agi_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.agi_asc_mult, "0,0.00")} Asc)`,
`Ch: x${numeralWrapper.format(memberObj.cha_mult * memberObj.cha_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.cha_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.cha_asc_mult, "0,0.00")} Asc)`].join("
"),
});
+ UIElems.gangMemberPanels[name]["statsDiv"] = statsDiv;
const statsP = createElement("pre", {
display: "inline",
id: name + "gang-member-stats-text",
@@ -1518,7 +1570,7 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
if (!UIElems.gangContentCreated || !Player.inGang()) {return;}
var name = memberObj.name;
- //TODO Add upgrade information
+ // Update stats + exp
var stats = document.getElementById(name + "gang-member-stats-text");
if (stats) {
stats.innerText =
@@ -1530,6 +1582,22 @@ Gang.prototype.updateGangMemberDisplayElement = function(memberObj) {
`Charisma: ${formatNumber(memberObj.cha, 0)} (${numeralWrapper.format(memberObj.cha_exp, '(0.00a)')} exp)`].join("\n");
}
+ // Update tooltip for stat multipliers
+ const panel = UIElems.gangMemberPanels[name];
+ if (panel) {
+ const statsDiv = panel["statsDiv"];
+ if (statsDiv) {
+ statsDiv.firstChild.innerHTML =
+ [`Hk: x${numeralWrapper.format(memberObj.hack_mult * memberObj.hack_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.hack_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.hack_asc_mult, "0,0.00")} Asc)`,
+ `St: x${numeralWrapper.format(memberObj.str_mult * memberObj.str_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.str_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.str_asc_mult, "0,0.00")} Asc)`,
+ `Df: x${numeralWrapper.format(memberObj.def_mult * memberObj.def_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.def_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.def_asc_mult, "0,0.00")} Asc)`,
+ `Dx: x${numeralWrapper.format(memberObj.dex_mult * memberObj.dex_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.dex_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.dex_asc_mult, "0,0.00")} Asc)`,
+ `Ag: x${numeralWrapper.format(memberObj.agi_mult * memberObj.agi_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.agi_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.agi_asc_mult, "0,0.00")} Asc)`,
+ `Ch: x${numeralWrapper.format(memberObj.cha_mult * memberObj.cha_asc_mult, "0,0.00")}(x${numeralWrapper.format(memberObj.cha_mult, "0,0.00")} Up, x${numeralWrapper.format(memberObj.cha_asc_mult, "0,0.00")} Asc)`].join("
");
+ }
+ }
+
+ // Update info about gang member's earnings/gains
var gainInfo = document.getElementById(name + "gang-member-gain-info");
if (gainInfo) {
gainInfo.innerHTML =
@@ -1556,7 +1624,9 @@ Gang.prototype.clearUI = function() {
for (const prop in UIElems) {
UIElems[prop] = null;
- UIElems.gangContentCreated = false;
- UIElems.gangMemberUpgradeBoxOpened = false;
}
+
+ UIElems.gangContentCreated = false;
+ UIElems.gangMemberUpgradeBoxOpened = false;
+ UIElems.gangMemberPanels = {};
}