mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-20 05:05:47 +01:00
Initial v0.46.0 changes - Fixed BN9 bugs. Rebalanced BN11 and Corporations. Added memory to Dup sleeves. Various bug fixes
This commit is contained in:
parent
83fc4d81b2
commit
c4cb7daac5
@ -3,7 +3,7 @@
|
||||
|
||||
/* Pop-up boxes */
|
||||
.popup-box-container {
|
||||
display: none; /* Hidden by default */
|
||||
display: none; /* Initially hidden */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 10; /* Sit on top */
|
||||
left: 0;
|
||||
|
@ -80,6 +80,16 @@ when you normally install Augmentations.
|
||||
The cost of purchasing an Augmentation for a Duplicate Sleeve is **not** affected
|
||||
by how many Augmentations you have purchased for yourself, and vice versa.
|
||||
|
||||
Memory
|
||||
~~~~~~
|
||||
Sleeve memory dictates what a sleeve's synchronization will be when its reset by
|
||||
switching BitNodes. For example, if a sleeve has a memory of 10, then when you
|
||||
switch BitNodes its synchronization will initially be set to 10, rather than 1.
|
||||
|
||||
Memory can only be increased by purchasing upgrades from The Covenant.
|
||||
It is a persistent stat, meaning it never gets reset back to 1.
|
||||
The maximum possible value for a sleeve's memory is 100.
|
||||
|
||||
Re-sleeving
|
||||
^^^^^^^^^^^
|
||||
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
||||
|
@ -10,8 +10,8 @@ getSleeveStats() Netscript Function
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
shock: current shock of the sleeve [0-1],
|
||||
sync: current sync of the sleeve [0-1],
|
||||
shock: current shock of the sleeve [0-100],
|
||||
sync: current sync of the sleeve [0-100],
|
||||
hacking_skill: current hacking skill of the sleeve,
|
||||
strength: current strength of the sleeve,
|
||||
defense: current defense of the sleeve,
|
||||
|
@ -4,6 +4,6 @@ purchaseSleeveAug() Netscript Function
|
||||
.. js:function:: purchaseSleeveAug(sleeveNumber, augName)
|
||||
|
||||
:param int sleeveNumber: Index of the sleeve to buy an aug for. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
:param string augName: Name of the aug to buy. See :ref:`here <netscript_sleeveapi_referencingaduplicatesleeve>`
|
||||
:param string augName: Name of the aug to buy. Must be an exact match
|
||||
|
||||
Return true if the aug was purchased and installed on the sleeve.
|
@ -201,8 +201,9 @@ export function initBitNodes() {
|
||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking stat and experience gain are halved<br>" +
|
||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||
"The growth rate of servers is halved<br>" +
|
||||
"The growth rate of servers is significantly reduced<br>" +
|
||||
"Weakening a server is twice as effective<br>" +
|
||||
"Company wages are decreased by 50%<br>" +
|
||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||
@ -213,9 +214,9 @@ export function initBitNodes() {
|
||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%");
|
||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||
"To iterate is human, to recurse divine.<br><br>" +
|
||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||
@ -349,17 +350,20 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.45;
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 3;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 5;
|
||||
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.1;
|
||||
BitNodeMultipliers.HackExpGain = 0.1;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2.5;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
@ -385,9 +389,11 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.5;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.5;
|
||||
BitNodeMultipliers.ServerGrowthRate = 0.2;
|
||||
BitNodeMultipliers.ServerWeakenRate = 2;
|
||||
BitNodeMultipliers.CrimeMoney = 3;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
||||
@ -395,8 +401,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||
BitNodeMultipliers.InfiltrationMoney = 2.5;
|
||||
BitNodeMultipliers.InfiltrationRep = 2.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.01;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.1;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.25;
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 4;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
break;
|
||||
|
@ -78,8 +78,8 @@ const RanksPerSkillPoint = 3; //How many ranks needed to get 1 Skill
|
||||
|
||||
const ContractBaseMoneyGain = 250e3; //Base Money Gained per contract
|
||||
|
||||
const HrcHpGain = 2; // HP gained from Hyperbolic Regeneration Chamber
|
||||
const HrcStaminaGain = 0.1; // Stamina gained from Hyperbolic Regeneration Chamber
|
||||
const HrcHpGain = 2; // HP Gained from Hyperbolic Regeneration chamber
|
||||
const HrcStaminaGain = 1; // Percentage Stamina gained from Hyperbolic Regeneration Chamber
|
||||
|
||||
//DOM related variables
|
||||
var ActiveActionCssClass = "bladeburner-active-action";
|
||||
@ -1417,14 +1417,17 @@ Bladeburner.prototype.completeAction = function() {
|
||||
}
|
||||
this.startAction(this.action); // Repeat Action
|
||||
break;
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]:
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]: {
|
||||
Player.regenerateHp(HrcHpGain);
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + HrcStaminaGain);
|
||||
|
||||
const staminaGain = this.maxStamina * (HrcStaminaGain / 100);
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
|
||||
this.startAction(this.action);
|
||||
if (this.logging.general) {
|
||||
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${HrcHpGain} HP and gained ${HrcStaminaGain} stamina`);
|
||||
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${HrcHpGain} HP and gained ${numeralWrapper.format(staminaGain, "0.0")} stamina`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
|
||||
break;
|
||||
|
@ -116,7 +116,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
|
||||
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
|
||||
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
|
||||
InfiltrationExpPow: 0.7,
|
||||
InfiltrationExpPow: 0.8,
|
||||
|
||||
//Stock market constants
|
||||
WSEAccountCost: 200e6,
|
||||
@ -273,37 +273,20 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.45.1
|
||||
* Added two new Corporation Researches
|
||||
* General UI improvements (by hydroflame and koriar)
|
||||
* Bug Fix: Sleeve Netscript API should no longer cause Dynamic RAM errors
|
||||
* Bug Fix: sleeve.getSleeveStats() should now work properly
|
||||
v0.46.0
|
||||
* Added BitNode-9: Hacktocracy
|
||||
* Changed BitNode-11's multipliers to make it slightly harder overall
|
||||
* Source-File 11 is now slightly stronger
|
||||
* Added several functions to Netscript Sleeve API for buying Sleeve augmentations (by hydroflame)
|
||||
* Added a new stat for Duplicate Sleeves: Memory
|
||||
* Increase baseline experience earned from Infiltration, but it now gives diminishing returns (on exp) as you get to higher difficulties/levels
|
||||
* In Bladeburner, stamina gained from Hyperbolic Regeneration Chamber is now a percentage of your max stamina
|
||||
|
||||
v0.45.0
|
||||
* Corporation changes:
|
||||
** Decreased the time of a full market cycle from 15 seconds to 10 seconds.
|
||||
** This means that each Corporation 'state' will now only take 2 seconds, rather than 3
|
||||
** Increased initial salaries for newly-hired employees
|
||||
** Increased the cost multiplier for upgrading office size (the cost will increase faster)
|
||||
** The stats of your employees now has a slightly larger effect on production & sales
|
||||
** Added several new Research upgrades
|
||||
** Market-TA research now allows you to automatically set sale price at optimal values
|
||||
** Market-TA research now works for Products (not just Materials)
|
||||
** Reduced the amount of Scientific Research needed to unlock the Hi-Tech R&D Laboratory from 10k to 5k
|
||||
** Energy Material requirement of the Software industry reduced from 1 to 0.5
|
||||
** It is now slightly easier to increase the Software industry's production multiplier
|
||||
** Industries now have a maximum number of allowed products, starting at 3. This can be increased through research.
|
||||
** You can now see an approximation of how each material affects an industry's production multiplier by clicking the "?" help tip next to it
|
||||
** Significantly changed the effects of the different employee positions. See updated descriptions
|
||||
** Reduced the amount of money you gain from private investors
|
||||
** Training employees is now 3x more effective
|
||||
** Bug Fix: An industry's products are now properly separated between different cities
|
||||
* The QLink Augemntation is now significantly stronger, but also significantly more expensive (by hydroflame)
|
||||
* Added a Netscript API for Duplicate Sleeves (by hydroflame)
|
||||
* Modified the multipliers of BitNode-3 and BitNode-8 to make them slightly harder
|
||||
* After installing Augmentations, Duplicate Sleeves will now default to Synchronize if their Shock is 0
|
||||
* Bug Fix: Bladeburner's Hyperbolic Regeneration Chamber should no longer instantly refill all stamina
|
||||
* Bug Fix: growthAnalyze() function now properly accounts for BitNode multipliers
|
||||
* Bug Fix: The cost of purchasing Augmentations for Duplicate Sleeves no longer scales with how many Augs you've purchased for yourself
|
||||
* Corporation Changes:
|
||||
** 'Demand' value of products decreases more slowly
|
||||
** Bug Fix: Fixed a Corporation issue that broke the Market-TA2 Research
|
||||
|
||||
* Bug Fix: Money Statistics tracker was incorrectly recording profits when selling stocks manually
|
||||
* Bug Fix: Fixed an issue with the job requirement tooltip for security jobs
|
||||
`
|
||||
}
|
||||
|
@ -563,13 +563,15 @@ Industry.prototype.processMaterialMarket = function(marketCycles=1) {
|
||||
}
|
||||
}
|
||||
|
||||
//Process change in demand and competition for this industry's products
|
||||
// Process change in demand and competition for this industry's products
|
||||
Industry.prototype.processProductMarket = function(marketCycles=1) {
|
||||
//Demand gradually decreases, and competition gradually increases
|
||||
for (var name in this.products) {
|
||||
// Demand gradually decreases, and competition gradually increases
|
||||
for (const name in this.products) {
|
||||
if (this.products.hasOwnProperty(name)) {
|
||||
var product = this.products[name];
|
||||
var change = getRandomInt(1, 3) * 0.0004;
|
||||
const product = this.products[name];
|
||||
let change = getRandomInt(0, 3) * 0.0004;
|
||||
if (change === 0) { continue; }
|
||||
|
||||
if (this.type === Industries.Pharmaceutical || this.type === Industries.Software ||
|
||||
this.type === Industries.Robotics) {
|
||||
change *= 3;
|
||||
@ -770,7 +772,17 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
|
||||
* advertisingFactor
|
||||
* this.getSalesMultiplier());
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
const optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = mat.bCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + mat.bCost;
|
||||
}
|
||||
|
||||
// We'll store this "Optimal Price" in a property so that we don't have
|
||||
// to re-calculate it for the UI
|
||||
@ -1089,7 +1101,12 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
|
||||
const denominator = Math.sqrt(sqrtNumerator / sqrtDenominator);
|
||||
let optimalPrice;
|
||||
if (sqrtDenominator === 0 || denominator === 0) {
|
||||
optimalPrice = 0;
|
||||
if (sqrtNumerator === 0) {
|
||||
optimalPrice = 0; // No production
|
||||
} else {
|
||||
optimalPrice = product.pCost + markupLimit;
|
||||
console.warn(`In Corporation, found illegal 0s when trying to calculate MarketTA2 sale cost`);
|
||||
}
|
||||
} else {
|
||||
optimalPrice = (numerator / denominator) + product.pCost;
|
||||
}
|
||||
@ -1251,7 +1268,7 @@ Industry.prototype.getAdvertisingFactors = function() {
|
||||
|
||||
//Returns a multiplier based on a materials demand and competition that affects sales
|
||||
Industry.prototype.getMarketFactor = function(mat) {
|
||||
return mat.dmd * (100 - mat.cmp)/100;
|
||||
return Math.max(0.1, mat.dmd * (100 - mat.cmp) / 100);
|
||||
}
|
||||
|
||||
// Returns a boolean indicating whether this Industry has the specified Research
|
||||
|
@ -86,7 +86,7 @@ export class Material {
|
||||
this.mku = 6;
|
||||
break;
|
||||
case "Energy":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 80; this.cmpR = [65, 95];
|
||||
this.bCost = 2000; this.mv = 0.2;
|
||||
this.mku = 6;
|
||||
@ -122,26 +122,26 @@ export class Material {
|
||||
this.mku = 2;
|
||||
break;
|
||||
case "Real Estate":
|
||||
this.dmd = 50; this.dmdR = [5, 100];
|
||||
this.dmd = 50; this.dmdR = [5, 99];
|
||||
this.cmp = 50; this.cmpR = [25, 75];
|
||||
this.bCost = 80e3; this.mv = 1.5; //Less mv bc its processed twice
|
||||
this.mku = 1.5;
|
||||
break;
|
||||
case "Drugs":
|
||||
this.dmd = 60; this.dmdR = [45, 75];
|
||||
this.cmp = 70; this.cmpR = [40, 100];
|
||||
this.cmp = 70; this.cmpR = [40, 99];
|
||||
this.bCost = 40e3; this.mv = 1.6;
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "Robots":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 9];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 75e3; this.mv = 0.5; //Less mv bc its processed twice
|
||||
this.mku = 1;
|
||||
break;
|
||||
case "AI Cores":
|
||||
this.dmd = 90; this.dmdR = [80, 100];
|
||||
this.cmp = 90; this.cmpR = [80, 100];
|
||||
this.dmd = 90; this.dmdR = [80, 99];
|
||||
this.cmp = 90; this.cmpR = [80, 9];
|
||||
this.bCost = 15e3; this.mv = 0.8; //Less mv bc its processed twice
|
||||
this.mku = 0.5;
|
||||
break;
|
||||
|
@ -780,7 +780,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
@ -1052,7 +1057,12 @@ export class CorporationEventHandler {
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleLabel);
|
||||
useTa2AutoSaleDiv.appendChild(useTa2AutoSaleCheckbox);
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, closeBtn]);
|
||||
const ta2OverridesTa1 = createElement("p", {
|
||||
innerText: "Note that Market-TA.II overrides Market-TA.I. This means that if " +
|
||||
"both are enabled, then Market-TA.II will take effect, not Market-TA.I"
|
||||
});
|
||||
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, ta2Text, ta2Input, useTa2AutoSaleDiv, ta2OverridesTa1, closeBtn]);
|
||||
} else {
|
||||
// Market-TA.I only
|
||||
createPopup(popupId, [ta1, useTa1AutoSaleDiv, closeBtn]);
|
||||
|
@ -301,7 +301,7 @@ export class IndustryOffice extends BaseReactComponent {
|
||||
|
||||
<p>Avg Employee Morale: {numeralWrapper.format(avgMorale, "0.000")}</p>
|
||||
<p>Avg Employee Happiness: {numeralWrapper.format(avgHappiness, "0.000")}</p>
|
||||
<p>Avg Energy Morale: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Avg Employee Energy: {numeralWrapper.format(avgEnergy, "0.000")}</p>
|
||||
<p>Total Employee Salary: {numeralWrapper.formatMoney(totalSalary)}</p>
|
||||
{
|
||||
vechain &&
|
||||
|
@ -218,7 +218,7 @@ function MaterialComponent(props) {
|
||||
mat.buy === 0 && mat.imp === 0;
|
||||
|
||||
// Purchase material button
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nf)})`;
|
||||
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
|
||||
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
|
||||
const purchaseButtonOnClick = eventHandler.createPurchaseMaterialPopup.bind(eventHandler, mat, division, warehouse);
|
||||
|
||||
@ -229,9 +229,9 @@ function MaterialComponent(props) {
|
||||
let sellButtonText;
|
||||
if (mat.sllman[0]) {
|
||||
if (isString(mat.sllman[1])) {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${mat.sllman[1]})`
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${mat.sllman[1]})`
|
||||
} else {
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nf)}/${numeralWrapper.format(mat.sllman[1], nf)})`;
|
||||
sellButtonText = `Sell (${numeralWrapper.format(mat.sll, nfB)}/${numeralWrapper.format(mat.sllman[1], nfB)})`;
|
||||
}
|
||||
|
||||
if (mat.marketTa2) {
|
||||
@ -469,7 +469,7 @@ export class IndustryWarehouse extends BaseReactComponent {
|
||||
return (
|
||||
<div className={"cmpy-mgmt-warehouse-panel"}>
|
||||
<p className={"tooltip"} style={sizeUsageStyle}>
|
||||
Storage: {numeralWrapper.format(warehouse.sizeUsed, "0.000")} / {numeralWrapper.format(warehouse.size, "0.000")}
|
||||
Storage: {numeralWrapper.formatBigNumber(warehouse.sizeUsed)} / {numeralWrapper.formatBigNumber(warehouse.size)}
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={{__html: warehouse.breakdown}}></span>
|
||||
</p>
|
||||
|
||||
|
@ -14,7 +14,7 @@ import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
|
||||
import { createPurchaseSleevesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
import { createSleevePurchasesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
|
||||
|
||||
import {Page, routing} from "../ui/navigationTracking";
|
||||
import {numeralWrapper} from "../ui/numeralFormat";
|
||||
@ -348,7 +348,7 @@ function displayFactionContent(factionName) {
|
||||
class: "std-button",
|
||||
innerText: "Purchase Duplicate Sleeves",
|
||||
clickListener: () => {
|
||||
createPurchaseSleevesFromCovenantPopup(Player);
|
||||
createSleevePurchasesFromCovenantPopup(Player);
|
||||
}
|
||||
}));
|
||||
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
|
||||
|
@ -19,7 +19,8 @@ import { generateRandomContractOnHome } from "../CodingContractGenerator
|
||||
import { iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "../InteractiveTutorial";
|
||||
import { Player } from "../Player";
|
||||
import { AddToAllServers } from "../Server/AllServers";
|
||||
import { AddToAllServers,
|
||||
AllServers } from "../Server/AllServers";
|
||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
@ -33,9 +34,10 @@ import { HacknetRoot } from "./ui/Root";
|
||||
let hacknetNodesDiv;
|
||||
function hacknetNodesInit() {
|
||||
hacknetNodesDiv = document.getElementById("hacknet-nodes-container");
|
||||
document.removeEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit, false);
|
||||
document.addEventListener("DOMContentLoaded", hacknetNodesInit);
|
||||
|
||||
// Returns a boolean indicating whether the player has Hacknet Servers
|
||||
// (the upgraded form of Hacknet Nodes)
|
||||
@ -73,7 +75,7 @@ export function purchaseHacknet() {
|
||||
});
|
||||
|
||||
Player.loseMoney(cost);
|
||||
Player.hacknetNodes.push(server);
|
||||
Player.hacknetNodes.push(server.ip);
|
||||
|
||||
// Configure the HacknetServer to actually act as a Server
|
||||
AddToAllServers(server);
|
||||
@ -308,7 +310,8 @@ function processAllHacknetServerEarnings(numCycles) {
|
||||
|
||||
let hashes = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
hashes += Player.hacknetNodes[i].process(numCycles);
|
||||
const hserver = AllServers[Player.hacknetNodes[i]]; // hacknetNodes array only contains the IP addresses
|
||||
hashes += hserver.process(numCycles);
|
||||
}
|
||||
|
||||
Player.hashManager.storeHashes(hashes);
|
||||
@ -316,16 +319,6 @@ function processAllHacknetServerEarnings(numCycles) {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
export function getHacknetNode(name) {
|
||||
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (Player.hacknetNodes[i].name == name) {
|
||||
return Player.hacknetNodes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||
if (!(Player.hashManager instanceof HashManager)) {
|
||||
console.error(`Player does not have a HashManager`);
|
||||
@ -423,7 +416,6 @@ export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log("Hash Upgrade successfully purchased");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ import { Generic_fromJSON,
|
||||
export const HacknetServerHashesPerLevel: number = 0.001;
|
||||
|
||||
// Constants for Hacknet Server purchase/upgrade costs
|
||||
export const BaseCostForHacknetServer: number = 10e3;
|
||||
export const BaseCostForHacknetServer: number = 50e3;
|
||||
export const BaseCostFor1GBHacknetServerRam: number = 200e3;
|
||||
export const BaseCostForHacknetServerCore: number = 1e6;
|
||||
export const BaseCostForHacknetServerCache: number = 10e6;
|
||||
@ -37,7 +37,7 @@ export const MaxNumberHacknetServers: number = 25; // Max number of Hac
|
||||
export const HacknetServerMaxLevel: number = 300;
|
||||
export const HacknetServerMaxRam: number = 8192;
|
||||
export const HacknetServerMaxCores: number = 128;
|
||||
export const HacknetServerMaxCache: number = 15; // Max cache level. So max capacity is 2 ^ 12
|
||||
export const HacknetServerMaxCache: number = 15;
|
||||
|
||||
interface IConstructorParams {
|
||||
adminRights?: boolean;
|
||||
@ -306,6 +306,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
this.maxRam *= 2;
|
||||
}
|
||||
this.maxRam = Math.round(this.maxRam);
|
||||
this.updateHashRate(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -321,7 +322,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
|
||||
}
|
||||
|
||||
updateHashCapacity(): void {
|
||||
this.hashCapacity = 16 * Math.pow(2, this.cache);
|
||||
this.hashCapacity = 32 * Math.pow(2, this.cache);
|
||||
}
|
||||
|
||||
updateHashRate(p: IPlayer): void {
|
||||
|
@ -11,6 +11,7 @@ import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { IMap } from "../types";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { AllServers } from "../Server/AllServers";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
Reviver } from "../../utils/JSONReviver";
|
||||
@ -106,13 +107,29 @@ export class HashManager {
|
||||
}
|
||||
|
||||
updateCapacity(p: IPlayer): void {
|
||||
if (p.hacknetNodes.length <= 0) { this.capacity = 0; }
|
||||
if (!(p.hacknetNodes[0] instanceof HacknetServer)) { this.capacity = 0; }
|
||||
if (p.hacknetNodes.length <= 0) {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the Player's `hacknetNodes` property actually holds Hacknet Servers
|
||||
const ip: string = <string>p.hacknetNodes[0];
|
||||
if (typeof ip !== "string") {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const hserver = <HacknetServer>AllServers[ip];
|
||||
if (!(hserver instanceof HacknetServer)) {
|
||||
this.capacity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let total: number = 0;
|
||||
for (let i = 0; i < p.hacknetNodes.length; ++i) {
|
||||
const hacknetServer = <HacknetServer>(p.hacknetNodes[i]);
|
||||
total += hacknetServer.hashCapacity;
|
||||
const h = <HacknetServer>AllServers[<string>p.hacknetNodes[i]];
|
||||
total += h.hashCapacity;
|
||||
}
|
||||
|
||||
this.capacity = total;
|
||||
|
@ -17,8 +17,10 @@ import { getCostOfNextHacknetNode,
|
||||
purchaseHacknet } from "../HacknetHelpers";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { PopupCloseButton } from "../../ui/React/PopupCloseButton";
|
||||
|
||||
export const PurchaseMultipliers = Object.freeze({
|
||||
"x1": 1,
|
||||
@ -52,7 +54,12 @@ export class HacknetRoot extends React.Component {
|
||||
let total = 0;
|
||||
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
|
||||
if (hasHacknetServers()) {
|
||||
total += Player.hacknetNodes[i].hashRate;
|
||||
const hserver = AllServers[Player.hacknetNodes[i]];
|
||||
if (hserver) {
|
||||
total += hserver.hashRate;
|
||||
} else {
|
||||
console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`)
|
||||
}
|
||||
} else {
|
||||
total += Player.hacknetNodes[i].moneyGainRatePerSecond;
|
||||
}
|
||||
@ -97,10 +104,14 @@ export class HacknetRoot extends React.Component {
|
||||
// HacknetNode components
|
||||
const nodes = Player.hacknetNodes.map((node) => {
|
||||
if (hasHacknetServers()) {
|
||||
const hserver = AllServers[node];
|
||||
if (hserver == null) {
|
||||
throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`);
|
||||
}
|
||||
return (
|
||||
<HacknetServer
|
||||
key={node.hostname}
|
||||
node={node}
|
||||
key={hserver.hostname}
|
||||
node={hserver}
|
||||
purchaseMultiplier={this.state.purchaseMultiplier}
|
||||
recalculate={this.recalculateTotalProduction.bind(this)}
|
||||
/>
|
||||
|
@ -54,7 +54,7 @@ function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff)
|
||||
|
||||
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||
if (!this.clearanceLevel || isNaN(this.clearanceLevel) || !this.maxClearanceLevel ||isNaN(this.maxClearanceLevel)) return 1;
|
||||
return 2 * this.clearanceLevel / this.maxClearanceLevel;
|
||||
return 2.5 * this.clearanceLevel / this.maxClearanceLevel;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||
@ -64,7 +64,7 @@ InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedHackingExp = function() {
|
||||
if(!this.hackingExpGained || isNaN(this.hackingExpGained)) return 0;
|
||||
return Math.pow(this.hackingExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
return Math.pow(this.hackingExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||
@ -73,8 +73,8 @@ InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedStrengthExp = function() {
|
||||
if(!this.strExpGained || isNaN(this.strExpGained)) return 0;
|
||||
return Math.pow(this.strExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
if (!this.strExpGained || isNaN(this.strExpGained)) return 0;
|
||||
return Math.pow(this.strExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||
@ -83,8 +83,8 @@ InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDefenseExp = function() {
|
||||
if(!this.defExpGained || isNaN(this.defExpGained)) return 0;
|
||||
return Math.pow(this.defExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
if (!this.defExpGained || isNaN(this.defExpGained)) return 0;
|
||||
return Math.pow(this.defExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||
@ -93,8 +93,8 @@ InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDexterityExp = function() {
|
||||
if(!this.dexExpGained || isNaN(this.dexExpGained)) return 0;
|
||||
return Math.pow(this.dexExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
if (!this.dexExpGained || isNaN(this.dexExpGained)) return 0;
|
||||
return Math.pow(this.dexExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||
@ -103,8 +103,8 @@ InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedAgilityExp = function() {
|
||||
if(!this.agiExpGained || isNaN(this.agiExpGained)) return 0;
|
||||
return Math.pow(this.agiExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
if (!this.agiExpGained || isNaN(this.agiExpGained)) return 0;
|
||||
return Math.pow(this.agiExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||
@ -113,8 +113,8 @@ InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedCharismaExp = function() {
|
||||
if(!this.chaExpGained || isNaN(this.chaExpGained)) return 0;
|
||||
return Math.pow(this.chaExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
if (!this.chaExpGained || isNaN(this.chaExpGained)) return 0;
|
||||
return Math.pow(this.chaExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
||||
|
@ -327,7 +327,7 @@ function displayLocationContent() {
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]], networkEngineerJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.BusinessCompanyPositions[0]], businessJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]], businessConsultantJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.SecurityCompanyPositions[0]], securityJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.SecurityCompanyPositions[2]], securityJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.AgentCompanyPositions[0]], agentJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.MiscCompanyPositions[1]], employeeJob);
|
||||
setJobRequirementTooltip(loc, CompanyPositions[posNames.PartTimeCompanyPositions[1]], employeePartTimeJob);
|
||||
|
@ -196,8 +196,11 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre
|
||||
}
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = threads;
|
||||
server.runScript(runningScriptObj, Player); // Push onto runningScripts
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
|
||||
// Push onto runningScripts.
|
||||
// This has to come after addWorkerScript() because that fn updates RAM usage
|
||||
server.runScript(runningScriptObj, Player);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export interface IPlayer {
|
||||
corporation: any;
|
||||
currentServer: string;
|
||||
factions: string[];
|
||||
hacknetNodes: (HacknetNode | HacknetServer)[];
|
||||
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
|
||||
hasWseAccount: boolean;
|
||||
jobs: IMap<string>;
|
||||
karma: number;
|
||||
|
@ -114,10 +114,9 @@ export class Sleeve extends Person {
|
||||
logs: string[] = [];
|
||||
|
||||
/**
|
||||
* Clone retains memory% of exp upon prestige. If exp would be lower than previously
|
||||
* kept exp, nothing happens
|
||||
* Clone retains 'memory' synchronization (and maybe exp?) upon prestige/installing Augs
|
||||
*/
|
||||
memory: number = 0;
|
||||
memory: number = 1;
|
||||
|
||||
/**
|
||||
* Sleeve shock. Number between 0 and 100
|
||||
@ -339,6 +338,31 @@ export class Sleeve extends Person {
|
||||
p.gainMoney(gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cost of upgrading this sleeve's memory by a certain amount
|
||||
*/
|
||||
getMemoryUpgradeCost(n: number): number {
|
||||
const amt = Math.round(n);
|
||||
if (amt < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.memory + amt > 100) {
|
||||
return this.getMemoryUpgradeCost(100 - this.memory);
|
||||
}
|
||||
|
||||
const mult = 1.02;
|
||||
const baseCost = 1e12;
|
||||
let currCost = 0;
|
||||
let currMemory = this.memory-1;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
currCost += (Math.pow(mult, currMemory));
|
||||
++currMemory;
|
||||
}
|
||||
|
||||
return currCost * baseCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reputation gain for the current task
|
||||
* Only applicable when working for company or faction
|
||||
@ -408,6 +432,20 @@ export class Sleeve extends Person {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on every sleeve for a Source File prestige
|
||||
*/
|
||||
prestige(p: IPlayer) {
|
||||
this.resetTaskStatus();
|
||||
this.earningsForSleeves = createTaskTracker();
|
||||
this.earningsForPlayer = createTaskTracker();
|
||||
this.logs = [];
|
||||
this.shock = 1;
|
||||
this.storedCycles = 0;
|
||||
this.sync = Math.max(this.memory, 1);
|
||||
this.shockRecovery(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process loop
|
||||
* Returns an object containing the amount of experience that should be
|
||||
@ -818,6 +856,15 @@ export class Sleeve extends Person {
|
||||
return true;
|
||||
}
|
||||
|
||||
upgradeMemory(n: number): void {
|
||||
if (n < 0) {
|
||||
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.memory = Math.min(100, Math.round(this.memory + n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
|
@ -1,48 +1,18 @@
|
||||
/**
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant,
|
||||
* as well as the purchasing of upgrades (memory)
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { yesNoBoxCreate,
|
||||
yesNoBoxClose,
|
||||
yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton } from "../../../utils/YesNoBox";
|
||||
import { CovenantPurchasesRoot } from "./ui/CovenantPurchasesRoot";
|
||||
import { createPopup,
|
||||
removePopup } from "../../ui/React/createPopup";
|
||||
|
||||
export const MaxSleevesFromCovenant: number = 5;
|
||||
export const BaseCostPerSleeve: number = 10e12;
|
||||
export const PopupId: string = "covenant-sleeve-purchases-popup";
|
||||
|
||||
export function createPurchaseSleevesFromCovenantPopup(p: IPlayer) {
|
||||
if (p.sleevesFromCovenant >= MaxSleevesFromCovenant) { return; }
|
||||
|
||||
// First sleeve purchased costs the base amount. Then, the price of
|
||||
// each successive one increases by the same amount
|
||||
const baseCostPerExtraSleeve: number = 10e12;
|
||||
const cost: number = (p.sleevesFromCovenant + 1) * baseCostPerExtraSleeve;
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
|
||||
yesBtn!.addEventListener("click", () => {
|
||||
if (p.canAfford(cost)) {
|
||||
p.loseMoney(cost);
|
||||
p.sleevesFromCovenant += 1;
|
||||
p.sleeves.push(new Sleeve(p));
|
||||
yesNoBoxClose();
|
||||
} else {
|
||||
dialogBoxCreate("You cannot afford to purchase a Duplicate Sleeve", false);
|
||||
}
|
||||
});
|
||||
|
||||
noBtn!.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
const txt = `Would you like to purchase an additional Duplicate Sleeve from The Covenant for ` +
|
||||
`${numeralWrapper.formatMoney(cost)}?<br><br>` +
|
||||
`These Duplicate Sleeves are permanent. You can purchase a total of 5 Duplicate ` +
|
||||
`Sleeves from The Covenant`;
|
||||
yesNoBoxCreate(txt);
|
||||
export function createSleevePurchasesFromCovenantPopup(p: IPlayer) {
|
||||
const removePopupFn = removePopup.bind(null, PopupId);
|
||||
createPopup(PopupId, CovenantPurchasesRoot, { p: p, closeFn: removePopupFn });
|
||||
}
|
||||
|
@ -383,7 +383,8 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
`HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}`,
|
||||
`City: ${sleeve.city}`,
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>");
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`,
|
||||
`Memory: ${numeralWrapper.format(sleeve.memory, "0")}`].join("<br>");
|
||||
|
||||
let repGainText: string = "";
|
||||
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
|
||||
|
@ -45,5 +45,13 @@ export const SleeveFaq: string =
|
||||
"are not available for sleeves.<br><br>",
|
||||
|
||||
"<strong><u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u></strong><br>",
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations."
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations.<br><br>",
|
||||
|
||||
"<strong><u>What is Memory?</u></strong><br>",
|
||||
"Sleeve memory dictates what a sleeve's synchronization will be",
|
||||
"when its reset by switching BitNodes. For example, if a sleeve has a memory of 25,",
|
||||
"then when you switch BitNodes its synchronization will initially be set to 25, rather than 1.<br><br>",
|
||||
"Memory can only be increased by purchasing upgrades from The Covenant. It is a",
|
||||
"persistent stat, meaning it never gets resets back to 1. The maximum possible",
|
||||
"value for a sleeve's memory is 100."
|
||||
].join(" ");
|
||||
|
112
src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx
Normal file
112
src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Root React component for the popup that lets player purchase Duplicate
|
||||
* Sleeves and Sleeve-related upgrades from The Covenant
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CovenantSleeveUpgrades } from "./CovenantSleeveUpgrades";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { BaseCostPerSleeve,
|
||||
MaxSleevesFromCovenant,
|
||||
PopupId } from "../SleeveCovenantPurchases";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
|
||||
import { PopupCloseButton } from "../../../ui/React/PopupCloseButton";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
|
||||
import { dialogBoxCreate } from "../../../../utils/DialogBox";
|
||||
|
||||
interface IProps {
|
||||
closeFn: () => void;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
update: number;
|
||||
}
|
||||
|
||||
export class CovenantPurchasesRoot extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
update: 0,
|
||||
}
|
||||
|
||||
this.rerender = this.rerender.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cost to purchase a new Duplicate Sleeve
|
||||
*/
|
||||
purchaseCost(): number {
|
||||
return (this.props.p.sleevesFromCovenant + 1) * BaseCostPerSleeve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a rerender by just changing an arbitrary state value
|
||||
*/
|
||||
rerender() {
|
||||
this.setState((state: IState) => ({
|
||||
update: state.update + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
// Purchasing a new Duplicate Sleeve
|
||||
let purchaseDisabled = false;
|
||||
if (!this.props.p.canAfford(this.purchaseCost())) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
if (this.props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
const purchaseOnClick = () => {
|
||||
if (this.props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) { return; }
|
||||
|
||||
if (this.props.p.canAfford(this.purchaseCost())) {
|
||||
this.props.p.loseMoney(this.purchaseCost());
|
||||
this.props.p.sleevesFromCovenant += 1;
|
||||
this.props.p.sleeves.push(new Sleeve(this.props.p));
|
||||
this.rerender();
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Purchasing Upgrades for Sleeves
|
||||
const upgradePanels = [];
|
||||
for (let i = 0; i < this.props.p.sleeves.length; ++i) {
|
||||
const sleeve = this.props.p.sleeves[i];
|
||||
upgradePanels.push(
|
||||
<CovenantSleeveUpgrades {...this.props} sleeve={sleeve} index={i} rerender={this.rerender} key={i} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PopupCloseButton popup={PopupId} text={"Close"} />
|
||||
<p>
|
||||
Would you like to purchase an additional Duplicate Sleeve from The Covenant
|
||||
for {numeralWrapper.formatMoney(this.purchaseCost())}?
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
These Duplicate Sleeves are permanent (they persist through BitNodes). You can
|
||||
purchase a total of {MaxSleevesFromCovenant} from The Covenant.
|
||||
</p>
|
||||
<StdButton disabled={purchaseDisabled} onClick={purchaseOnClick} text={"Purchase"} />
|
||||
<br /><br />
|
||||
<p>
|
||||
Here, you can also purchase upgrades for your Duplicate Sleeves. These upgrades
|
||||
are also permanent, meaning they persist across BitNodes.
|
||||
</p>
|
||||
{upgradePanels}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
97
src/PersonObjects/Sleeve/ui/CovenantSleeveMemoryUpgrade.tsx
Normal file
97
src/PersonObjects/Sleeve/ui/CovenantSleeveMemoryUpgrade.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* React component for a panel that lets you purchase upgrades for a Duplicate
|
||||
* Sleeve's Memory (through The Covenant)
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
amt: number;
|
||||
}
|
||||
|
||||
export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
amt: 1,
|
||||
}
|
||||
|
||||
this.changePurchaseAmount = this.changePurchaseAmount.bind(this);
|
||||
this.purchaseMemory = this.purchaseMemory.bind(this);
|
||||
}
|
||||
|
||||
changePurchaseAmount(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
const n: number = parseInt(e.target.value);
|
||||
|
||||
this.setState({
|
||||
amt: n,
|
||||
});
|
||||
}
|
||||
|
||||
getPurchaseCost(): number {
|
||||
if (isNaN(this.state.amt)) { return Infinity; }
|
||||
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
if (this.state.amt > maxMemory) { return Infinity; }
|
||||
|
||||
return this.props.sleeve.getMemoryUpgradeCost(this.state.amt);
|
||||
}
|
||||
|
||||
purchaseMemory(): void {
|
||||
const cost = this.getPurchaseCost();
|
||||
if (this.props.p.canAfford(cost)) {
|
||||
this.props.sleeve.upgradeMemory(this.state.amt);
|
||||
this.props.p.loseMoney(cost);
|
||||
this.props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const inputId = `sleeve-${this.props.index}-memory-upgrade-input`;
|
||||
|
||||
// Memory cannot go above 100
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
|
||||
// Purchase button props
|
||||
const cost = this.getPurchaseCost();
|
||||
const purchaseBtnDisabled = !this.props.p.canAfford(cost);
|
||||
let purchaseBtnText;
|
||||
if (this.state.amt > maxMemory) {
|
||||
purchaseBtnText = `Memory cannot exceed 100`;
|
||||
} else if (isNaN(this.state.amt)) {
|
||||
purchaseBtnText = "Invalid value";
|
||||
} else {
|
||||
purchaseBtnText = `Purchase ${this.state.amt} memory - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2><u>Upgrade Memory</u></h2>
|
||||
<p>
|
||||
Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory
|
||||
is 100 (current: {numeralWrapper.format(this.props.sleeve.memory, "0")})
|
||||
</p>
|
||||
|
||||
<label htmlFor={inputId}>
|
||||
Amount of memory to purchase (must be an integer):
|
||||
</label>
|
||||
<input id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={this.state.amt} />
|
||||
<br />
|
||||
<StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnText} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
28
src/PersonObjects/Sleeve/ui/CovenantSleeveUpgrades.tsx
Normal file
28
src/PersonObjects/Sleeve/ui/CovenantSleeveUpgrades.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* React Component for a panel that lets you purchase upgrades for a single
|
||||
* Duplicate Sleeve through The Covenant
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CovenantSleeveMemoryUpgrade } from "./CovenantSleeveMemoryUpgrade";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export class CovenantSleeveUpgrades extends React.Component<IProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<div className={"bladeburner-action"}>
|
||||
<h1>Duplicate Sleeve {this.props.index}</h1>
|
||||
<CovenantSleeveMemoryUpgrade {...this.props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import { Faction } from "./Faction/Faction";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { displayFactionContent } from "./Faction/FactionHelpers";
|
||||
import {Gang, resetGangs} from "./Gang";
|
||||
import { hasHacknetServers } from "./Hacknet/HacknetHelpers";
|
||||
import { HashManager } from "./Hacknet/HashManager";
|
||||
import {Locations} from "./Locations";
|
||||
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
|
||||
@ -117,7 +118,7 @@ function PlayerObject() {
|
||||
this.purchasedServers = []; //IP Addresses of purchased servers
|
||||
|
||||
// Hacknet Nodes/Servers
|
||||
this.hacknetNodes = [];
|
||||
this.hacknetNodes = []; // Note: For Hacknet Servers, this array holds the IP addresses of the servers
|
||||
this.hashManager = new HashManager();
|
||||
|
||||
//Factions
|
||||
@ -382,8 +383,12 @@ PlayerObject.prototype.prestigeSourceFile = function() {
|
||||
// Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists)
|
||||
this.sleeves.length = SourceFileFlags[10] + this.sleevesFromCovenant;
|
||||
for (let i = 0; i < this.sleeves.length; ++i) {
|
||||
if (this.sleeves[i] instanceof Sleeve) {
|
||||
this.sleeves[i].prestige(this);
|
||||
} else {
|
||||
this.sleeves[i] = new Sleeve(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.isWorking = false;
|
||||
this.currentWorkFactionName = "";
|
||||
@ -2333,11 +2338,20 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
var totalHacknetRam = 0;
|
||||
var totalHacknetCores = 0;
|
||||
var totalHacknetLevels = 0;
|
||||
for (var i = 0; i < this.hacknetNodes.length; ++i) {
|
||||
for (let i = 0; i < this.hacknetNodes.length; ++i) {
|
||||
if (hasHacknetServers()) {
|
||||
const hserver = AllServers[this.hacknetNodes[i]];
|
||||
if (hserver) {
|
||||
totalHacknetLevels += hserver.level;
|
||||
totalHacknetRam += hserver.maxRam;
|
||||
totalHacknetCores += hserver.cores;
|
||||
}
|
||||
} else {
|
||||
totalHacknetLevels += this.hacknetNodes[i].level;
|
||||
totalHacknetRam += this.hacknetNodes[i].ram;
|
||||
totalHacknetCores += this.hacknetNodes[i].cores;
|
||||
}
|
||||
}
|
||||
if (!netburnersFac.isBanned && !netburnersFac.isMember && !netburnersFac.alreadyInvited &&
|
||||
this.hacking_skill >= 80 && totalHacknetRam >= 8 &&
|
||||
totalHacknetCores >= 4 && totalHacknetLevels >= 100) {
|
||||
|
@ -2,6 +2,8 @@ import { Server } from "./Server";
|
||||
import { SpecialServerIps } from "./SpecialServerIps";
|
||||
import { serverMetadata } from "./data/servers";
|
||||
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
|
||||
import { IMap } from "../types";
|
||||
import { createRandomIp,
|
||||
ipExists } from "../../utils/IPAddress";
|
||||
@ -11,7 +13,7 @@ import { Reviver } from "../../utils/JSONReviver";
|
||||
// Map of all Servers that exist in the game
|
||||
// Key (string) = IP
|
||||
// Value = Server object
|
||||
export let AllServers: IMap<Server> = {};
|
||||
export let AllServers: IMap<Server | HacknetServer> = {};
|
||||
|
||||
// Saftely add a Server to the AllServers map
|
||||
export function AddToAllServers(server: Server): void {
|
||||
|
@ -3,6 +3,7 @@ import { Server } from "./Server";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
|
||||
@ -89,7 +90,7 @@ export function prestigeHomeComputer(homeComp: Server) {
|
||||
|
||||
//Returns server object with corresponding hostname
|
||||
// Relatively slow, would rather not use this a lot
|
||||
export function GetServerByHostname(hostname: string): Server | null {
|
||||
export function GetServerByHostname(hostname: string): Server | HacknetServer | null {
|
||||
for (var ip in AllServers) {
|
||||
if (AllServers.hasOwnProperty(ip)) {
|
||||
if (AllServers[ip].hostname == hostname) {
|
||||
@ -102,7 +103,7 @@ export function GetServerByHostname(hostname: string): Server | null {
|
||||
}
|
||||
|
||||
//Get server by IP or hostname. Returns null if invalid
|
||||
export function getServer(s: string): Server | null {
|
||||
export function getServer(s: string): Server | HacknetServer | null {
|
||||
if (!isValidIPAddress(s)) {
|
||||
return GetServerByHostname(s);
|
||||
}
|
||||
|
@ -68,9 +68,9 @@ function initSourceFiles() {
|
||||
SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
|
||||
"at that company by 1% per favor (rather than just the reputation gain). This Source-File also " +
|
||||
" increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%<br>");
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%<br>");
|
||||
SourceFiles["SourceFile12"] = new SourceFile(12, "This Source-File increases all your multipliers by 1% per level. This effect is multiplicative with itself. " +
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
||||
}
|
||||
@ -194,7 +194,7 @@ function applySourceFile(srcFile) {
|
||||
case 11: //The Big Crash
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (24 / (Math.pow(2, i)));
|
||||
mult += (32 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.work_money_mult *= incMult;
|
||||
|
@ -462,9 +462,10 @@ function sellStock(stock, shares) {
|
||||
shares = Math.round(shares);
|
||||
if (shares > stock.playerShares) {shares = stock.playerShares;}
|
||||
if (shares === 0) {return false;}
|
||||
var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
|
||||
const gains = stock.price * shares - CONSTANTS.StockMarketCommission;
|
||||
const netProfit = ((stock.price - stock.playerAvgPx) * shares) - CONSTANTS.StockMarketCommission;
|
||||
Player.gainMoney(gains);
|
||||
Player.recordMoneySource(gains, "stock");
|
||||
Player.recordMoneySource(netProfit, "stock");
|
||||
stock.playerShares = Math.round(stock.playerShares - shares);
|
||||
if (stock.playerShares == 0) {
|
||||
stock.playerAvgPx = 0;
|
||||
|
@ -1861,17 +1861,20 @@ let Terminal = {
|
||||
visited[ip] = 0;
|
||||
}
|
||||
|
||||
var stack = [];
|
||||
var depthQueue = [0];
|
||||
var currServ = Player.getCurrentServer();
|
||||
const stack = [];
|
||||
const depthQueue = [0];
|
||||
const currServ = Player.getCurrentServer();
|
||||
stack.push(currServ);
|
||||
while(stack.length != 0) {
|
||||
var s = stack.pop();
|
||||
var d = depthQueue.pop();
|
||||
const s = stack.pop();
|
||||
const d = depthQueue.pop();
|
||||
const isHacknet = s instanceof HacknetServer;
|
||||
if (!all && s.purchasedByPlayer && s.hostname != "home") {
|
||||
continue; //Purchased server
|
||||
continue; // Purchased server
|
||||
} else if (visited[s.ip] || d > depth) {
|
||||
continue; //Already visited or out-of-depth
|
||||
continue; // Already visited or out-of-depth
|
||||
} else if (!all && isHacknet) {
|
||||
continue; // Hacknet Server
|
||||
} else {
|
||||
visited[s.ip] = 1;
|
||||
}
|
||||
@ -1891,8 +1894,8 @@ let Terminal = {
|
||||
//var dashes = Array(d * 2 + 1).join("-");
|
||||
var c = "NO";
|
||||
if (s.hasAdminRights) {c = "YES";}
|
||||
post(dashes + "Root Access: " + c + ", Required hacking skill: " + s.requiredHackingSkill);
|
||||
post(dashes + "Number of open ports required to NUKE: " + s.numOpenPortsRequired);
|
||||
post(`${dashes}Root Access: ${c} ${!isHacknet ? ", Required hacking skill: " + s.requiredHackingSkill : ""}`);
|
||||
if (!isHacknet) { post(dashes + "Number of open ports required to NUKE: " + s.numOpenPortsRequired); }
|
||||
post(dashes + "RAM: " + s.maxRam);
|
||||
post(" ");
|
||||
}
|
||||
@ -2242,9 +2245,12 @@ let Terminal = {
|
||||
post("May take a few seconds to start up the process...");
|
||||
var runningScriptObj = new RunningScript(script, args);
|
||||
runningScriptObj.threads = numThreads;
|
||||
server.runScript(runningScriptObj, Player);
|
||||
|
||||
addWorkerScript(runningScriptObj, server);
|
||||
|
||||
// This has to come after addWorkerScript() because that fn
|
||||
// updates the RAM usage. This kinda sucks, address if possible
|
||||
server.runScript(runningScriptObj, Player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
24
src/ui/React/Popup.tsx
Normal file
24
src/ui/React/Popup.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* React component for a popup content container
|
||||
*
|
||||
* Takes in a prop for rendering the content inside the popup
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
type ReactComponent = new(...args: any[]) => React.Component<any, any>
|
||||
|
||||
interface IProps {
|
||||
content: ReactComponent;
|
||||
id: string;
|
||||
props: object;
|
||||
}
|
||||
|
||||
export class Popup extends React.Component<IProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<div className={"popup-box-content"} id={`${this.props.id}-content`}>
|
||||
{React.createElement(this.props.content, this.props.props)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||
import { removeElement } from "../../../utils/uiHelpers/removeElement";
|
||||
|
||||
export interface IPopupCloseButtonProps {
|
||||
class?: string;
|
||||
@ -43,7 +44,8 @@ export class PopupCloseButton extends React.Component<IPopupCloseButtonProps, an
|
||||
|
||||
// TODO Check if this is okay? This is essentially calling to unmount a parent component
|
||||
if (popup instanceof HTMLElement) {
|
||||
ReactDOM.unmountComponentAtNode(popup);
|
||||
ReactDOM.unmountComponentAtNode(popup); // Removes everything inside the wrapper container
|
||||
removeElement(popup); // Removes the wrapper container
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +60,7 @@ export class PopupCloseButton extends React.Component<IPopupCloseButtonProps, an
|
||||
|
||||
return (
|
||||
<button className={className} onClick={this.closePopup} style={this.props.style}>
|
||||
{this.props.text};
|
||||
{this.props.text}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export class StdButton extends React.Component<IStdButtonProps, any> {
|
||||
|
||||
return (
|
||||
<button className={className} onClick={this.props.onClick} style={this.props.style}>
|
||||
{this.props.text};
|
||||
{this.props.text}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
@ -4,41 +4,45 @@
|
||||
* Calling this function with the same ID and React Root Component will trigger a re-render
|
||||
*
|
||||
* @param id The (hopefully) unique identifier for the popup container
|
||||
* @param rootComponent Root React Component
|
||||
* @param rootComponent Root React Component for the content (NOT the popup containers themselves)
|
||||
*/
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
import { Popup } from "./Popup";
|
||||
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
|
||||
|
||||
type ReactComponent = new(...args: any[]) => React.Component<any, any>;
|
||||
|
||||
export function createPopup(id: string, rootComponent: ReactComponent, props: object): HTMLElement {
|
||||
let gameContainer: HTMLElement;
|
||||
|
||||
function getGameContainer() {
|
||||
let container = document.getElementById("entire-game-container");
|
||||
if (container == null) {
|
||||
throw new Error(`Failed to find game container DOM element`)
|
||||
}
|
||||
|
||||
gameContainer = container;
|
||||
document.removeEventListener("DOMContentLoaded", getGameContainer);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", getGameContainer);
|
||||
|
||||
export function createPopup(id: string, rootComponent: ReactComponent, props: object): HTMLElement | null {
|
||||
let container = document.getElementById(id);
|
||||
let content = document.getElementById(`${id}-content`);
|
||||
if (container == null || content == null) {
|
||||
if (container == null) {
|
||||
container = createElement("div", {
|
||||
class: "popup-box-container",
|
||||
display: "flex",
|
||||
id: id,
|
||||
});
|
||||
|
||||
content = createElement("div", {
|
||||
class: "popup-box-content",
|
||||
id: `${id}-content`,
|
||||
});
|
||||
|
||||
container.appendChild(content);
|
||||
|
||||
try {
|
||||
document.getElementById("entire-game-container")!.appendChild(container);
|
||||
} catch(e) {
|
||||
console.error(`Exception caught when creating popup: ${e}`);
|
||||
}
|
||||
gameContainer.appendChild(container);
|
||||
}
|
||||
|
||||
ReactDOM.render(React.createElement(rootComponent, props), content);
|
||||
ReactDOM.render(<Popup content={rootComponent} id={id} props={props} />, container);
|
||||
|
||||
return container;
|
||||
}
|
||||
@ -47,7 +51,7 @@ export function createPopup(id: string, rootComponent: ReactComponent, props: ob
|
||||
* Closes a popup created with the createPopup() function above
|
||||
*/
|
||||
export function removePopup(id: string): void {
|
||||
let content = document.getElementById(`${id}-content`);
|
||||
let content = document.getElementById(`${id}`);
|
||||
if (content == null) { return; }
|
||||
|
||||
ReactDOM.unmountComponentAtNode(content);
|
Loading…
Reference in New Issue
Block a user