mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 09:33:49 +01:00
commit
564bc4502b
23
css/staneksgift.scss
Normal file
23
css/staneksgift.scss
Normal file
@ -0,0 +1,23 @@
|
||||
.staneksgift_row {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.staneksgift_cell {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-color: #808080;
|
||||
font-color: white;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
border: 1px solid black;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.staneksgift_cell:first-child {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.staneksgift_container {
|
||||
position: fixed;
|
||||
}
|
36
dist/vendor.bundle.js
vendored
36
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
20
doc/source/netscript/netscriptstanekapi.rst
Normal file
20
doc/source/netscript/netscriptstanekapi.rst
Normal file
@ -0,0 +1,20 @@
|
||||
.. _netscriptstanek:
|
||||
|
||||
Netscript Stanek Functions
|
||||
============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game.
|
||||
|
||||
The Stanek API allow you to control Stanek's Gift.
|
||||
|
||||
All these function require Source-File 13-1 or to be in BitNode 13.
|
||||
|
||||
.. toctree::
|
||||
charge() <stanekapi/charge>
|
||||
fragmentDefinitions() <stanekapi/fragmentDefinitions>
|
||||
placedFragments() <stanekapi/placedFragments>
|
||||
clear() <stanekapi/clear>
|
||||
canPlace() <stanekapi/canPlace>
|
||||
place() <stanekapi/place>
|
||||
fragmentAt() <stanekapi/fragmentAt>
|
||||
deleteAt() <stanekapi/deleteAt>
|
16
doc/source/netscript/stanekapi/canPlace.rst
Normal file
16
doc/source/netscript/stanekapi/canPlace.rst
Normal file
@ -0,0 +1,16 @@
|
||||
canPlace() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: canPlace(worldX, worldY, rotation, fragmentId)
|
||||
|
||||
:RAM cost: 0.5 GB
|
||||
:param int worldX: World X against which to align the top left of the fragment.
|
||||
:param int worldY: World Y against which to align the top left of the fragment.
|
||||
:param int rotation: A number from 0 to 3, the mount of 90 degree turn to take.
|
||||
:param int fragmentId: ID of the fragment to place.
|
||||
:returns: `true` if the fragment can be placed at that position. `false` otherwise.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
canPlace(0, 4, 17); // returns true
|
21
doc/source/netscript/stanekapi/charge.rst
Normal file
21
doc/source/netscript/stanekapi/charge.rst
Normal file
@ -0,0 +1,21 @@
|
||||
charge() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: charge(worldX, worldY)
|
||||
|
||||
:RAM cost: 0.4 GB
|
||||
:param int worldX: World X of the fragment to charge.
|
||||
:param int worldY: World Y of the fragment to charge.
|
||||
|
||||
Charge a fragment, increasing it's power but also it's heat. The
|
||||
effectiveness of the charge depends on the amount of ram the running script
|
||||
consumes as well as the fragments current heat. This operation takes time to
|
||||
complete.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
charge(0, 4); // Finishes 5 seconds later.
|
||||
.. warning::
|
||||
|
||||
Netscript JS users: This function is `async`
|
13
doc/source/netscript/stanekapi/clear.rst
Normal file
13
doc/source/netscript/stanekapi/clear.rst
Normal file
@ -0,0 +1,13 @@
|
||||
clear() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: clear()
|
||||
|
||||
:RAM cost: 0 GB
|
||||
|
||||
Completely clear Stanek's Gift.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
clear(); // No more fragments.
|
16
doc/source/netscript/stanekapi/deleteAt.rst
Normal file
16
doc/source/netscript/stanekapi/deleteAt.rst
Normal file
@ -0,0 +1,16 @@
|
||||
deleteAt() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: deleteAt(worldX, worldY)
|
||||
|
||||
:RAM cost: 0.15 GB
|
||||
:param int worldX: World X coordinate of the fragment to delete.
|
||||
:param int worldY: World Y coordinate of the fragment to delete.
|
||||
:returns: `true` if the fragment was deleted. `false` otherwise.
|
||||
|
||||
Delete the fragment located at `[worldX, worldY]`.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
deleteAt(0, 4); // returns true
|
28
doc/source/netscript/stanekapi/fragmentAt.rst
Normal file
28
doc/source/netscript/stanekapi/fragmentAt.rst
Normal file
@ -0,0 +1,28 @@
|
||||
fragmentAt() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: fragmentAt(worldX, worldY)
|
||||
|
||||
:RAM cost: 2 GB
|
||||
:param int worldX: World X coordinate of the fragment.
|
||||
:param int worldY: World Y coordinate of the fragment.
|
||||
:returns: The fragment located at `[worldX, worldY]` in Stanek's Gift, or null.
|
||||
|
||||
.. code-block:: typescript
|
||||
{
|
||||
// In world coordinates
|
||||
x: number;
|
||||
y: number;
|
||||
heat: number;
|
||||
charge: number;
|
||||
id: number;
|
||||
shape: boolean[][];
|
||||
type: string;
|
||||
magnitude: number;
|
||||
limit: number;
|
||||
}
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
var fragment = fragmentAt(0, 4);
|
||||
print(fragment); // {'heat': 50, 'charge': 98}
|
23
doc/source/netscript/stanekapi/fragmentDefinitions.rst
Normal file
23
doc/source/netscript/stanekapi/fragmentDefinitions.rst
Normal file
@ -0,0 +1,23 @@
|
||||
fragmentDefinitions() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: fragmentDefinitions()
|
||||
|
||||
:RAM cost: 0 GB
|
||||
:returns: The list of all fragment that can be embedded in Stanek's Gift.
|
||||
|
||||
.. code-block:: typescript
|
||||
[
|
||||
{
|
||||
id: number;
|
||||
shape: boolean[][];
|
||||
type: string;
|
||||
magnitude: number;
|
||||
limit: number;
|
||||
}
|
||||
]
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
var fragments = fragmentDefinitions();
|
||||
print(fragment); // prints all possible fragments
|
16
doc/source/netscript/stanekapi/place.rst
Normal file
16
doc/source/netscript/stanekapi/place.rst
Normal file
@ -0,0 +1,16 @@
|
||||
place() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: place(worldX, worldY, fragmentId)
|
||||
|
||||
:RAM cost: 5 GB
|
||||
:param int worldX: World X against which to align the top left of the fragment.
|
||||
:param int worldY: World Y against which to align the top left of the fragment.
|
||||
:param int rotation: A number from 0 to 3, the mount of 90 degree turn to take.
|
||||
:param int fragmentId: ID of the fragment to place.
|
||||
:returns: `true` if the fragment has been placed at that position. `false` otherwise.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
place(0, 4, 17); // returns true
|
26
doc/source/netscript/stanekapi/placedFragments.rst
Normal file
26
doc/source/netscript/stanekapi/placedFragments.rst
Normal file
@ -0,0 +1,26 @@
|
||||
placedFragments() Netscript Function
|
||||
=======================================
|
||||
|
||||
.. js:function:: placedFragments()
|
||||
|
||||
:RAM cost: 5 GB
|
||||
:returns: The list of all fragment that are embedded in Stanek's Gift.
|
||||
|
||||
.. code-block:: typescript
|
||||
[
|
||||
{
|
||||
// In world coordinates
|
||||
x: number;
|
||||
y: number;
|
||||
charge: number;
|
||||
id: number;
|
||||
shape: boolean[][];
|
||||
type: string;
|
||||
power: number;
|
||||
limit: number;
|
||||
}
|
||||
]
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
var myFragments = placedFragments();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -2366,6 +2366,134 @@ function initAugmentations(): void {
|
||||
resetAugmentation(BladesSimulacrum);
|
||||
}
|
||||
|
||||
// Special CotMG Augmentations
|
||||
const ChurchOfTheMachineGodFactionName = "Church of the Machine God";
|
||||
if (factionExists(ChurchOfTheMachineGodFactionName)) {
|
||||
const StaneksGift1 = new Augmentation({
|
||||
name: AugmentationNames.StaneksGift1,
|
||||
repCost: 0,
|
||||
moneyCost: 0,
|
||||
info:
|
||||
'Allison "Mother" Stanek imparts you with her gift. An ' +
|
||||
"experimental Augmentation implanted at the base of the neck. " +
|
||||
"It allows you to overclock your entire system by carefully " +
|
||||
"changing the configuration.",
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 0.9,
|
||||
hacking_speed_mult: 0.9,
|
||||
hacking_money_mult: 0.9,
|
||||
hacking_grow_mult: 0.9,
|
||||
hacking_mult: 0.9,
|
||||
strength_mult: 0.9,
|
||||
defense_mult: 0.9,
|
||||
dexterity_mult: 0.9,
|
||||
agility_mult: 0.9,
|
||||
charisma_mult: 0.9,
|
||||
hacking_exp_mult: 0.9,
|
||||
strength_exp_mult: 0.9,
|
||||
defense_exp_mult: 0.9,
|
||||
dexterity_exp_mult: 0.9,
|
||||
agility_exp_mult: 0.9,
|
||||
charisma_exp_mult: 0.9,
|
||||
company_rep_mult: 0.9,
|
||||
faction_rep_mult: 0.9,
|
||||
crime_money_mult: 0.9,
|
||||
crime_success_mult: 0.9,
|
||||
hacknet_node_money_mult: 0.9,
|
||||
hacknet_node_purchase_cost_mult: 1.1,
|
||||
hacknet_node_ram_cost_mult: 1.1,
|
||||
hacknet_node_core_cost_mult: 1.1,
|
||||
hacknet_node_level_cost_mult: 1.1,
|
||||
work_money_mult: 0.9,
|
||||
stats: <>Its unstable nature decreases all your stats by 10%</>,
|
||||
});
|
||||
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift1);
|
||||
|
||||
const StaneksGift2 = new Augmentation({
|
||||
name: AugmentationNames.StaneksGift2,
|
||||
repCost: 1e6,
|
||||
moneyCost: 0,
|
||||
info:
|
||||
"The next evolution is near, A coming together of man and machine. A synthesis greater than the birth of the human " +
|
||||
"organism. Time spent with the gift has allowed for acclimitaztion of the invavise augment and the toll it takes upon " +
|
||||
"your frame granting lesser penalty of 5% to all stats.",
|
||||
prereqs: [AugmentationNames.StaneksGift1],
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 0.95 / 0.9,
|
||||
hacking_speed_mult: 0.95 / 0.9,
|
||||
hacking_money_mult: 0.95 / 0.9,
|
||||
hacking_grow_mult: 0.95 / 0.9,
|
||||
hacking_mult: 0.95 / 0.9,
|
||||
strength_mult: 0.95 / 0.9,
|
||||
defense_mult: 0.95 / 0.9,
|
||||
dexterity_mult: 0.95 / 0.9,
|
||||
agility_mult: 0.95 / 0.9,
|
||||
charisma_mult: 0.95 / 0.9,
|
||||
hacking_exp_mult: 0.95 / 0.9,
|
||||
strength_exp_mult: 0.95 / 0.9,
|
||||
defense_exp_mult: 0.95 / 0.9,
|
||||
dexterity_exp_mult: 0.95 / 0.9,
|
||||
agility_exp_mult: 0.95 / 0.9,
|
||||
charisma_exp_mult: 0.95 / 0.9,
|
||||
company_rep_mult: 0.95 / 0.9,
|
||||
faction_rep_mult: 0.95 / 0.9,
|
||||
crime_money_mult: 0.95 / 0.9,
|
||||
crime_success_mult: 0.95 / 0.9,
|
||||
hacknet_node_money_mult: 0.95 / 0.9,
|
||||
hacknet_node_purchase_cost_mult: 1.05 / 1.1,
|
||||
hacknet_node_ram_cost_mult: 1.05 / 1.1,
|
||||
hacknet_node_core_cost_mult: 1.05 / 1.1,
|
||||
hacknet_node_level_cost_mult: 1.05 / 1.1,
|
||||
work_money_mult: 0.95 / 0.9,
|
||||
stats: <>The penalty for the gift is reduced to 5%</>,
|
||||
});
|
||||
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift2);
|
||||
|
||||
const StaneksGift3 = new Augmentation({
|
||||
name: AugmentationNames.StaneksGift3,
|
||||
repCost: 1e8,
|
||||
moneyCost: 0,
|
||||
info:
|
||||
"The synthesis of human and machine is nothing to fear. It is our destiny. " +
|
||||
"You will become greater than the sum of our parts. As One. Enbrace your gift " +
|
||||
"fully and wholly free of it's accursed toll. Serenity brings tranquility the form " +
|
||||
"of no longer suffering a stat penalty. ",
|
||||
prereqs: [AugmentationNames.StaneksGift2],
|
||||
isSpecial: true,
|
||||
hacking_chance_mult: 1 / 0.95,
|
||||
hacking_speed_mult: 1 / 0.95,
|
||||
hacking_money_mult: 1 / 0.95,
|
||||
hacking_grow_mult: 1 / 0.95,
|
||||
hacking_mult: 1 / 0.95,
|
||||
strength_mult: 1 / 0.95,
|
||||
defense_mult: 1 / 0.95,
|
||||
dexterity_mult: 1 / 0.95,
|
||||
agility_mult: 1 / 0.95,
|
||||
charisma_mult: 1 / 0.95,
|
||||
hacking_exp_mult: 1 / 0.95,
|
||||
strength_exp_mult: 1 / 0.95,
|
||||
defense_exp_mult: 1 / 0.95,
|
||||
dexterity_exp_mult: 1 / 0.95,
|
||||
agility_exp_mult: 1 / 0.95,
|
||||
charisma_exp_mult: 1 / 0.95,
|
||||
company_rep_mult: 1 / 0.95,
|
||||
faction_rep_mult: 1 / 0.95,
|
||||
crime_money_mult: 1 / 0.95,
|
||||
crime_success_mult: 1 / 0.95,
|
||||
hacknet_node_money_mult: 1 / 0.95,
|
||||
hacknet_node_purchase_cost_mult: 1 / 1.05,
|
||||
hacknet_node_ram_cost_mult: 1 / 1.05,
|
||||
hacknet_node_core_cost_mult: 1 / 1.05,
|
||||
hacknet_node_level_cost_mult: 1 / 1.05,
|
||||
work_money_mult: 1 / 0.95,
|
||||
stats: <>Staneks Gift has no penalty.</>,
|
||||
});
|
||||
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift3);
|
||||
}
|
||||
|
||||
// Update costs based on how many have been purchased
|
||||
mult = Math.pow(
|
||||
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
|
||||
|
@ -108,6 +108,9 @@ export const AugmentationNames: {
|
||||
BladeArmorOmnibeam: string;
|
||||
BladeArmorIPU: string;
|
||||
BladesSimulacrum: string;
|
||||
StaneksGift1: string;
|
||||
StaneksGift2: string;
|
||||
StaneksGift3: string;
|
||||
} = {
|
||||
Targeting1: "Augmented Targeting I",
|
||||
Targeting2: "Augmented Targeting II",
|
||||
@ -219,6 +222,10 @@ export const AugmentationNames: {
|
||||
BladeArmorIPU: "BLADE-51b Tesla Armor: IPU Upgrade",
|
||||
BladesSimulacrum: "The Blade's Simulacrum",
|
||||
|
||||
StaneksGift1: "Stanek's Gift - Genesis",
|
||||
StaneksGift2: "Stanek's Gift - Awakening",
|
||||
StaneksGift3: "Stanek's Gift - Serenity",
|
||||
|
||||
//Wasteland Augs
|
||||
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
||||
//PepBoyForceField Generates plasma force fields
|
||||
|
@ -530,8 +530,38 @@ BitNodes["BitNode12"] = new BitNode(
|
||||
</>
|
||||
),
|
||||
);
|
||||
BitNodes["BitNode13"] = new BitNode(
|
||||
13,
|
||||
2,
|
||||
"They're lunatics",
|
||||
"1 step back, 2 steps forward",
|
||||
(
|
||||
<>
|
||||
With the invention of Augmentations in the 2040s a religious group known as the Church of the Machine God has
|
||||
rallied far more support than anyone would have hoped.
|
||||
<br />
|
||||
<br />
|
||||
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
|
||||
other. Find her in Chongquing and gain her trust.
|
||||
<br />
|
||||
<br />
|
||||
In this BitNode:
|
||||
<br />
|
||||
<br />
|
||||
Every stat is significantly reduced
|
||||
<br />
|
||||
Stanek's Gift power is significantly increased.
|
||||
<br />
|
||||
<br />
|
||||
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade its
|
||||
level up to a maximum of 3. This Source-File lets the Church of the Machine God appear in other BitNodes.
|
||||
<br />
|
||||
<br />
|
||||
Each level of this Source-File increases the size of Stanek's Gift.
|
||||
</>
|
||||
),
|
||||
);
|
||||
// Books: Frontera, Shiner
|
||||
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
|
||||
@ -553,6 +583,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers[mult] = 1;
|
||||
}
|
||||
}
|
||||
// Special case.
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||
|
||||
switch (p.bitNodeN) {
|
||||
case 1: // Source Genesis (every multiplier is 1)
|
||||
@ -566,6 +598,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.InfiltrationMoney = 3;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -6;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||
break;
|
||||
@ -583,6 +617,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.HacknetNodeMoney = 0.25;
|
||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -2;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||
break;
|
||||
@ -599,6 +635,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.HackExpGain = 0.4;
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||
break;
|
||||
case 5: // Artificial intelligence
|
||||
@ -613,6 +651,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.3;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||
break;
|
||||
case 6: // Bladeburner
|
||||
@ -630,6 +670,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.HackExpGain = 0.25;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||
break;
|
||||
@ -653,6 +695,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.9;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -1;
|
||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||
break;
|
||||
@ -667,6 +711,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.RepToDonateToFaction = 0;
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -7;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 4;
|
||||
BitNodeMultipliers.GangSoftcap = 0;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0;
|
||||
@ -691,6 +736,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||
BitNodeMultipliers.GangSoftcap = 0.8;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||
break;
|
||||
@ -716,6 +763,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0.6;
|
||||
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
|
||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = -3;
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.1;
|
||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||
@ -807,10 +856,56 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
|
||||
BitNodeMultipliers.BladeburnerRank = dec;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
||||
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = inc;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = inc;
|
||||
BitNodeMultipliers.GangSoftcap = 0.8;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.8;
|
||||
break;
|
||||
}
|
||||
case 13: {
|
||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.5;
|
||||
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.3;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.3;
|
||||
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.45;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||
|
||||
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.4;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0.4;
|
||||
BitNodeMultipliers.CrimeMoney = 0.4;
|
||||
BitNodeMultipliers.HacknetNodeMoney = 0.4;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.4;
|
||||
|
||||
BitNodeMultipliers.CompanyWorkExpGain = 0.5;
|
||||
BitNodeMultipliers.ClassGymExpGain = 0.5;
|
||||
BitNodeMultipliers.FactionWorkExpGain = 0.5;
|
||||
BitNodeMultipliers.HackExpGain = 0.5;
|
||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||
|
||||
BitNodeMultipliers.FactionWorkRepGain = 0.6;
|
||||
|
||||
BitNodeMultipliers.FourSigmaMarketDataCost = 10;
|
||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 10;
|
||||
|
||||
BitNodeMultipliers.CorporationValuation = 0.001;
|
||||
|
||||
BitNodeMultipliers.BladeburnerRank = 0.45;
|
||||
BitNodeMultipliers.BladeburnerSkillCost = 2;
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
|
||||
BitNodeMultipliers.StaneksGiftExtraSize = 1;
|
||||
BitNodeMultipliers.GangSoftcap = 0.3;
|
||||
BitNodeMultipliers.CorporationSoftCap = 0.3;
|
||||
BitNodeMultipliers.WorldDaemonDifficulty = 2.5;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.warn("Player.bitNodeN invalid");
|
||||
break;
|
||||
|
@ -217,6 +217,21 @@ interface IBitNodeMultipliers {
|
||||
*/
|
||||
StrengthLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences the power of the gift.
|
||||
*/
|
||||
StaneksGiftPowerMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences the size of the gift.
|
||||
*/
|
||||
StaneksGiftExtraSize: number;
|
||||
|
||||
/**
|
||||
* Influences the hacking skill required to backdoor the world daemon.
|
||||
*/
|
||||
WorldDaemonDifficulty: number;
|
||||
|
||||
// Index signature
|
||||
[key: string]: number;
|
||||
}
|
||||
@ -282,4 +297,9 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
|
||||
GangSoftcap: 1,
|
||||
|
||||
DaedalusAugsRequirement: 1,
|
||||
|
||||
StaneksGiftPowerMultiplier: 1,
|
||||
StaneksGiftExtraSize: 0,
|
||||
|
||||
WorldDaemonDifficulty: 1,
|
||||
};
|
||||
|
@ -160,7 +160,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ O \__| \_| | O |/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
|
||||
|
95
src/CotMG/ActiveFragment.ts
Normal file
95
src/CotMG/ActiveFragment.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { Fragment, FragmentById } from "./Fragment";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
|
||||
export interface IActiveFragmentParams {
|
||||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
fragment: Fragment;
|
||||
}
|
||||
|
||||
export class ActiveFragment {
|
||||
id: number;
|
||||
avgCharge: number;
|
||||
numCharge: number;
|
||||
rotation: number;
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
constructor(params?: IActiveFragmentParams) {
|
||||
if (params) {
|
||||
this.id = params.fragment.id;
|
||||
this.x = params.x;
|
||||
this.y = params.y;
|
||||
this.avgCharge = 0;
|
||||
this.numCharge = 0;
|
||||
this.rotation = params.rotation;
|
||||
} else {
|
||||
this.id = -1;
|
||||
this.x = -1;
|
||||
this.y = -1;
|
||||
this.avgCharge = -1;
|
||||
this.numCharge = -1;
|
||||
this.rotation = -1;
|
||||
}
|
||||
}
|
||||
|
||||
collide(other: ActiveFragment): boolean {
|
||||
const thisFragment = this.fragment();
|
||||
const otherFragment = other.fragment();
|
||||
// These 2 variables converts 'this' local coordinates to world to other local.
|
||||
const dx: number = other.x - this.x;
|
||||
const dy: number = other.y - this.y;
|
||||
for (let j = 0; j < thisFragment.shape.length; j++) {
|
||||
for (let i = 0; i < thisFragment.shape[j].length; i++) {
|
||||
if (thisFragment.fullAt(i, j, this.rotation) && otherFragment.fullAt(i - dx, j - dy, other.rotation))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fragment(): Fragment {
|
||||
const fragment = FragmentById(this.id);
|
||||
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
|
||||
return fragment;
|
||||
}
|
||||
|
||||
fullAt(worldX: number, worldY: number): boolean {
|
||||
return this.fragment().fullAt(worldX - this.x, worldY - this.y, this.rotation);
|
||||
}
|
||||
|
||||
neighboors(): number[][] {
|
||||
return this.fragment()
|
||||
.neighboors(this.rotation)
|
||||
.map((cell) => [this.x + cell[0], this.y + cell[1]]);
|
||||
}
|
||||
|
||||
copy(): ActiveFragment {
|
||||
// We have to do a round trip because the constructor.
|
||||
const fragment = FragmentById(this.id);
|
||||
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
|
||||
const c = new ActiveFragment({ x: this.x, y: this.y, rotation: this.rotation, fragment: fragment });
|
||||
c.avgCharge = this.avgCharge;
|
||||
c.numCharge = this.numCharge;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an active fragment to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("ActiveFragment", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an acive fragment from a JSON save state
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
static fromJSON(value: any): ActiveFragment {
|
||||
return Generic_fromJSON(ActiveFragment, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.ActiveFragment = ActiveFragment;
|
356
src/CotMG/Fragment.ts
Normal file
356
src/CotMG/Fragment.ts
Normal file
@ -0,0 +1,356 @@
|
||||
import { FragmentType } from "./FragmentType";
|
||||
import { Shapes } from "./data/Shapes";
|
||||
|
||||
export const Fragments: Fragment[] = [];
|
||||
|
||||
export class Fragment {
|
||||
id: number;
|
||||
shape: boolean[][];
|
||||
type: FragmentType;
|
||||
power: number;
|
||||
limit: number;
|
||||
|
||||
constructor(id: number, shape: boolean[][], type: FragmentType, power: number, limit: number) {
|
||||
this.id = id;
|
||||
this.shape = shape;
|
||||
this.type = type;
|
||||
this.power = power;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
fullAt(x: number, y: number, rotation: number): boolean {
|
||||
if (y < 0) return false;
|
||||
if (y >= this.height(rotation)) return false;
|
||||
if (x < 0) return false;
|
||||
if (x >= this.width(rotation)) return false;
|
||||
// start xy, modifier xy
|
||||
let [sx, sy, mx, my] = [0, 0, 1, 1];
|
||||
if (rotation === 1) {
|
||||
[sx, sy, mx, my] = [this.width(rotation) - 1, 0, -1, 1];
|
||||
} else if (rotation === 2) {
|
||||
[sx, sy, mx, my] = [this.width(rotation) - 1, this.height(rotation) - 1, -1, -1];
|
||||
} else if (rotation === 3) {
|
||||
[sx, sy, mx, my] = [0, this.height(rotation) - 1, 1, -1];
|
||||
}
|
||||
let [qx, qy] = [sx + mx * x, sy + my * y];
|
||||
if (rotation % 2 === 1) [qx, qy] = [qy, qx];
|
||||
return this.shape[qy][qx];
|
||||
}
|
||||
|
||||
width(rotation: number): number {
|
||||
if (rotation % 2 === 0) return this.shape[0].length;
|
||||
return this.shape.length;
|
||||
}
|
||||
|
||||
height(rotation: number): number {
|
||||
if (rotation % 2 === 0) return this.shape.length;
|
||||
return this.shape[0].length;
|
||||
}
|
||||
|
||||
// List of direct neighboors of this fragment.
|
||||
neighboors(rotation: number): number[][] {
|
||||
const candidates: number[][] = [];
|
||||
|
||||
const add = (x: number, y: number): void => {
|
||||
if (this.fullAt(x, y, rotation)) return;
|
||||
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
|
||||
candidates.push([x, y]);
|
||||
};
|
||||
for (let y = 0; y < this.height(rotation); y++) {
|
||||
for (let x = 0; x < this.width(rotation); x++) {
|
||||
// This cell is full, add all it's neighboors.
|
||||
if (!this.fullAt(x, y, rotation)) continue;
|
||||
add(x - 1, y);
|
||||
add(x + 1, y);
|
||||
add(x, y - 1);
|
||||
add(x, y + 1);
|
||||
}
|
||||
}
|
||||
const cells: number[][] = [];
|
||||
for (const candidate of candidates) {
|
||||
if (cells.some((cell) => cell[0] === candidate[0] && cell[1] === candidate[1])) continue;
|
||||
cells.push(candidate);
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
copy(): Fragment {
|
||||
return new Fragment(
|
||||
this.id,
|
||||
this.shape.map((a) => a.slice()),
|
||||
this.type,
|
||||
this.power,
|
||||
this.limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function FragmentById(id: number): Fragment | null {
|
||||
for (const fragment of Fragments) {
|
||||
if (fragment.id === id) return fragment;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
(function () {
|
||||
const _ = false;
|
||||
const X = true;
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
0, // id
|
||||
Shapes.S,
|
||||
FragmentType.Hacking, // type
|
||||
1,
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
1, // id
|
||||
Shapes.Z,
|
||||
FragmentType.Hacking, // type
|
||||
1,
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
5, // id
|
||||
Shapes.T,
|
||||
FragmentType.HackingSpeed, // type
|
||||
1.3,
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
6, // id
|
||||
Shapes.I,
|
||||
FragmentType.HackingMoney, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
7, // id
|
||||
Shapes.J,
|
||||
FragmentType.HackingGrow, // type
|
||||
0.5, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
10, // id
|
||||
Shapes.T,
|
||||
FragmentType.Strength, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
12, // id
|
||||
Shapes.L,
|
||||
FragmentType.Defense, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
14, // id
|
||||
Shapes.L,
|
||||
FragmentType.Dexterity, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
16, // id
|
||||
Shapes.S,
|
||||
FragmentType.Agility, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
18, // id
|
||||
Shapes.S,
|
||||
FragmentType.Charisma, // type
|
||||
3, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
20, // id
|
||||
Shapes.I,
|
||||
FragmentType.HacknetMoney, // type
|
||||
1, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
21, // id
|
||||
Shapes.O,
|
||||
FragmentType.HacknetCost, // type
|
||||
-1, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
25, // id
|
||||
Shapes.J,
|
||||
FragmentType.Rep, // type
|
||||
0.5, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
27, // id
|
||||
Shapes.J,
|
||||
FragmentType.WorkMoney, // type
|
||||
10, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
28, // id
|
||||
Shapes.L,
|
||||
FragmentType.Crime, // type
|
||||
2, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
30, // id
|
||||
Shapes.S,
|
||||
FragmentType.Bladeburner, // type
|
||||
0.4, // power
|
||||
1, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
100, // id
|
||||
[
|
||||
// shape
|
||||
[_, X, X],
|
||||
[X, X, _],
|
||||
[_, X, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
101, // id
|
||||
[
|
||||
// shape
|
||||
[X, X, X, X],
|
||||
[X, _, _, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
102, // id
|
||||
[
|
||||
// shape
|
||||
[_, X, X, X],
|
||||
[X, X, _, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
103, // id
|
||||
[
|
||||
// shape
|
||||
[X, X, X, _],
|
||||
[_, _, X, X],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
104, // id
|
||||
[
|
||||
// shape
|
||||
[_, X, X],
|
||||
[_, X, _],
|
||||
[X, X, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
105, // id
|
||||
[
|
||||
// shape
|
||||
[_, _, X],
|
||||
[_, X, X],
|
||||
[X, X, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
106, // id
|
||||
[
|
||||
// shape
|
||||
[X, _, _],
|
||||
[X, X, X],
|
||||
[X, _, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
Fragments.push(
|
||||
new Fragment(
|
||||
107, // id
|
||||
[
|
||||
// shape
|
||||
[_, X, _],
|
||||
[X, X, X],
|
||||
[_, X, _],
|
||||
],
|
||||
FragmentType.Booster, // type
|
||||
1.1, // power
|
||||
99, // limit
|
||||
),
|
||||
);
|
||||
})();
|
||||
|
||||
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
|
||||
export const DeleteFragment = new Fragment(-2, [], FragmentType.Delete, 0, Infinity);
|
96
src/CotMG/FragmentType.ts
Normal file
96
src/CotMG/FragmentType.ts
Normal file
@ -0,0 +1,96 @@
|
||||
export enum FragmentType {
|
||||
// Special fragments for the UI
|
||||
None,
|
||||
Delete,
|
||||
|
||||
// Stats boosting fragments
|
||||
HackingChance,
|
||||
HackingSpeed,
|
||||
HackingMoney,
|
||||
HackingGrow,
|
||||
Hacking,
|
||||
Strength,
|
||||
Defense,
|
||||
Dexterity,
|
||||
Agility,
|
||||
Charisma,
|
||||
HacknetMoney,
|
||||
HacknetCost,
|
||||
Rep,
|
||||
WorkMoney,
|
||||
Crime,
|
||||
Bladeburner,
|
||||
|
||||
// utility fragments.
|
||||
Booster,
|
||||
}
|
||||
|
||||
export function Effect(tpe: FragmentType): string {
|
||||
switch (tpe) {
|
||||
case FragmentType.HackingChance: {
|
||||
return "+x% hack() success chance";
|
||||
break;
|
||||
}
|
||||
case FragmentType.HackingSpeed: {
|
||||
return "+x% faster hack(), grow(), and weaken()";
|
||||
break;
|
||||
}
|
||||
case FragmentType.HackingMoney: {
|
||||
return "+x% hack() power";
|
||||
break;
|
||||
}
|
||||
case FragmentType.HackingGrow: {
|
||||
return "+x% grow() power";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Hacking: {
|
||||
return "+x% hacking skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Strength: {
|
||||
return "+x% strength skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Defense: {
|
||||
return "+x% defense skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Dexterity: {
|
||||
return "+x% dexterity skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Agility: {
|
||||
return "+x% agility skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Charisma: {
|
||||
return "+x% charisma skill";
|
||||
break;
|
||||
}
|
||||
case FragmentType.HacknetMoney: {
|
||||
return "+x% hacknet production";
|
||||
break;
|
||||
}
|
||||
case FragmentType.HacknetCost: {
|
||||
return "-x% all hacknet cost";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Rep: {
|
||||
return "+x% reputation from factions and companies";
|
||||
break;
|
||||
}
|
||||
case FragmentType.WorkMoney: {
|
||||
return "+x% work money";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Crime: {
|
||||
return "+x% crime money";
|
||||
break;
|
||||
}
|
||||
case FragmentType.Bladeburner: {
|
||||
return "+x% all bladeburner stats";
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new Error("Calling effect for fragment type that doesn't have an effect " + tpe);
|
||||
}
|
14
src/CotMG/Helper.tsx
Normal file
14
src/CotMG/Helper.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
|
||||
import { IStaneksGift } from "./IStaneksGift";
|
||||
import { StaneksGift } from "./StaneksGift";
|
||||
|
||||
export let staneksGift: IStaneksGift = new StaneksGift();
|
||||
|
||||
export function loadStaneksGift(saveString: string): void {
|
||||
if (saveString) {
|
||||
staneksGift = JSON.parse(saveString, Reviver);
|
||||
} else {
|
||||
staneksGift = new StaneksGift();
|
||||
}
|
||||
}
|
23
src/CotMG/IStaneksGift.ts
Normal file
23
src/CotMG/IStaneksGift.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ActiveFragment } from "./ActiveFragment";
|
||||
import { Fragment } from "./Fragment";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export interface IStaneksGift {
|
||||
storedCycles: number;
|
||||
fragments: ActiveFragment[];
|
||||
width(): number;
|
||||
height(): number;
|
||||
charge(player: IPlayer, fragment: ActiveFragment, threads: number): void;
|
||||
process(p: IPlayer, n: number): void;
|
||||
effect(fragment: ActiveFragment): number;
|
||||
canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean;
|
||||
place(x: number, y: number, rotation: number, fragment: Fragment): boolean;
|
||||
findFragment(rootX: number, rootY: number): ActiveFragment | undefined;
|
||||
fragmentAt(rootX: number, rootY: number): ActiveFragment | undefined;
|
||||
delete(rootX: number, rootY: number): boolean;
|
||||
clear(): void;
|
||||
count(fragment: Fragment): number;
|
||||
inBonus(): boolean;
|
||||
prestigeAugmentation(): void;
|
||||
prestigeSourceFile(): void;
|
||||
}
|
234
src/CotMG/StaneksGift.ts
Normal file
234
src/CotMG/StaneksGift.ts
Normal file
@ -0,0 +1,234 @@
|
||||
import { Fragment } from "./Fragment";
|
||||
import { ActiveFragment } from "./ActiveFragment";
|
||||
import { FragmentType } from "./FragmentType";
|
||||
import { IStaneksGift } from "./IStaneksGift";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { CalculateEffect } from "./formulas/effect";
|
||||
import { StaneksGiftEvents } from "./StaneksGiftEvents";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { StanekConstants } from "./data/Constants";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Player } from "../Player";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
|
||||
export class StaneksGift implements IStaneksGift {
|
||||
storedCycles = 0;
|
||||
fragments: ActiveFragment[] = [];
|
||||
|
||||
baseSize(): number {
|
||||
return StanekConstants.BaseSize + BitNodeMultipliers.StaneksGiftExtraSize + Player.sourceFileLvl(13);
|
||||
}
|
||||
|
||||
width(): number {
|
||||
return Math.floor(this.baseSize() / 2 + 1);
|
||||
}
|
||||
height(): number {
|
||||
return Math.floor(this.baseSize() / 2 + 0.6);
|
||||
}
|
||||
|
||||
charge(player: IPlayer, af: ActiveFragment, threads: number): void {
|
||||
af.avgCharge = (af.numCharge * af.avgCharge + threads) / (af.numCharge + 1);
|
||||
af.numCharge++;
|
||||
|
||||
const cotmg = Factions["Church of the Machine God"];
|
||||
cotmg.playerReputation += (player.faction_rep_mult * (Math.pow(threads, 0.95) * (cotmg.favor + 100))) / 1000;
|
||||
}
|
||||
|
||||
inBonus(): boolean {
|
||||
return (this.storedCycles * CONSTANTS._idleSpeed) / 1000 > 1;
|
||||
}
|
||||
|
||||
process(p: IPlayer, numCycles = 1): void {
|
||||
if (!p.hasAugmentation(AugmentationNames.StaneksGift1)) return;
|
||||
this.storedCycles += numCycles;
|
||||
this.storedCycles -= 10;
|
||||
this.storedCycles = Math.max(0, this.storedCycles);
|
||||
this.updateMults(p);
|
||||
StaneksGiftEvents.emit();
|
||||
}
|
||||
|
||||
effect(fragment: ActiveFragment): number {
|
||||
// Find all the neighbooring cells
|
||||
const cells = fragment.neighboors();
|
||||
// find the neighbooring active fragments.
|
||||
const maybeFragments = cells.map((n) => this.fragmentAt(n[0], n[1]));
|
||||
|
||||
// Filter out undefined with typescript "Type guard". Whatever
|
||||
let neighboors = maybeFragments.filter((v: ActiveFragment | undefined): v is ActiveFragment => !!v);
|
||||
|
||||
neighboors = neighboors.filter((fragment) => fragment.fragment().type === FragmentType.Booster);
|
||||
let boost = 1;
|
||||
|
||||
neighboors = neighboors.filter((v, i, s) => s.indexOf(v) === i);
|
||||
for (const neighboor of neighboors) {
|
||||
boost *= neighboor.fragment().power;
|
||||
}
|
||||
return CalculateEffect(fragment.avgCharge, fragment.numCharge, fragment.fragment().power, boost);
|
||||
}
|
||||
|
||||
canPlace(rootX: number, rootY: number, rotation: number, fragment: Fragment): boolean {
|
||||
if (rootX < 0 || rootY < 0) return false;
|
||||
if (rootX + fragment.width(rotation) > this.width()) return false;
|
||||
if (rootY + fragment.height(rotation) > this.height()) return false;
|
||||
if (this.count(fragment) >= fragment.limit) return false;
|
||||
const newFrag = new ActiveFragment({ x: rootX, y: rootY, rotation: rotation, fragment: fragment });
|
||||
for (const aFrag of this.fragments) {
|
||||
if (aFrag.collide(newFrag)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
place(rootX: number, rootY: number, rotation: number, fragment: Fragment): boolean {
|
||||
if (!this.canPlace(rootX, rootY, rotation, fragment)) return false;
|
||||
this.fragments.push(new ActiveFragment({ x: rootX, y: rootY, rotation: rotation, fragment: fragment }));
|
||||
return true;
|
||||
}
|
||||
|
||||
findFragment(rootX: number, rootY: number): ActiveFragment | undefined {
|
||||
return this.fragments.find((f) => f.x === rootX && f.y === rootY);
|
||||
}
|
||||
|
||||
fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined {
|
||||
for (const aFrag of this.fragments) {
|
||||
if (aFrag.fullAt(worldX, worldY)) {
|
||||
return aFrag;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
count(fragment: Fragment): number {
|
||||
let amt = 0;
|
||||
for (const aFrag of this.fragments) {
|
||||
if (aFrag.fragment().id === fragment.id) amt++;
|
||||
}
|
||||
return amt;
|
||||
}
|
||||
|
||||
delete(rootX: number, rootY: number): boolean {
|
||||
for (let i = 0; i < this.fragments.length; i++) {
|
||||
if (this.fragments[i].x === rootX && this.fragments[i].y === rootY) {
|
||||
this.fragments.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.fragments = [];
|
||||
}
|
||||
|
||||
clearCharge(): void {
|
||||
this.fragments.forEach((f) => {
|
||||
f.avgCharge = 0;
|
||||
f.numCharge = 0;
|
||||
});
|
||||
}
|
||||
|
||||
updateMults(p: IPlayer): void {
|
||||
p.reapplyAllAugmentations(true);
|
||||
p.reapplyAllSourceFiles();
|
||||
|
||||
for (const aFrag of this.fragments) {
|
||||
const fragment = aFrag.fragment();
|
||||
|
||||
const power = this.effect(aFrag);
|
||||
switch (fragment.type) {
|
||||
case FragmentType.HackingChance:
|
||||
p.hacking_chance_mult *= power;
|
||||
break;
|
||||
case FragmentType.HackingSpeed:
|
||||
p.hacking_speed_mult *= power;
|
||||
break;
|
||||
case FragmentType.HackingMoney:
|
||||
p.hacking_money_mult *= power;
|
||||
break;
|
||||
case FragmentType.HackingGrow:
|
||||
p.hacking_grow_mult *= power;
|
||||
break;
|
||||
case FragmentType.Hacking:
|
||||
p.hacking_mult *= power;
|
||||
p.hacking_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.Strength:
|
||||
p.strength_mult *= power;
|
||||
p.strength_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.Defense:
|
||||
p.defense_mult *= power;
|
||||
p.defense_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.Dexterity:
|
||||
p.dexterity_mult *= power;
|
||||
p.dexterity_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.Agility:
|
||||
p.agility_mult *= power;
|
||||
p.agility_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.Charisma:
|
||||
p.charisma_mult *= power;
|
||||
p.charisma_exp_mult *= power;
|
||||
break;
|
||||
case FragmentType.HacknetMoney:
|
||||
p.hacknet_node_money_mult *= power;
|
||||
break;
|
||||
case FragmentType.HacknetCost:
|
||||
p.hacknet_node_purchase_cost_mult *= power;
|
||||
p.hacknet_node_ram_cost_mult *= power;
|
||||
p.hacknet_node_core_cost_mult *= power;
|
||||
p.hacknet_node_level_cost_mult *= power;
|
||||
break;
|
||||
case FragmentType.Rep:
|
||||
p.company_rep_mult *= power;
|
||||
p.faction_rep_mult *= power;
|
||||
break;
|
||||
case FragmentType.WorkMoney:
|
||||
p.work_money_mult *= power;
|
||||
break;
|
||||
case FragmentType.Crime:
|
||||
p.crime_success_mult *= power;
|
||||
p.crime_money_mult *= power;
|
||||
break;
|
||||
case FragmentType.Bladeburner:
|
||||
p.bladeburner_max_stamina_mult *= power;
|
||||
p.bladeburner_stamina_gain_mult *= power;
|
||||
p.bladeburner_analysis_mult *= power;
|
||||
p.bladeburner_success_chance_mult *= power;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p.updateSkillLevels();
|
||||
}
|
||||
|
||||
prestigeAugmentation(): void {
|
||||
this.clearCharge();
|
||||
}
|
||||
|
||||
prestigeSourceFile(): void {
|
||||
this.clear();
|
||||
this.storedCycles = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize Staneks Gift to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("StaneksGift", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes Staneks Gift from a JSON save state
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
static fromJSON(value: any): StaneksGift {
|
||||
return Generic_fromJSON(StaneksGift, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.StaneksGift = StaneksGift;
|
2
src/CotMG/StaneksGiftEvents.ts
Normal file
2
src/CotMG/StaneksGiftEvents.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
export const StaneksGiftEvents = new EventEmitter<[]>();
|
7
src/CotMG/data/Constants.ts
Normal file
7
src/CotMG/data/Constants.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const StanekConstants: {
|
||||
RAMBonus: number;
|
||||
BaseSize: number;
|
||||
} = {
|
||||
RAMBonus: 0.1,
|
||||
BaseSize: 9,
|
||||
};
|
37
src/CotMG/data/Shapes.ts
Normal file
37
src/CotMG/data/Shapes.ts
Normal file
@ -0,0 +1,37 @@
|
||||
const _ = false;
|
||||
const X = true;
|
||||
export const Shapes: {
|
||||
O: boolean[][];
|
||||
I: boolean[][];
|
||||
L: boolean[][];
|
||||
J: boolean[][];
|
||||
S: boolean[][];
|
||||
Z: boolean[][];
|
||||
T: boolean[][];
|
||||
} = {
|
||||
O: [
|
||||
[X, X],
|
||||
[X, X],
|
||||
],
|
||||
I: [[X, X, X, X]],
|
||||
L: [
|
||||
[_, _, X],
|
||||
[X, X, X],
|
||||
],
|
||||
J: [
|
||||
[X, _, _],
|
||||
[X, X, X],
|
||||
],
|
||||
S: [
|
||||
[_, X, X],
|
||||
[X, X, _],
|
||||
],
|
||||
Z: [
|
||||
[X, X, _],
|
||||
[_, X, X],
|
||||
],
|
||||
T: [
|
||||
[X, X, X],
|
||||
[_, X, _],
|
||||
],
|
||||
};
|
5
src/CotMG/formulas/charge.ts
Normal file
5
src/CotMG/formulas/charge.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { StanekConstants } from "../data/Constants";
|
||||
|
||||
export function CalculateCharge(ram: number): number {
|
||||
return ram * Math.pow(1 + Math.log2(ram) * StanekConstants.RAMBonus, 0.7);
|
||||
}
|
12
src/CotMG/formulas/effect.ts
Normal file
12
src/CotMG/formulas/effect.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
export function CalculateEffect(avgCharge: number, numCharge: number, power: number, boost: number): number {
|
||||
return (
|
||||
1 +
|
||||
(Math.log(avgCharge + 1) / (Math.log(1.8) * 100)) *
|
||||
Math.pow((numCharge + 1) / 5, 0.07) *
|
||||
power *
|
||||
boost *
|
||||
BitNodeMultipliers.StaneksGiftPowerMultiplier
|
||||
);
|
||||
}
|
4
src/CotMG/notes
Normal file
4
src/CotMG/notes
Normal file
@ -0,0 +1,4 @@
|
||||
incentive for more threads
|
||||
boosters just multiply output, eg 20% * 1.5 = 30%
|
||||
|
||||
git remote add danielyxie git@github.com:danielyxie/bitburner.git
|
40
src/CotMG/ui/Cell.tsx
Normal file
40
src/CotMG/ui/Cell.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import * as React from "react";
|
||||
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import { TableCell as MuiTableCell, TableCellProps } from "@mui/material";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
border: "1px solid white",
|
||||
width: "5px",
|
||||
height: "5px",
|
||||
},
|
||||
});
|
||||
|
||||
export const TableCell: React.FC<TableCellProps> = (props: TableCellProps) => {
|
||||
return (
|
||||
<MuiTableCell
|
||||
{...props}
|
||||
classes={{
|
||||
root: useStyles().root,
|
||||
...props.classes,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type IProps = {
|
||||
onMouseEnter?: () => void;
|
||||
onClick?: () => void;
|
||||
color: string;
|
||||
};
|
||||
|
||||
export function Cell(cellProps: IProps): React.ReactElement {
|
||||
return (
|
||||
<TableCell
|
||||
style={{ backgroundColor: cellProps.color }}
|
||||
onMouseEnter={cellProps.onMouseEnter}
|
||||
onClick={cellProps.onClick}
|
||||
></TableCell>
|
||||
);
|
||||
}
|
81
src/CotMG/ui/FragmentInspector.tsx
Normal file
81
src/CotMG/ui/FragmentInspector.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { FragmentType, Effect } from "../FragmentType";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
type IProps = {
|
||||
gift: IStaneksGift;
|
||||
fragment: ActiveFragment | undefined;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export function FragmentInspector(props: IProps): React.ReactElement {
|
||||
const [, setC] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setC(new Date()), 250);
|
||||
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
if (props.fragment === undefined) {
|
||||
return (
|
||||
<Paper>
|
||||
<Typography>
|
||||
ID: N/A
|
||||
<br />
|
||||
Effect: N/A
|
||||
<br />
|
||||
Magnitude: N/A
|
||||
<br />
|
||||
Charge: N/A
|
||||
<br />
|
||||
Heat: N/A
|
||||
<br />
|
||||
Effect: N/A
|
||||
<br />
|
||||
[X, Y] N/A
|
||||
<br />
|
||||
[X, Y] {props.x}, {props.y}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
const f = props.fragment.fragment();
|
||||
|
||||
let charge = `${numeralWrapper.formatStaneksGiftCharge(props.fragment.avgCharge)} avg. * ${
|
||||
props.fragment.numCharge
|
||||
} times`;
|
||||
let effect = "N/A";
|
||||
// Boosters and cooling don't deal with heat.
|
||||
if ([FragmentType.Booster, FragmentType.None, FragmentType.Delete].includes(f.type)) {
|
||||
charge = "N/A";
|
||||
effect = `${f.power}x adjacent fragment power`;
|
||||
} else {
|
||||
effect = Effect(f.type).replace(/-*x%/, numeralWrapper.formatPercentage(props.gift.effect(props.fragment) - 1));
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<Typography>
|
||||
ID: {props.fragment.id}
|
||||
<br />
|
||||
Effect: {effect}
|
||||
<br />
|
||||
Base Power: {numeralWrapper.formatStaneksGiftPower(f.power)}
|
||||
<br />
|
||||
Charge: {charge}
|
||||
<br />
|
||||
<br />
|
||||
root [X, Y] {props.fragment.x}, {props.fragment.y}
|
||||
<br />
|
||||
[X, Y] {props.x}, {props.y}
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
31
src/CotMG/ui/FragmentPreview.tsx
Normal file
31
src/CotMG/ui/FragmentPreview.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import * as React from "react";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { Table } from "../../ui/React/Table";
|
||||
|
||||
type IProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
colorAt: (x: number, y: number) => string;
|
||||
};
|
||||
|
||||
export function FragmentPreview(props: IProps): React.ReactElement {
|
||||
// switch the width/length to make axis consistent.
|
||||
const elems = [];
|
||||
for (let j = 0; j < props.height; j++) {
|
||||
const cells = [];
|
||||
for (let i = 0; i < props.width; i++) {
|
||||
cells.push(<Cell key={i} color={props.colorAt(i, j)} />);
|
||||
}
|
||||
elems.push(<TableRow key={j}>{cells}</TableRow>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableBody>{elems}</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
89
src/CotMG/ui/FragmentSelector.tsx
Normal file
89
src/CotMG/ui/FragmentSelector.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import React, { useState } from "react";
|
||||
import { Fragments, Fragment, NoneFragment, DeleteFragment } from "../Fragment";
|
||||
import { FragmentType, Effect } from "../FragmentType";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { FragmentPreview } from "./FragmentPreview";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
type IOptionProps = {
|
||||
gift: IStaneksGift;
|
||||
fragment: Fragment;
|
||||
selectFragment: (fragment: Fragment) => void;
|
||||
};
|
||||
|
||||
function FragmentOption(props: IOptionProps): React.ReactElement {
|
||||
const left = props.fragment.limit - props.gift.count(props.fragment);
|
||||
const remaining = props.fragment.limit !== Infinity ? <>{left} remaining</> : <></>;
|
||||
return (
|
||||
<Box display="flex">
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<FragmentPreview
|
||||
width={props.fragment.width(0)}
|
||||
height={props.fragment.height(0)}
|
||||
colorAt={(x, y) => {
|
||||
if (!props.fragment.fullAt(x, y, 0)) return "";
|
||||
if (left === 0) return "grey";
|
||||
return props.fragment.type === FragmentType.Booster ? "blue" : "green";
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography>
|
||||
{props.fragment.type === FragmentType.Booster
|
||||
? `${props.fragment.power}x adjacent fragment power`
|
||||
: Effect(props.fragment.type)}
|
||||
<br />
|
||||
power: {numeralWrapper.formatStaneksGiftPower(props.fragment.power)}
|
||||
<br />
|
||||
{remaining}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
gift: IStaneksGift;
|
||||
selectFragment: (fragment: Fragment) => void;
|
||||
};
|
||||
|
||||
export function FragmentSelector(props: IProps): React.ReactElement {
|
||||
const [value, setValue] = useState<string | number>("None");
|
||||
function onChange(event: SelectChangeEvent<string | number>): void {
|
||||
const v = event.target.value;
|
||||
setValue(v);
|
||||
if (v === "None") {
|
||||
props.selectFragment(NoneFragment);
|
||||
return;
|
||||
} else if (v === "Delete") {
|
||||
props.selectFragment(DeleteFragment);
|
||||
return;
|
||||
}
|
||||
const fragment = Fragments.find((f) => f.id === v);
|
||||
if (fragment === undefined) throw new Error("Fragment selector selected an undefined fragment with id " + v);
|
||||
if (typeof v === "number") props.selectFragment(fragment);
|
||||
}
|
||||
return (
|
||||
<Select sx={{ width: "100%" }} onChange={onChange} value={value}>
|
||||
<MenuItem value="None">
|
||||
<Typography>None</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Delete">
|
||||
<Typography>Delete</Typography>
|
||||
</MenuItem>
|
||||
{Fragments.map((fragment) => (
|
||||
<MenuItem key={fragment.id} value={fragment.id}>
|
||||
<FragmentOption
|
||||
key={fragment.id}
|
||||
gift={props.gift}
|
||||
selectFragment={props.selectFragment}
|
||||
fragment={fragment}
|
||||
/>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
172
src/CotMG/ui/MainBoard.tsx
Normal file
172
src/CotMG/ui/MainBoard.tsx
Normal file
@ -0,0 +1,172 @@
|
||||
import * as React from "react";
|
||||
import { Fragment, NoneFragment } from "../Fragment";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { FragmentType } from "../FragmentType";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { Cell } from "./Cell";
|
||||
import { FragmentInspector } from "./FragmentInspector";
|
||||
import { FragmentSelector } from "./FragmentSelector";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { Table } from "../../ui/React/Table";
|
||||
|
||||
function zeros(dimensions: number[]): any {
|
||||
const array = [];
|
||||
|
||||
for (let i = 0; i < dimensions[0]; ++i) {
|
||||
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
function randomColor(fragment: ActiveFragment): string {
|
||||
// Can't set Math.random seed so copy casino. TODO refactor both RNG later.
|
||||
let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10);
|
||||
let s2 = s1;
|
||||
let s3 = s1;
|
||||
|
||||
const colors = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
s1 = (171 * s1) % 30269;
|
||||
s2 = (172 * s2) % 30307;
|
||||
s3 = (170 * s3) % 30323;
|
||||
colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0);
|
||||
}
|
||||
|
||||
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
gift: IStaneksGift;
|
||||
}
|
||||
|
||||
export function MainBoard(props: IProps): React.ReactElement {
|
||||
function calculateGrid(gift: IStaneksGift): any {
|
||||
const newgrid = zeros([gift.width(), gift.height()]);
|
||||
for (let i = 0; i < gift.width(); i++) {
|
||||
for (let j = 0; j < gift.height(); j++) {
|
||||
const fragment = gift.fragmentAt(i, j);
|
||||
if (!fragment) continue;
|
||||
newgrid[i][j] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return newgrid;
|
||||
}
|
||||
|
||||
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
|
||||
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
|
||||
const [pos, setPos] = React.useState([0, 0]);
|
||||
const [rotation, setRotation] = React.useState(0);
|
||||
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
|
||||
|
||||
function moveGhost(worldX: number, worldY: number, rotation: number): void {
|
||||
setPos([worldX, worldY]);
|
||||
if (selectedFragment.type === FragmentType.None || selectedFragment.type === FragmentType.Delete) return;
|
||||
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||
for (let y = 0; y < selectedFragment.height(rotation); y++) {
|
||||
for (let x = 0; x < selectedFragment.width(rotation); x++) {
|
||||
if (!selectedFragment.fullAt(x, y, rotation)) continue;
|
||||
if (worldX + x > newgrid.length - 1) continue;
|
||||
if (worldY + y > newgrid[worldX + x].length - 1) continue;
|
||||
newgrid[worldX + x][worldY + y] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
setGhostGrid(newgrid);
|
||||
}
|
||||
|
||||
function deleteAt(worldX: number, worldY: number): boolean {
|
||||
const f = props.gift.fragmentAt(worldX, worldY);
|
||||
if (f === undefined) return false;
|
||||
return props.gift.delete(f.x, f.y);
|
||||
}
|
||||
|
||||
function clickAt(worldX: number, worldY: number): void {
|
||||
if (selectedFragment.type == FragmentType.None) return;
|
||||
if (selectedFragment.type == FragmentType.Delete) {
|
||||
deleteAt(worldX, worldY);
|
||||
} else {
|
||||
if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return;
|
||||
props.gift.place(worldX, worldY, rotation, selectedFragment);
|
||||
}
|
||||
setGrid(calculateGrid(props.gift));
|
||||
}
|
||||
|
||||
function color(worldX: number, worldY: number): string {
|
||||
if (ghostGrid[worldX][worldY] && grid[worldX][worldY]) return "red";
|
||||
if (ghostGrid[worldX][worldY]) return "white";
|
||||
|
||||
if (grid[worldX][worldY]) {
|
||||
const fragment = props.gift.fragmentAt(worldX, worldY);
|
||||
if (!fragment) throw new Error("ActiveFragment should not be null");
|
||||
return randomColor(fragment);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function clear(): void {
|
||||
props.gift.clear();
|
||||
setGrid(zeros([props.gift.width(), props.gift.height()]));
|
||||
}
|
||||
|
||||
// switch the width/length to make axis consistent.
|
||||
const elems = [];
|
||||
for (let j = 0; j < props.gift.height(); j++) {
|
||||
const cells = [];
|
||||
for (let i = 0; i < props.gift.width(); i++) {
|
||||
cells.push(
|
||||
<Cell
|
||||
key={i}
|
||||
onMouseEnter={() => moveGhost(i, j, rotation)}
|
||||
onClick={() => clickAt(i, j)}
|
||||
color={color(i, j)}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
elems.push(
|
||||
<TableRow key={j} className="staneksgift_row">
|
||||
{cells}
|
||||
</TableRow>,
|
||||
);
|
||||
}
|
||||
|
||||
function updateSelectedFragment(fragment: Fragment): void {
|
||||
setSelectedFragment(fragment);
|
||||
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||
setGhostGrid(newgrid);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
function doRotate(this: Document, event: KeyboardEvent): void {
|
||||
if (event.key === "q") {
|
||||
const r = (rotation - 1 + 4) % 4;
|
||||
setRotation(r);
|
||||
moveGhost(pos[0], pos[1], r);
|
||||
}
|
||||
if (event.key === "e") {
|
||||
const r = (rotation + 1) % 4;
|
||||
setRotation(r);
|
||||
moveGhost(pos[0], pos[1], r);
|
||||
}
|
||||
}
|
||||
document.addEventListener("keydown", doRotate);
|
||||
return () => document.removeEventListener("keydown", doRotate);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={clear}>Clear</Button>
|
||||
<Box display="flex">
|
||||
<Table>
|
||||
<TableBody>{elems}</TableBody>
|
||||
</Table>
|
||||
<FragmentInspector gift={props.gift} x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
||||
</Box>
|
||||
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
||||
</>
|
||||
);
|
||||
}
|
36
src/CotMG/ui/StaneksGiftRoot.tsx
Normal file
36
src/CotMG/ui/StaneksGiftRoot.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { StaneksGiftEvents } from "../StaneksGiftEvents";
|
||||
import { MainBoard } from "./MainBoard";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
type IProps = {
|
||||
staneksGift: IStaneksGift;
|
||||
};
|
||||
|
||||
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||
const setRerender = useState(true)[1];
|
||||
function rerender(): void {
|
||||
setRerender((o) => !o);
|
||||
}
|
||||
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">Stanek's Gift</Typography>
|
||||
<Typography>
|
||||
The gift is a grid on which you can place upgrades called fragments. The main type of fragment increases a stat,
|
||||
like your hacking skill or agility exp. Once a stat fragment is placed it then needs to be charged via scripts
|
||||
in order to become useful. The other kind of fragment is called booster fragments. They increase the efficiency
|
||||
of the charged happening on fragments neighboring them (no diagonal). Q/E to rotate fragments.
|
||||
</Typography>
|
||||
{staneksGift.storedCycles > 5 && (
|
||||
<Typography>
|
||||
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
|
||||
</Typography>
|
||||
)}
|
||||
<MainBoard gift={staneksGift} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,6 +2,7 @@ import { IPlayer } from "./PersonObjects/IPlayer";
|
||||
import { Bladeburner } from "./Bladeburner/Bladeburner";
|
||||
import { IEngine } from "./IEngine";
|
||||
import { IRouter } from "./ui/Router";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
|
||||
import React from "react";
|
||||
|
||||
@ -19,6 +20,7 @@ import { Corporation } from "./DevMenu/ui/Corporation";
|
||||
import { CodingContracts } from "./DevMenu/ui/CodingContracts";
|
||||
import { StockMarket } from "./DevMenu/ui/StockMarket";
|
||||
import { Sleeves } from "./DevMenu/ui/Sleeves";
|
||||
import { Stanek } from "./DevMenu/ui/Stanek";
|
||||
import { TimeSkip } from "./DevMenu/ui/TimeSkip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
@ -52,6 +54,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
|
||||
{props.player.hasWseAccount && <StockMarket />}
|
||||
|
||||
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
|
||||
{props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1) && <Stanek />}
|
||||
|
||||
<TimeSkip player={props.player} engine={props.engine} />
|
||||
</>
|
||||
|
85
src/DevMenu/ui/Stanek.tsx
Normal file
85
src/DevMenu/ui/Stanek.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
|
||||
import { staneksGift } from "../../CotMG/Helper";
|
||||
|
||||
import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Adjuster } from "./Adjuster";
|
||||
|
||||
export function Stanek(): React.ReactElement {
|
||||
function addCycles(): void {
|
||||
staneksGift.storedCycles = 1e6;
|
||||
}
|
||||
|
||||
function modCycles(modify: number): (x: number) => void {
|
||||
return function (cycles: number): void {
|
||||
staneksGift.storedCycles += cycles * modify;
|
||||
};
|
||||
}
|
||||
|
||||
function resetCycles(): void {
|
||||
staneksGift.storedCycles = 0;
|
||||
}
|
||||
|
||||
function addCharge(): void {
|
||||
staneksGift.fragments.forEach((f) => {
|
||||
f.avgCharge = 1e21;
|
||||
f.numCharge = 1e21;
|
||||
});
|
||||
}
|
||||
|
||||
function modCharge(modify: number): (x: number) => void {
|
||||
return function (cycles: number): void {
|
||||
staneksGift.fragments.forEach((f) => (f.avgCharge += cycles * modify));
|
||||
};
|
||||
}
|
||||
|
||||
function resetCharge(): void {
|
||||
staneksGift.fragments.forEach((f) => {
|
||||
f.avgCharge = 0;
|
||||
f.numCharge = 0;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>Stanek's Gift</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Adjuster
|
||||
label="cycles"
|
||||
placeholder="amt"
|
||||
tons={addCycles}
|
||||
add={modCycles(1)}
|
||||
subtract={modCycles(-1)}
|
||||
reset={resetCycles}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Adjuster
|
||||
label="all charge"
|
||||
placeholder="amt"
|
||||
tons={addCharge}
|
||||
add={modCharge(1)}
|
||||
subtract={modCharge(-1)}
|
||||
reset={resetCharge}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
@ -526,4 +526,47 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
// prettier-ignore
|
||||
"Church of the Machine God": new FactionInfo(<>
|
||||
{" `` "}<br />
|
||||
{" -odmmNmds: "}<br />
|
||||
{" `hNmo:..-omNh. "}<br />
|
||||
{" yMd` `hNh "}<br />
|
||||
{" mMd oNm "}<br />
|
||||
{" oMNo .mM/ "}<br />
|
||||
{" `dMN+ -mM+ "}<br />
|
||||
{" -mMNo -mN+ "}<br />
|
||||
{" .+- :mMNo/mN/ "}<br />
|
||||
{":yNMd. :NMNNN/ "}<br />
|
||||
{"-mMMMh. /NMMh` "}<br />
|
||||
{" .dMMMd. /NMMMy` "}<br />
|
||||
{" `yMMMd. /NNyNMMh` "}<br />
|
||||
{" `sMMMd. +Nm: +NMMh. "}<br />
|
||||
{" oMMMm- oNm: /NMMd. "}<br />
|
||||
{" +NMMmsMm- :mMMd. "}<br />
|
||||
{" /NMMMm- -mMMd. "}<br />
|
||||
{" /MMMm- -mMMd. "}<br />
|
||||
{" `sMNMMm- .mMmo "}<br />
|
||||
{" `sMd:hMMm. ./. "}<br />
|
||||
{" `yMy` `yNMd` "}<br />
|
||||
{" `hMs` oMMy "}<br />
|
||||
{" `hMh sMN- "}<br />
|
||||
{" /MM- .NMo "}<br />
|
||||
{" +MM: :MM+ "}<br />
|
||||
{" sNNo-.`.-omNy` "}<br />
|
||||
{" -smNNNNmdo- "}<br />
|
||||
{" `..` "}<br /><br />
|
||||
Many cultures predict an end to humanity in the near future, a final
|
||||
Armageddon that will end the world; but we disagree.
|
||||
<br /><br />Note that for this faction, reputation can
|
||||
only be gained by charging Stanek's gift.</>,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
};
|
||||
|
@ -17,6 +17,9 @@ import Button from "@mui/material/Button";
|
||||
import { Location } from "../Location";
|
||||
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { joinFaction } from "../../Faction/FactionHelpers";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
@ -24,6 +27,7 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { N00dles } from "../../utils/helpers/N00dles";
|
||||
import { Exploit } from "../../Exploits/Exploit";
|
||||
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -119,6 +123,21 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
return <Button onClick={handleResleeving}>Re-Sleeve</Button>;
|
||||
}
|
||||
|
||||
function handleCotMG(): void {
|
||||
const faction = Factions["Church of the Machine God"];
|
||||
if (!player.factions.includes("Church of the Machine God")) {
|
||||
joinFaction(faction);
|
||||
}
|
||||
if (
|
||||
!player.augmentations.some((a) => a.name === AugmentationNames.StaneksGift1) &&
|
||||
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
|
||||
) {
|
||||
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
|
||||
}
|
||||
|
||||
router.toFaction(faction);
|
||||
}
|
||||
|
||||
function renderCotMG(): React.ReactElement {
|
||||
// prettier-ignore
|
||||
const symbol = <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>
|
||||
@ -150,7 +169,46 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
{" sNNo-.`.-omNy` "}<br />
|
||||
{" -smNNNNmdo- "}<br />
|
||||
{" `..` "}</Typography>
|
||||
if (player.hasAugmentation(AugmentationNames.StaneksGift3, true)) {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<i>
|
||||
Allison "Mother" Stanek: ..can ...you hear them too ...? Come now, don't be shy and let me get a closer
|
||||
look at you. Yes wonderful, I see my creation has taken root without consquence or much ill effect it
|
||||
seems. Curious, Just how much of a machine's soul do you house in that body?
|
||||
</i>
|
||||
</Typography>
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (player.hasAugmentation(AugmentationNames.StaneksGift2, true)) {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<i>
|
||||
Allison "Mother" Stanek: I see you've taken to my creation. So much so it could hardly be recoginized as
|
||||
one of my own after your tinkering with it. I see you follow the ways of the MachineGod as I do, and your
|
||||
mastery of the gift thus for clearly demonstrates that. My hopes are climbing by the day for you.
|
||||
</i>
|
||||
</Typography>
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (player.factions.includes("Church of the Machine God")) {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<i>Allison "Mother" Stanek: Welcome back my child!</i>
|
||||
</Typography>
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!player.canAccessCotMG()) {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
@ -164,6 +222,36 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0 ||
|
||||
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<i>
|
||||
Allison "Mother" Stanek: Begone you filth! My gift must be the first modification that your body should
|
||||
have!
|
||||
</i>
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<i>
|
||||
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
|
||||
form? If you are, accept my gift.
|
||||
</i>
|
||||
</Typography>
|
||||
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
switch (props.loc.name) {
|
||||
case LocationName.NewTokyoVitaLife: {
|
||||
return renderResleeving();
|
||||
|
@ -55,6 +55,15 @@ export const RamCostConstants: IMap<number> = {
|
||||
ScriptGangApiBaseRamCost: 4,
|
||||
|
||||
ScriptBladeburnerApiBaseRamCost: 4,
|
||||
|
||||
ScriptStanekCharge: 0.4,
|
||||
ScriptStanekFragmentDefinitions: 0,
|
||||
ScriptStanekPlacedFragments: 5,
|
||||
ScriptStanekClear: 0,
|
||||
ScriptStanekCanPlace: 0.5,
|
||||
ScriptStanekPlace: 5,
|
||||
ScriptStanekFragmentAt: 2,
|
||||
ScriptStanekDeleteAt: 0.15,
|
||||
};
|
||||
|
||||
export const RamCosts: IMap<any> = {
|
||||
@ -326,6 +335,17 @@ export const RamCosts: IMap<any> = {
|
||||
purchaseSleeveAug: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
},
|
||||
|
||||
stanek: {
|
||||
charge: RamCostConstants.ScriptStanekCharge,
|
||||
fragmentDefinitions: RamCostConstants.ScriptStanekFragmentDefinitions,
|
||||
activeFragments: RamCostConstants.ScriptStanekPlacedFragments,
|
||||
clear: RamCostConstants.ScriptStanekClear,
|
||||
canPlace: RamCostConstants.ScriptStanekCanPlace,
|
||||
place: RamCostConstants.ScriptStanekPlace,
|
||||
get: RamCostConstants.ScriptStanekFragmentAt,
|
||||
remove: RamCostConstants.ScriptStanekDeleteAt,
|
||||
},
|
||||
|
||||
heart: {
|
||||
// Easter egg function
|
||||
break: 0,
|
||||
|
@ -63,14 +63,24 @@ import { NetscriptGang } from "./NetscriptFunctions/Gang";
|
||||
import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
|
||||
import { NetscriptExtra } from "./NetscriptFunctions/Extra";
|
||||
import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
|
||||
import { NS as INS, Player as INetscriptPlayer, SourceFileLvl } from "./ScriptEditor/NetscriptDefinitions";
|
||||
import { NetscriptStanek } from "./NetscriptFunctions/Stanek";
|
||||
|
||||
import { NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner";
|
||||
import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract";
|
||||
import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
|
||||
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
|
||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
||||
import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket";
|
||||
|
||||
import {
|
||||
NS as INS,
|
||||
Player as INetscriptPlayer,
|
||||
Gang as IGang,
|
||||
Bladeburner as IBladeburner,
|
||||
Stanek as IStanek,
|
||||
SourceFileLvl,
|
||||
} from "./ScriptEditor/NetscriptDefinitions";
|
||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
||||
|
||||
import { toNative } from "./NetscriptFunctions/toNative";
|
||||
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
@ -80,6 +90,9 @@ import { Flags } from "./NetscriptFunctions/Flags";
|
||||
|
||||
interface NS extends INS {
|
||||
[key: string]: any;
|
||||
gang: IGang;
|
||||
bladeburner: IBladeburner;
|
||||
stanek: IStanek;
|
||||
}
|
||||
|
||||
export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
@ -431,6 +444,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
||||
const extra = NetscriptExtra(Player, workerScript);
|
||||
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
||||
const stanek = NetscriptStanek(Player, workerScript, helper);
|
||||
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
||||
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
||||
const corporation = NetscriptCorporation(Player);
|
||||
@ -445,6 +459,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
bladeburner: bladeburner,
|
||||
codingcontract: codingcontract,
|
||||
sleeve: sleeve,
|
||||
stanek: stanek,
|
||||
|
||||
formulas: formulas,
|
||||
stock: stockmarket,
|
||||
@ -2255,7 +2270,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
Object.assign(data.jobs, Player.jobs);
|
||||
return data;
|
||||
},
|
||||
|
||||
atExit: function (f: any): void {
|
||||
if (typeof f !== "function") {
|
||||
throw makeRuntimeErrorMsg("atExit", "argument should be function");
|
||||
|
109
src/NetscriptFunctions/Stanek.ts
Normal file
109
src/NetscriptFunctions/Stanek.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { netscriptDelay } from "../NetscriptEvaluator";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { Fragments, FragmentById } from "../CotMG/Fragment";
|
||||
|
||||
import {
|
||||
Stanek as IStanek,
|
||||
Fragment as IFragment,
|
||||
ActiveFragment as IActiveFragment,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
|
||||
export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IStanek {
|
||||
function checkStanekAPIAccess(func: string): void {
|
||||
if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) {
|
||||
helper.makeRuntimeErrorMsg(func, "Requires Stanek's Gift installed.");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: function (): number {
|
||||
return staneksGift.width();
|
||||
},
|
||||
height: function (): number {
|
||||
return staneksGift.height();
|
||||
},
|
||||
charge: function (arootX: any, arootY: any): Promise<void> {
|
||||
const rootX = helper.number("stanek.charge", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.charge", "rootY", arootY);
|
||||
|
||||
helper.updateDynamicRam("charge", getRamCost("stanek", "charge"));
|
||||
checkStanekAPIAccess("charge");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.charge", `No fragment with root (${rootX}, ${rootY}).`);
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
if (workerScript.env.stopFlag) {
|
||||
return Promise.reject(workerScript);
|
||||
}
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
workerScript.log("stanek.charge", () => `Charged fragment for ${charge} charge.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
fragmentDefinitions: function (): IFragment[] {
|
||||
helper.updateDynamicRam("fragmentDefinitions", getRamCost("stanek", "fragmentDefinitions"));
|
||||
checkStanekAPIAccess("fragmentDefinitions");
|
||||
workerScript.log("stanek.fragmentDefinitions", () => `Returned ${Fragments.length} fragments`);
|
||||
return Fragments.map((f) => f.copy());
|
||||
},
|
||||
activeFragments: function (): IActiveFragment[] {
|
||||
helper.updateDynamicRam("activeFragments", getRamCost("stanek", "activeFragments"));
|
||||
checkStanekAPIAccess("activeFragments");
|
||||
workerScript.log("stanek.activeFragments", () => `Returned ${staneksGift.fragments.length} fragments`);
|
||||
return staneksGift.fragments.map((af) => {
|
||||
return { ...af.copy(), ...af.fragment().copy() };
|
||||
});
|
||||
},
|
||||
clear: function (): void {
|
||||
helper.updateDynamicRam("clear", getRamCost("stanek", "clear"));
|
||||
checkStanekAPIAccess("clear");
|
||||
workerScript.log("stanek.clear", () => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlace: function (arootX: any, arootY: any, arotation: any, afragmentId: any): boolean {
|
||||
const rootX = helper.number("stanek.canPlace", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.canPlace", "rootY", arootY);
|
||||
const rotation = helper.number("stanek.canPlace", "rotation", arotation);
|
||||
const fragmentId = helper.number("stanek.canPlace", "fragmentId", afragmentId);
|
||||
helper.updateDynamicRam("canPlace", getRamCost("stanek", "canPlace"));
|
||||
checkStanekAPIAccess("canPlace");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${fragmentId}`);
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
place: function (arootX: any, arootY: any, arotation: any, afragmentId: any): boolean {
|
||||
const rootX = helper.number("stanek.place", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.place", "rootY", arootY);
|
||||
const rotation = helper.number("stanek.place", "rotation", arotation);
|
||||
const fragmentId = helper.number("stanek.place", "fragmentId", afragmentId);
|
||||
helper.updateDynamicRam("place", getRamCost("stanek", "place"));
|
||||
checkStanekAPIAccess("place");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
get: function (arootX: any, arootY: any): IActiveFragment | undefined {
|
||||
const rootX = helper.number("stanek.get", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.get", "rootY", arootY);
|
||||
helper.updateDynamicRam("get", getRamCost("stanek", "get"));
|
||||
checkStanekAPIAccess("get");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
remove: function (arootX: any, arootY: any): boolean {
|
||||
const rootX = helper.number("stanek.remove", "rootX", arootX);
|
||||
const rootY = helper.number("stanek.remove", "rootY", arootY);
|
||||
helper.updateDynamicRam("remove", getRamCost("stanek", "remove"));
|
||||
checkStanekAPIAccess("remove");
|
||||
return staneksGift.delete(rootX, rootY);
|
||||
},
|
||||
};
|
||||
}
|
@ -115,6 +115,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
if (typeof workerScript.env.vars[prop] !== "function") continue;
|
||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
||||
}
|
||||
workerScript.env.vars.stanek.charge = wrap("stanek.prop", workerScript.env.vars.stanek.charge);
|
||||
|
||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||
// to that script, which env.vars does at this point.
|
||||
|
@ -277,5 +277,6 @@ export interface IPlayer {
|
||||
setBitNodeNumber(n: number): void;
|
||||
getMult(name: string): number;
|
||||
setMult(name: string, mult: number): void;
|
||||
canAccessCotMG(): boolean;
|
||||
sourceFileLvl(n: number): number;
|
||||
}
|
||||
|
@ -283,6 +283,7 @@ export class PlayerObject implements IPlayer {
|
||||
setBitNodeNumber: (n: number) => void;
|
||||
getMult: (name: string) => number;
|
||||
setMult: (name: string, mult: number) => void;
|
||||
canAccessCotMG: () => boolean;
|
||||
sourceFileLvl: (n: number) => number;
|
||||
|
||||
constructor() {
|
||||
@ -579,6 +580,8 @@ export class PlayerObject implements IPlayer {
|
||||
|
||||
this.getMult = generalMethods.getMult;
|
||||
this.setMult = generalMethods.setMult;
|
||||
|
||||
this.canAccessCotMG = generalMethods.canAccessCotMG;
|
||||
this.sourceFileLvl = generalMethods.sourceFileLvl;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,14 @@ export function generateResleeves(): Resleeve[] {
|
||||
const randKey: string = augKeys[randIndex];
|
||||
|
||||
// Forbidden augmentations
|
||||
if (randKey === AugmentationNames.TheRedPill || randKey === AugmentationNames.NeuroFluxGovernor) {
|
||||
const forbidden = [
|
||||
AugmentationNames.TheRedPill,
|
||||
AugmentationNames.NeuroFluxGovernor,
|
||||
AugmentationNames.StaneksGift1,
|
||||
AugmentationNames.StaneksGift2,
|
||||
AugmentationNames.StaneksGift3,
|
||||
];
|
||||
if (forbidden.includes(randKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,10 @@ import { Terminal } from "./Terminal";
|
||||
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
|
||||
import { staneksGift } from "./CotMG/Helper";
|
||||
import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
|
||||
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { LogBoxClearEvents } from "./ui/React/LogBoxManager";
|
||||
|
||||
const BitNode8StartingMoney = 250e6;
|
||||
@ -143,6 +145,14 @@ export function prestigeAugmentation(): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (augmentationExists(AugmentationNames.StaneksGift1) && Augmentations[AugmentationNames.StaneksGift1].owned) {
|
||||
// TODO(hydroflame): refactor faction names so we don't have to hard
|
||||
// code strings.
|
||||
joinFaction(Factions["Church of the Machine God"]);
|
||||
}
|
||||
|
||||
staneksGift.prestigeAugmentation();
|
||||
|
||||
resetPidCounter();
|
||||
ProgramsSeen.splice(0, ProgramsSeen.length);
|
||||
InvitationsSeen.splice(0, InvitationsSeen.length);
|
||||
@ -247,6 +257,10 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
dialogBoxCreate("Visit VitaLife in New Tokyo if you'd like to purchase a new sleeve!");
|
||||
}
|
||||
|
||||
if (Player.bitNodeN === 13) {
|
||||
dialogBoxCreate("Trouble is brewing in Chongqing");
|
||||
}
|
||||
|
||||
// Reset Stock market, gang, and corporation
|
||||
if (Player.hasWseAccount) {
|
||||
initStockMarket();
|
||||
@ -272,6 +286,11 @@ export function prestigeSourceFile(flume: boolean): void {
|
||||
updateHashManagerCapacity(Player);
|
||||
}
|
||||
|
||||
if (Player.bitNodeN === 13) {
|
||||
Player.money = CONSTANTS.TravelCost;
|
||||
}
|
||||
staneksGift.prestigeSourceFile();
|
||||
|
||||
// Gain int exp
|
||||
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
|
||||
|
||||
|
@ -9,6 +9,7 @@ import { saveAllServers, loadAllServers, GetAllServers } from "./Server/AllServe
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
|
||||
import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
|
||||
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
|
||||
@ -38,6 +39,7 @@ class BitburnerSaveObject {
|
||||
VersionSave = "";
|
||||
AllGangsSave = "";
|
||||
LastExportBonus = "";
|
||||
StaneksGiftSave = "";
|
||||
|
||||
getSaveString(): string {
|
||||
this.PlayerSave = JSON.stringify(Player);
|
||||
@ -52,6 +54,7 @@ class BitburnerSaveObject {
|
||||
this.SettingsSave = JSON.stringify(Settings);
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||
if (Player.inGang()) {
|
||||
this.AllGangsSave = JSON.stringify(AllGangs);
|
||||
}
|
||||
@ -240,6 +243,12 @@ function loadGame(saveString: string): boolean {
|
||||
loadCompanies(saveObj.CompaniesSave);
|
||||
loadFactions(saveObj.FactionsSave);
|
||||
|
||||
if (saveObj.hasOwnProperty("StaneksGiftSave")) {
|
||||
loadStaneksGift(saveObj.StaneksGiftSave);
|
||||
} else {
|
||||
console.warn(`Could not load Staneks Gift from save`);
|
||||
loadStaneksGift("");
|
||||
}
|
||||
if (saveObj.hasOwnProperty("AliasesSave")) {
|
||||
try {
|
||||
loadAliases(saveObj.AliasesSave);
|
||||
|
@ -183,6 +183,8 @@ async function parseOnlyRamCalculate(
|
||||
func = workerScript.env.vars.bladeburner[ref];
|
||||
} else if (ref in workerScript.env.vars.codingcontract) {
|
||||
func = workerScript.env.vars.codingcontract[ref];
|
||||
} else if (ref in workerScript.env.vars.stanek) {
|
||||
func = workerScript.env.vars.stanek[ref];
|
||||
} else if (ref in workerScript.env.vars.gang) {
|
||||
func = workerScript.env.vars.gang[ref];
|
||||
} else if (ref in workerScript.env.vars.sleeve) {
|
||||
|
124
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
124
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -3325,6 +3325,125 @@ export interface Formulas {
|
||||
hacknetServers: HacknetServersFormulas;
|
||||
}
|
||||
|
||||
export interface Fragment {
|
||||
id: number;
|
||||
shape: boolean[][];
|
||||
type: number;
|
||||
power: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface ActiveFragment {
|
||||
id: number;
|
||||
avgCharge: number;
|
||||
numCharge: number;
|
||||
rotation: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stanek's Gift API.
|
||||
* @public
|
||||
*/
|
||||
interface Stanek {
|
||||
/**
|
||||
* Stanek's Gift width.
|
||||
* @remarks
|
||||
* RAM cost: 0.4 GB
|
||||
* @returns The width of the gift.
|
||||
*/
|
||||
width(): number;
|
||||
/**
|
||||
* Stanek's Gift height.
|
||||
* @remarks
|
||||
* RAM cost: 0.4 GB
|
||||
* @returns The height of the gift.
|
||||
*/
|
||||
height(): number;
|
||||
|
||||
/**
|
||||
* Charge a fragment, increasing it's power.
|
||||
* @remarks
|
||||
* RAM cost: 0.4 GB
|
||||
* @param rootX - rootX Root X against which to align the top left of the fragment.
|
||||
* @param rootY - rootY Root Y against which to align the top left of the fragment.
|
||||
* @returns Promise that lasts until the charge action is over.
|
||||
*/
|
||||
charge(rootX: number, rootY: number): Promise<void>;
|
||||
|
||||
/**
|
||||
* List possible fragments.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
*
|
||||
* @returns List of possible fragments.
|
||||
*/
|
||||
fragmentDefinitions(): Fragment[];
|
||||
|
||||
/**
|
||||
* List of fragments in Stanek's Gift.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
*
|
||||
* @returns List of active fragments placed on Stanek's Gift.
|
||||
*/
|
||||
activeFragments(): ActiveFragment[];
|
||||
|
||||
/**
|
||||
* Clear the board of all fragments.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0 GB
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
* Check if fragment can be placed at specified location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0.5 GB
|
||||
*
|
||||
* @param rootX - rootX Root X against which to align the top left of the fragment.
|
||||
* @param rootY - rootY Root Y against which to align the top left of the fragment.
|
||||
* @param rotation - rotation A number from 0 to 3, the mount of 90 degree turn to take.
|
||||
* @param fragmentId - fragmentId ID of the fragment to place.
|
||||
* @returns true if the fragment can be placed at that position. false otherwise.
|
||||
*/
|
||||
canPlace(rootX: number, rootY: number, rotation: number, fragmentId: number): boolean;
|
||||
/**
|
||||
* Place fragment on Stanek's Gift.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
* @param rotation - A number from 0 to 3, the mount of 90 degree turn to take.
|
||||
* @param fragmentId - ID of the fragment to place.
|
||||
* @returns true if the fragment can be placed at that position. false otherwise.
|
||||
*/
|
||||
place(rootX: number, rootY: number, rotation: number, fragmentId: number): boolean;
|
||||
/**
|
||||
* Get placed fragment at location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 5 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
* @returns The fragment at [rootX, rootY], if any.
|
||||
*/
|
||||
get(rootX: number, rootY: number): ActiveFragment | undefined;
|
||||
|
||||
/**
|
||||
* Remove fragment at location.
|
||||
* @remarks
|
||||
* RAM cost: cost: 0.15 GB
|
||||
*
|
||||
* @param rootX - X against which to align the top left of the fragment.
|
||||
* @param rootY - Y against which to align the top left of the fragment.
|
||||
* @returns The fragment at [rootX, rootY], if any.
|
||||
*/
|
||||
remove(rootX: number, rootY: number): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection of all functions passed to scripts
|
||||
* @public
|
||||
@ -3374,6 +3493,11 @@ export interface NS extends Singularity {
|
||||
* RAM cost: 0 GB
|
||||
*/
|
||||
readonly formulas: Formulas;
|
||||
/**
|
||||
* Namespace for stanek functions.
|
||||
* @ramCost 0 GB
|
||||
*/
|
||||
readonly stanek: Stanek;
|
||||
|
||||
/**
|
||||
* Arguments passed into the script.
|
||||
|
@ -9,6 +9,8 @@ import { createRandomIp } from "../utils/IPAddress";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
||||
import { SpecialServers } from "./data/SpecialServers";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
/**
|
||||
* Map of all Servers that exist in the game
|
||||
@ -161,6 +163,9 @@ export function initForeignServers(homeComputer: Server): void {
|
||||
server.messages.push(filename);
|
||||
}
|
||||
|
||||
if (server.hostname === SpecialServers.WorldDaemon) {
|
||||
server.requiredHackingSkill *= BitNodeMultipliers.WorldDaemonDifficulty;
|
||||
}
|
||||
AddToAllServers(server);
|
||||
if (metadata.networkLayer !== undefined) {
|
||||
networkLayers[toNumber(metadata.networkLayer) - 1].push(server);
|
||||
|
@ -49,6 +49,7 @@ import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveT
|
||||
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { redPillFlag } from "../../RedPill";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
||||
@ -157,13 +158,12 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
|
||||
const canOpenSleeves = props.player.sleeves.length > 0;
|
||||
|
||||
// TODO(hydroflame): these should not as any but right now the def is that it
|
||||
// can only be defined;
|
||||
const canCorporation = !!(props.player.corporation as any);
|
||||
const canGang = !!(props.player.gang as any);
|
||||
const canJob = props.player.companyName !== "";
|
||||
const canStockMarket = props.player.hasWseAccount;
|
||||
const canBladeburner = !!(props.player.bladeburner as any);
|
||||
const canStaneksGift = props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1);
|
||||
|
||||
function clickTerminal(): void {
|
||||
props.router.toTerminal();
|
||||
@ -188,6 +188,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
props.router.toCreateProgram();
|
||||
}
|
||||
|
||||
function clickStaneksGift(): void {
|
||||
props.router.toStaneksGift();
|
||||
}
|
||||
|
||||
function clickFactions(): void {
|
||||
props.router.toFactions();
|
||||
}
|
||||
@ -427,6 +431,25 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)}
|
||||
{canStaneksGift && (
|
||||
<ListItem
|
||||
button
|
||||
key={"Staneks Gift"}
|
||||
className={clsx({
|
||||
[classes.active]: props.page === Page.StaneksGift,
|
||||
})}
|
||||
onClick={clickStaneksGift}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DeveloperBoardIcon color={props.page !== Page.StaneksGift ? "secondary" : "primary"} />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography color={props.page !== Page.StaneksGift ? "secondary" : "primary"}>
|
||||
Stanek's Gift
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Collapse>
|
||||
|
||||
|
@ -204,3 +204,7 @@ SourceFiles["SourceFile12"] = new SourceFile(
|
||||
12,
|
||||
<>This Source-File lets the player start with Neuroflux Governor equal to the level of this Source-File.</>,
|
||||
);
|
||||
SourceFiles["SourceFile13"] = new SourceFile(
|
||||
13,
|
||||
<>Each level of this Source-File increases the size of Stanek's Gift.</>,
|
||||
);
|
||||
|
@ -163,7 +163,10 @@ export function applySourceFile(srcFile: PlayerOwnedSourceFile): void {
|
||||
break;
|
||||
}
|
||||
case 12: // The Recursion
|
||||
// No effects, grants neuroflux.
|
||||
// Grants neuroflux.
|
||||
break;
|
||||
case 13: // They're Lunatics
|
||||
// Grants more space on Stanek's Gift.
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid source file number: ${srcFile.n}`);
|
||||
|
@ -12,6 +12,7 @@ import { initCompanies } from "./Company/Companies";
|
||||
import { Corporation } from "./Corporation/Corporation";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Factions, initFactions } from "./Faction/Factions";
|
||||
import { staneksGift } from "./CotMG/Helper";
|
||||
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
import { SetupTextEditor } from "./ScriptEditor/ui/ScriptEditorRoot";
|
||||
@ -101,6 +102,9 @@ const Engine: {
|
||||
Player.gang.process(numCycles, Player);
|
||||
}
|
||||
|
||||
// Staneks gift
|
||||
staneksGift.process(Player, numCycles);
|
||||
|
||||
// Corporation
|
||||
if (Player.corporation instanceof Corporation) {
|
||||
// Stores cycles in a "buffer". Processed separately using Engine Counters
|
||||
@ -345,6 +349,8 @@ const Engine: {
|
||||
Player.bladeburner.storeCycles(numCyclesOffline);
|
||||
}
|
||||
|
||||
staneksGift.process(Player, numCyclesOffline);
|
||||
|
||||
// Sleeves offline progress
|
||||
for (let i = 0; i < Player.sleeves.length; ++i) {
|
||||
if (Player.sleeves[i] instanceof Sleeve) {
|
||||
|
33
src/engineStyle.ts
Normal file
33
src/engineStyle.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// These should really be imported with the module that is presenting that UI, but because they very much depend on the
|
||||
// cascade order, we'll pull them all in here.
|
||||
import "normalize.css";
|
||||
import "../css/styles.scss";
|
||||
import "../css/tooltips.scss";
|
||||
import "../css/buttons.scss";
|
||||
import "../css/mainmenu.scss";
|
||||
import "../css/characteroverview.scss";
|
||||
|
||||
import "../css/scripteditor.scss";
|
||||
import "../css/hacknetnodes.scss";
|
||||
import "../css/menupages.scss";
|
||||
import "../css/augmentations.scss";
|
||||
import "../css/redpill.scss";
|
||||
import "../css/stockmarket.scss";
|
||||
import "../css/workinprogress.scss";
|
||||
import "../css/popupboxes.scss";
|
||||
import "../css/gameoptions.scss";
|
||||
import "../css/interactivetutorial.scss";
|
||||
import "../css/loader.scss";
|
||||
import "../css/missions.scss";
|
||||
import "../css/companymanagement.scss";
|
||||
import "../css/bladeburner.scss";
|
||||
import "../css/gang.scss";
|
||||
import "../css/sleeves.scss";
|
||||
import "../css/resleeving.scss";
|
||||
import "../css/treant.css";
|
||||
import "../css/grid.min.css";
|
||||
import "../css/dev-menu.css";
|
||||
import "../css/casino.scss";
|
||||
import "../css/milestones.scss";
|
||||
import "../css/infiltration.scss";
|
||||
import "../css/staneksgift.scss";
|
@ -61,6 +61,8 @@ import { CharacterStats } from "./CharacterStats";
|
||||
import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
|
||||
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
|
||||
import { BitverseRoot } from "../BitNode/ui/BitverseRoot";
|
||||
import { StaneksGiftRoot } from "../CotMG/ui/StaneksGiftRoot";
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { CharacterOverview } from "./React/CharacterOverview";
|
||||
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
|
||||
import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
@ -181,6 +183,9 @@ export let Router: IRouter = {
|
||||
toLocation: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
};
|
||||
|
||||
function determineStartPage(player: IPlayer): Page {
|
||||
@ -279,6 +284,9 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
setLocation(location);
|
||||
setPage(Page.Location);
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
setPage(Page.StaneksGift);
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -316,6 +324,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
<TerminalRoot terminal={terminal} router={Router} player={player} />
|
||||
) : page === Page.Sleeves ? (
|
||||
<SleeveRoot />
|
||||
) : page === Page.StaneksGift ? (
|
||||
<StaneksGiftRoot staneksGift={staneksGift} />
|
||||
) : page === Page.Stats ? (
|
||||
<CharacterStats />
|
||||
) : page === Page.ScriptEditor ? (
|
||||
|
@ -641,6 +641,9 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
<Link href="https://www.reddit.com/r/bitburner" target="_blank">
|
||||
<Typography>Reddit</Typography>
|
||||
</Link>
|
||||
<Link href="https://plaza.dsolver.ca/games/bitburner" target="_blank">
|
||||
<Typography>Incremental game plaza</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -34,6 +34,7 @@ export enum Page {
|
||||
BladeburnerCinematic,
|
||||
Location,
|
||||
Loading,
|
||||
StaneksGift,
|
||||
Recovery,
|
||||
}
|
||||
|
||||
@ -74,4 +75,5 @@ export interface IRouter {
|
||||
toWork(): void;
|
||||
toBladeburnerCinematic(): void;
|
||||
toLocation(location: Location): void;
|
||||
toStaneksGift(): void;
|
||||
}
|
||||
|
@ -169,6 +169,18 @@ class NumeralFormatter {
|
||||
return this.format(n, "0,0");
|
||||
}
|
||||
|
||||
formatStaneksGiftHeat(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatStaneksGiftCharge(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatStaneksGiftPower(n: number): string {
|
||||
return this.format(n, "0.00");
|
||||
}
|
||||
|
||||
parseMoney(s: string): number {
|
||||
// numeral library does not handle formats like 1e10 well (returns 110),
|
||||
// so if both return a valid number, return the biggest one
|
||||
|
Loading…
Reference in New Issue
Block a user