mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-29 19:13: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);
|
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
|
// Update costs based on how many have been purchased
|
||||||
mult = Math.pow(
|
mult = Math.pow(
|
||||||
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
|
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
|
||||||
|
@ -108,6 +108,9 @@ export const AugmentationNames: {
|
|||||||
BladeArmorOmnibeam: string;
|
BladeArmorOmnibeam: string;
|
||||||
BladeArmorIPU: string;
|
BladeArmorIPU: string;
|
||||||
BladesSimulacrum: string;
|
BladesSimulacrum: string;
|
||||||
|
StaneksGift1: string;
|
||||||
|
StaneksGift2: string;
|
||||||
|
StaneksGift3: string;
|
||||||
} = {
|
} = {
|
||||||
Targeting1: "Augmented Targeting I",
|
Targeting1: "Augmented Targeting I",
|
||||||
Targeting2: "Augmented Targeting II",
|
Targeting2: "Augmented Targeting II",
|
||||||
@ -219,6 +222,10 @@ export const AugmentationNames: {
|
|||||||
BladeArmorIPU: "BLADE-51b Tesla Armor: IPU Upgrade",
|
BladeArmorIPU: "BLADE-51b Tesla Armor: IPU Upgrade",
|
||||||
BladesSimulacrum: "The Blade's Simulacrum",
|
BladesSimulacrum: "The Blade's Simulacrum",
|
||||||
|
|
||||||
|
StaneksGift1: "Stanek's Gift - Genesis",
|
||||||
|
StaneksGift2: "Stanek's Gift - Awakening",
|
||||||
|
StaneksGift3: "Stanek's Gift - Serenity",
|
||||||
|
|
||||||
//Wasteland Augs
|
//Wasteland Augs
|
||||||
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
||||||
//PepBoyForceField Generates plasma force fields
|
//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
|
// 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["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
|
||||||
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
|
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
|
||||||
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
|
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
|
||||||
@ -553,6 +583,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers[mult] = 1;
|
BitNodeMultipliers[mult] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Special case.
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||||
|
|
||||||
switch (p.bitNodeN) {
|
switch (p.bitNodeN) {
|
||||||
case 1: // Source Genesis (every multiplier is 1)
|
case 1: // Source Genesis (every multiplier is 1)
|
||||||
@ -566,6 +598,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.InfiltrationMoney = 3;
|
BitNodeMultipliers.InfiltrationMoney = 3;
|
||||||
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
BitNodeMultipliers.FactionWorkRepGain = 0.5;
|
||||||
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
BitNodeMultipliers.FactionPassiveRepGain = 0;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 2;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = -6;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||||
break;
|
break;
|
||||||
@ -583,6 +617,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.HacknetNodeMoney = 0.25;
|
BitNodeMultipliers.HacknetNodeMoney = 0.25;
|
||||||
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
BitNodeMultipliers.HomeComputerRamCost = 1.5;
|
||||||
BitNodeMultipliers.PurchasedServerCost = 2;
|
BitNodeMultipliers.PurchasedServerCost = 2;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = -2;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
BitNodeMultipliers.PurchasedServerSoftcap = 1.3;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||||
break;
|
break;
|
||||||
@ -599,6 +635,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.HackExpGain = 0.4;
|
BitNodeMultipliers.HackExpGain = 0.4;
|
||||||
BitNodeMultipliers.CrimeExpGain = 0.5;
|
BitNodeMultipliers.CrimeExpGain = 0.5;
|
||||||
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
BitNodeMultipliers.FactionWorkRepGain = 0.75;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.5;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||||
break;
|
break;
|
||||||
case 5: // Artificial intelligence
|
case 5: // Artificial intelligence
|
||||||
@ -613,6 +651,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
BitNodeMultipliers.AugmentationMoneyCost = 2;
|
||||||
BitNodeMultipliers.HackExpGain = 0.5;
|
BitNodeMultipliers.HackExpGain = 0.5;
|
||||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 1.3;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = 0;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
BitNodeMultipliers.PurchasedServerSoftcap = 1.2;
|
||||||
break;
|
break;
|
||||||
case 6: // Bladeburner
|
case 6: // Bladeburner
|
||||||
@ -630,6 +670,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.HackExpGain = 0.25;
|
BitNodeMultipliers.HackExpGain = 0.25;
|
||||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||||
break;
|
break;
|
||||||
@ -653,6 +695,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
|
||||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
BitNodeMultipliers.PurchasedServerSoftcap = 2;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.9;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = -1;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.7;
|
BitNodeMultipliers.GangSoftcap = 0.7;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||||
break;
|
break;
|
||||||
@ -667,6 +711,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.RepToDonateToFaction = 0;
|
BitNodeMultipliers.RepToDonateToFaction = 0;
|
||||||
BitNodeMultipliers.CorporationValuation = 0;
|
BitNodeMultipliers.CorporationValuation = 0;
|
||||||
BitNodeMultipliers.CodingContractMoney = 0;
|
BitNodeMultipliers.CodingContractMoney = 0;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = -7;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 4;
|
BitNodeMultipliers.PurchasedServerSoftcap = 4;
|
||||||
BitNodeMultipliers.GangSoftcap = 0;
|
BitNodeMultipliers.GangSoftcap = 0;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0;
|
BitNodeMultipliers.CorporationSoftCap = 0;
|
||||||
@ -691,6 +736,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
|
||||||
BitNodeMultipliers.BladeburnerRank = 0.9;
|
BitNodeMultipliers.BladeburnerRank = 0.9;
|
||||||
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
BitNodeMultipliers.BladeburnerSkillCost = 1.2;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = 2;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.8;
|
BitNodeMultipliers.GangSoftcap = 0.8;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||||
break;
|
break;
|
||||||
@ -716,6 +763,8 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.PurchasedServerLimit = 0.6;
|
BitNodeMultipliers.PurchasedServerLimit = 0.6;
|
||||||
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
|
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
|
||||||
BitNodeMultipliers.BladeburnerRank = 0.8;
|
BitNodeMultipliers.BladeburnerRank = 0.8;
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.75;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = -3;
|
||||||
BitNodeMultipliers.PurchasedServerSoftcap = 1.1;
|
BitNodeMultipliers.PurchasedServerSoftcap = 1.1;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.9;
|
BitNodeMultipliers.GangSoftcap = 0.9;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
BitNodeMultipliers.CorporationSoftCap = 0.9;
|
||||||
@ -807,10 +856,56 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
|
|
||||||
BitNodeMultipliers.BladeburnerRank = dec;
|
BitNodeMultipliers.BladeburnerRank = dec;
|
||||||
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
||||||
|
|
||||||
|
BitNodeMultipliers.StaneksGiftPowerMultiplier = inc;
|
||||||
|
BitNodeMultipliers.StaneksGiftExtraSize = inc;
|
||||||
BitNodeMultipliers.GangSoftcap = 0.8;
|
BitNodeMultipliers.GangSoftcap = 0.8;
|
||||||
BitNodeMultipliers.CorporationSoftCap = 0.8;
|
BitNodeMultipliers.CorporationSoftCap = 0.8;
|
||||||
break;
|
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:
|
default:
|
||||||
console.warn("Player.bitNodeN invalid");
|
console.warn("Player.bitNodeN invalid");
|
||||||
break;
|
break;
|
||||||
|
@ -217,6 +217,21 @@ interface IBitNodeMultipliers {
|
|||||||
*/
|
*/
|
||||||
StrengthLevelMultiplier: number;
|
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
|
// Index signature
|
||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
}
|
}
|
||||||
@ -282,4 +297,9 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
|
|||||||
GangSoftcap: 1,
|
GangSoftcap: 1,
|
||||||
|
|
||||||
DaedalusAugsRequirement: 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</Typography>
|
||||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | 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'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</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'}}> \| / \| | / / \ |/ </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>
|
<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 { Bladeburner } from "./Bladeburner/Bladeburner";
|
||||||
import { IEngine } from "./IEngine";
|
import { IEngine } from "./IEngine";
|
||||||
import { IRouter } from "./ui/Router";
|
import { IRouter } from "./ui/Router";
|
||||||
|
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ import { Corporation } from "./DevMenu/ui/Corporation";
|
|||||||
import { CodingContracts } from "./DevMenu/ui/CodingContracts";
|
import { CodingContracts } from "./DevMenu/ui/CodingContracts";
|
||||||
import { StockMarket } from "./DevMenu/ui/StockMarket";
|
import { StockMarket } from "./DevMenu/ui/StockMarket";
|
||||||
import { Sleeves } from "./DevMenu/ui/Sleeves";
|
import { Sleeves } from "./DevMenu/ui/Sleeves";
|
||||||
|
import { Stanek } from "./DevMenu/ui/Stanek";
|
||||||
import { TimeSkip } from "./DevMenu/ui/TimeSkip";
|
import { TimeSkip } from "./DevMenu/ui/TimeSkip";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
|
|||||||
{props.player.hasWseAccount && <StockMarket />}
|
{props.player.hasWseAccount && <StockMarket />}
|
||||||
|
|
||||||
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />}
|
{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} />
|
<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,
|
true,
|
||||||
false,
|
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,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
@ -85,7 +85,7 @@ Cities[CityName.Chongqing].asciiArt = `
|
|||||||
\\ o 78 [kuaigong international]
|
\\ o 78 [kuaigong international]
|
||||||
\\ /
|
\\ /
|
||||||
38 o----x--x------x------A------G--
|
38 o----x--x------x------A------G--
|
||||||
/ 39 | 41 [church]
|
/ 39 | 41 [church]
|
||||||
37 o + 79 o--x--x-C-0
|
37 o + 79 o--x--x-C-0
|
||||||
/ | /
|
/ | /
|
||||||
/ x-----+-----x-----0 [hospital]
|
/ x-----+-----x-----0 [hospital]
|
||||||
|
@ -17,6 +17,9 @@ import Button from "@mui/material/Button";
|
|||||||
import { Location } from "../Location";
|
import { Location } from "../Location";
|
||||||
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
|
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
|
||||||
import { LocationName } from "../data/LocationNames";
|
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";
|
import { use } from "../../ui/Context";
|
||||||
|
|
||||||
@ -24,6 +27,7 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
|||||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||||
import { N00dles } from "../../utils/helpers/N00dles";
|
import { N00dles } from "../../utils/helpers/N00dles";
|
||||||
import { Exploit } from "../../Exploits/Exploit";
|
import { Exploit } from "../../Exploits/Exploit";
|
||||||
|
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -119,46 +123,130 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
return <Button onClick={handleResleeving}>Re-Sleeve</Button>;
|
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 {
|
function renderCotMG(): React.ReactElement {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const symbol = <Typography sx={{ lineHeight: '1em', whiteSpace: 'pre' }}>
|
const symbol = <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>
|
||||||
{" `` "}<br />
|
{" `` "}<br />
|
||||||
{" -odmmNmds: "}<br />
|
{" -odmmNmds: "}<br />
|
||||||
{" `hNmo:..-omNh. "}<br />
|
{" `hNmo:..-omNh. "}<br />
|
||||||
{" yMd` `hNh "}<br />
|
{" yMd` `hNh "}<br />
|
||||||
{" mMd oNm "}<br />
|
{" mMd oNm "}<br />
|
||||||
{" oMNo .mM/ "}<br />
|
{" oMNo .mM/ "}<br />
|
||||||
{" `dMN+ -mM+ "}<br />
|
{" `dMN+ -mM+ "}<br />
|
||||||
{" -mMNo -mN+ "}<br />
|
{" -mMNo -mN+ "}<br />
|
||||||
{" .+- :mMNo/mN/ "}<br />
|
{" .+- :mMNo/mN/ "}<br />
|
||||||
{":yNMd. :NMNNN/ "}<br />
|
{":yNMd. :NMNNN/ "}<br />
|
||||||
{"-mMMMh. /NMMh` "}<br />
|
{"-mMMMh. /NMMh` "}<br />
|
||||||
{" .dMMMd. /NMMMy` "}<br />
|
{" .dMMMd. /NMMMy` "}<br />
|
||||||
{" `yMMMd. /NNyNMMh` "}<br />
|
{" `yMMMd. /NNyNMMh` "}<br />
|
||||||
{" `sMMMd. +Nm: +NMMh. "}<br />
|
{" `sMMMd. +Nm: +NMMh. "}<br />
|
||||||
{" oMMMm- oNm: /NMMd. "}<br />
|
{" oMMMm- oNm: /NMMd. "}<br />
|
||||||
{" +NMMmsMm- :mMMd. "}<br />
|
{" +NMMmsMm- :mMMd. "}<br />
|
||||||
{" /NMMMm- -mMMd. "}<br />
|
{" /NMMMm- -mMMd. "}<br />
|
||||||
{" /MMMm- -mMMd. "}<br />
|
{" /MMMm- -mMMd. "}<br />
|
||||||
{" `sMNMMm- .mMmo "}<br />
|
{" `sMNMMm- .mMmo "}<br />
|
||||||
{" `sMd:hMMm. ./. "}<br />
|
{" `sMd:hMMm. ./. "}<br />
|
||||||
{" `yMy` `yNMd` "}<br />
|
{" `yMy` `yNMd` "}<br />
|
||||||
{" `hMs` oMMy "}<br />
|
{" `hMs` oMMy "}<br />
|
||||||
{" `hMh sMN- "}<br />
|
{" `hMh sMN- "}<br />
|
||||||
{" /MM- .NMo "}<br />
|
{" /MM- .NMo "}<br />
|
||||||
{" +MM: :MM+ "}<br />
|
{" +MM: :MM+ "}<br />
|
||||||
{" sNNo-.`.-omNy` "}<br />
|
{" sNNo-.`.-omNy` "}<br />
|
||||||
{" -smNNNNmdo- "}<br />
|
{" -smNNNNmdo- "}<br />
|
||||||
{" `..` "}</Typography>
|
{" `..` "}</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>
|
||||||
|
A decrepit altar stands in the middle of a dilapidated church.
|
||||||
|
<br />
|
||||||
|
<br />A symbol is carved in the altar.
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
{symbol}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography>
|
<Typography>
|
||||||
A decrepit altar stands in the middle of a dilapidated church.
|
<i>
|
||||||
<br />
|
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
|
||||||
<br />A symbol is carved in the altar.
|
form? If you are, accept my gift.
|
||||||
|
</i>
|
||||||
</Typography>
|
</Typography>
|
||||||
<br />
|
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
|
||||||
{symbol}
|
{symbol}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -55,6 +55,15 @@ export const RamCostConstants: IMap<number> = {
|
|||||||
ScriptGangApiBaseRamCost: 4,
|
ScriptGangApiBaseRamCost: 4,
|
||||||
|
|
||||||
ScriptBladeburnerApiBaseRamCost: 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> = {
|
export const RamCosts: IMap<any> = {
|
||||||
@ -326,6 +335,17 @@ export const RamCosts: IMap<any> = {
|
|||||||
purchaseSleeveAug: RamCostConstants.ScriptSleeveBaseRamCost,
|
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: {
|
heart: {
|
||||||
// Easter egg function
|
// Easter egg function
|
||||||
break: 0,
|
break: 0,
|
||||||
|
@ -63,14 +63,24 @@ import { NetscriptGang } from "./NetscriptFunctions/Gang";
|
|||||||
import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
|
import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
|
||||||
import { NetscriptExtra } from "./NetscriptFunctions/Extra";
|
import { NetscriptExtra } from "./NetscriptFunctions/Extra";
|
||||||
import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
|
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 { NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner";
|
||||||
import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract";
|
import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract";
|
||||||
import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
|
import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
|
||||||
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
|
import { NetscriptFormulas } from "./NetscriptFunctions/Formulas";
|
||||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
|
||||||
import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket";
|
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 { toNative } from "./NetscriptFunctions/toNative";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||||
@ -80,6 +90,9 @@ import { Flags } from "./NetscriptFunctions/Flags";
|
|||||||
|
|
||||||
interface NS extends INS {
|
interface NS extends INS {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
gang: IGang;
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
stanek: IStanek;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||||
@ -431,6 +444,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
||||||
const extra = NetscriptExtra(Player, workerScript);
|
const extra = NetscriptExtra(Player, workerScript);
|
||||||
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
||||||
|
const stanek = NetscriptStanek(Player, workerScript, helper);
|
||||||
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
||||||
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
||||||
const corporation = NetscriptCorporation(Player);
|
const corporation = NetscriptCorporation(Player);
|
||||||
@ -445,6 +459,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
bladeburner: bladeburner,
|
bladeburner: bladeburner,
|
||||||
codingcontract: codingcontract,
|
codingcontract: codingcontract,
|
||||||
sleeve: sleeve,
|
sleeve: sleeve,
|
||||||
|
stanek: stanek,
|
||||||
|
|
||||||
formulas: formulas,
|
formulas: formulas,
|
||||||
stock: stockmarket,
|
stock: stockmarket,
|
||||||
@ -2255,7 +2270,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
Object.assign(data.jobs, Player.jobs);
|
Object.assign(data.jobs, Player.jobs);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
atExit: function (f: any): void {
|
atExit: function (f: any): void {
|
||||||
if (typeof f !== "function") {
|
if (typeof f !== "function") {
|
||||||
throw makeRuntimeErrorMsg("atExit", "argument should be 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;
|
if (typeof workerScript.env.vars[prop] !== "function") continue;
|
||||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
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
|
// 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.
|
// to that script, which env.vars does at this point.
|
||||||
|
@ -277,5 +277,6 @@ export interface IPlayer {
|
|||||||
setBitNodeNumber(n: number): void;
|
setBitNodeNumber(n: number): void;
|
||||||
getMult(name: string): number;
|
getMult(name: string): number;
|
||||||
setMult(name: string, mult: number): void;
|
setMult(name: string, mult: number): void;
|
||||||
|
canAccessCotMG(): boolean;
|
||||||
sourceFileLvl(n: number): number;
|
sourceFileLvl(n: number): number;
|
||||||
}
|
}
|
||||||
|
@ -283,6 +283,7 @@ export class PlayerObject implements IPlayer {
|
|||||||
setBitNodeNumber: (n: number) => void;
|
setBitNodeNumber: (n: number) => void;
|
||||||
getMult: (name: string) => number;
|
getMult: (name: string) => number;
|
||||||
setMult: (name: string, mult: number) => void;
|
setMult: (name: string, mult: number) => void;
|
||||||
|
canAccessCotMG: () => boolean;
|
||||||
sourceFileLvl: (n: number) => number;
|
sourceFileLvl: (n: number) => number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -579,6 +580,8 @@ export class PlayerObject implements IPlayer {
|
|||||||
|
|
||||||
this.getMult = generalMethods.getMult;
|
this.getMult = generalMethods.getMult;
|
||||||
this.setMult = generalMethods.setMult;
|
this.setMult = generalMethods.setMult;
|
||||||
|
|
||||||
|
this.canAccessCotMG = generalMethods.canAccessCotMG;
|
||||||
this.sourceFileLvl = generalMethods.sourceFileLvl;
|
this.sourceFileLvl = generalMethods.sourceFileLvl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,14 @@ export function generateResleeves(): Resleeve[] {
|
|||||||
const randKey: string = augKeys[randIndex];
|
const randKey: string = augKeys[randIndex];
|
||||||
|
|
||||||
// Forbidden augmentations
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +26,10 @@ import { Terminal } from "./Terminal";
|
|||||||
|
|
||||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||||
|
|
||||||
|
import { staneksGift } from "./CotMG/Helper";
|
||||||
import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
|
import { ProgramsSeen } from "./Programs/ui/ProgramsRoot";
|
||||||
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
|
import { InvitationsSeen } from "./Faction/ui/FactionsRoot";
|
||||||
|
import { CONSTANTS } from "./Constants";
|
||||||
import { LogBoxClearEvents } from "./ui/React/LogBoxManager";
|
import { LogBoxClearEvents } from "./ui/React/LogBoxManager";
|
||||||
|
|
||||||
const BitNode8StartingMoney = 250e6;
|
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();
|
resetPidCounter();
|
||||||
ProgramsSeen.splice(0, ProgramsSeen.length);
|
ProgramsSeen.splice(0, ProgramsSeen.length);
|
||||||
InvitationsSeen.splice(0, InvitationsSeen.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!");
|
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
|
// Reset Stock market, gang, and corporation
|
||||||
if (Player.hasWseAccount) {
|
if (Player.hasWseAccount) {
|
||||||
initStockMarket();
|
initStockMarket();
|
||||||
@ -272,6 +286,11 @@ export function prestigeSourceFile(flume: boolean): void {
|
|||||||
updateHashManagerCapacity(Player);
|
updateHashManagerCapacity(Player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Player.bitNodeN === 13) {
|
||||||
|
Player.money = CONSTANTS.TravelCost;
|
||||||
|
}
|
||||||
|
staneksGift.prestigeSourceFile();
|
||||||
|
|
||||||
// Gain int exp
|
// Gain int exp
|
||||||
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
|
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 { Settings } from "./Settings/Settings";
|
||||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||||
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
|
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
|
||||||
|
import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
|
||||||
|
|
||||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ class BitburnerSaveObject {
|
|||||||
VersionSave = "";
|
VersionSave = "";
|
||||||
AllGangsSave = "";
|
AllGangsSave = "";
|
||||||
LastExportBonus = "";
|
LastExportBonus = "";
|
||||||
|
StaneksGiftSave = "";
|
||||||
|
|
||||||
getSaveString(): string {
|
getSaveString(): string {
|
||||||
this.PlayerSave = JSON.stringify(Player);
|
this.PlayerSave = JSON.stringify(Player);
|
||||||
@ -52,6 +54,7 @@ class BitburnerSaveObject {
|
|||||||
this.SettingsSave = JSON.stringify(Settings);
|
this.SettingsSave = JSON.stringify(Settings);
|
||||||
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
this.VersionSave = JSON.stringify(CONSTANTS.VersionNumber);
|
||||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||||
|
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||||
if (Player.inGang()) {
|
if (Player.inGang()) {
|
||||||
this.AllGangsSave = JSON.stringify(AllGangs);
|
this.AllGangsSave = JSON.stringify(AllGangs);
|
||||||
}
|
}
|
||||||
@ -240,6 +243,12 @@ function loadGame(saveString: string): boolean {
|
|||||||
loadCompanies(saveObj.CompaniesSave);
|
loadCompanies(saveObj.CompaniesSave);
|
||||||
loadFactions(saveObj.FactionsSave);
|
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")) {
|
if (saveObj.hasOwnProperty("AliasesSave")) {
|
||||||
try {
|
try {
|
||||||
loadAliases(saveObj.AliasesSave);
|
loadAliases(saveObj.AliasesSave);
|
||||||
|
@ -183,6 +183,8 @@ async function parseOnlyRamCalculate(
|
|||||||
func = workerScript.env.vars.bladeburner[ref];
|
func = workerScript.env.vars.bladeburner[ref];
|
||||||
} else if (ref in workerScript.env.vars.codingcontract) {
|
} else if (ref in workerScript.env.vars.codingcontract) {
|
||||||
func = workerScript.env.vars.codingcontract[ref];
|
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) {
|
} else if (ref in workerScript.env.vars.gang) {
|
||||||
func = workerScript.env.vars.gang[ref];
|
func = workerScript.env.vars.gang[ref];
|
||||||
} else if (ref in workerScript.env.vars.sleeve) {
|
} 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;
|
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
|
* Collection of all functions passed to scripts
|
||||||
* @public
|
* @public
|
||||||
@ -3374,6 +3493,11 @@ export interface NS extends Singularity {
|
|||||||
* RAM cost: 0 GB
|
* RAM cost: 0 GB
|
||||||
*/
|
*/
|
||||||
readonly formulas: Formulas;
|
readonly formulas: Formulas;
|
||||||
|
/**
|
||||||
|
* Namespace for stanek functions.
|
||||||
|
* @ramCost 0 GB
|
||||||
|
*/
|
||||||
|
readonly stanek: Stanek;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arguments passed into the script.
|
* Arguments passed into the script.
|
||||||
|
@ -9,6 +9,8 @@ import { createRandomIp } from "../utils/IPAddress";
|
|||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { Reviver } from "../utils/JSONReviver";
|
import { Reviver } from "../utils/JSONReviver";
|
||||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
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
|
* Map of all Servers that exist in the game
|
||||||
@ -161,6 +163,9 @@ export function initForeignServers(homeComputer: Server): void {
|
|||||||
server.messages.push(filename);
|
server.messages.push(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server.hostname === SpecialServers.WorldDaemon) {
|
||||||
|
server.requiredHackingSkill *= BitNodeMultipliers.WorldDaemonDifficulty;
|
||||||
|
}
|
||||||
AddToAllServers(server);
|
AddToAllServers(server);
|
||||||
if (metadata.networkLayer !== undefined) {
|
if (metadata.networkLayer !== undefined) {
|
||||||
networkLayers[toNumber(metadata.networkLayer) - 1].push(server);
|
networkLayers[toNumber(metadata.networkLayer) - 1].push(server);
|
||||||
|
@ -49,6 +49,7 @@ import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveT
|
|||||||
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
|
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { redPillFlag } from "../../RedPill";
|
import { redPillFlag } from "../../RedPill";
|
||||||
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
|
|
||||||
import { KEY } from "../../utils/helpers/keyCodes";
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
||||||
@ -157,13 +158,12 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
const canOpenSleeves = props.player.sleeves.length > 0;
|
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 canCorporation = !!(props.player.corporation as any);
|
||||||
const canGang = !!(props.player.gang as any);
|
const canGang = !!(props.player.gang as any);
|
||||||
const canJob = props.player.companyName !== "";
|
const canJob = props.player.companyName !== "";
|
||||||
const canStockMarket = props.player.hasWseAccount;
|
const canStockMarket = props.player.hasWseAccount;
|
||||||
const canBladeburner = !!(props.player.bladeburner as any);
|
const canBladeburner = !!(props.player.bladeburner as any);
|
||||||
|
const canStaneksGift = props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1);
|
||||||
|
|
||||||
function clickTerminal(): void {
|
function clickTerminal(): void {
|
||||||
props.router.toTerminal();
|
props.router.toTerminal();
|
||||||
@ -188,6 +188,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
props.router.toCreateProgram();
|
props.router.toCreateProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clickStaneksGift(): void {
|
||||||
|
props.router.toStaneksGift();
|
||||||
|
}
|
||||||
|
|
||||||
function clickFactions(): void {
|
function clickFactions(): void {
|
||||||
props.router.toFactions();
|
props.router.toFactions();
|
||||||
}
|
}
|
||||||
@ -427,6 +431,25 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
</ListItemText>
|
</ListItemText>
|
||||||
</ListItem>
|
</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>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
|
@ -204,3 +204,7 @@ SourceFiles["SourceFile12"] = new SourceFile(
|
|||||||
12,
|
12,
|
||||||
<>This Source-File lets the player start with Neuroflux Governor equal to the level of this Source-File.</>,
|
<>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;
|
break;
|
||||||
}
|
}
|
||||||
case 12: // The Recursion
|
case 12: // The Recursion
|
||||||
// No effects, grants neuroflux.
|
// Grants neuroflux.
|
||||||
|
break;
|
||||||
|
case 13: // They're Lunatics
|
||||||
|
// Grants more space on Stanek's Gift.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(`Invalid source file number: ${srcFile.n}`);
|
console.error(`Invalid source file number: ${srcFile.n}`);
|
||||||
|
@ -12,6 +12,7 @@ import { initCompanies } from "./Company/Companies";
|
|||||||
import { Corporation } from "./Corporation/Corporation";
|
import { Corporation } from "./Corporation/Corporation";
|
||||||
import { CONSTANTS } from "./Constants";
|
import { CONSTANTS } from "./Constants";
|
||||||
import { Factions, initFactions } from "./Faction/Factions";
|
import { Factions, initFactions } from "./Faction/Factions";
|
||||||
|
import { staneksGift } from "./CotMG/Helper";
|
||||||
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
|
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
|
||||||
import { Router } from "./ui/GameRoot";
|
import { Router } from "./ui/GameRoot";
|
||||||
import { SetupTextEditor } from "./ScriptEditor/ui/ScriptEditorRoot";
|
import { SetupTextEditor } from "./ScriptEditor/ui/ScriptEditorRoot";
|
||||||
@ -101,6 +102,9 @@ const Engine: {
|
|||||||
Player.gang.process(numCycles, Player);
|
Player.gang.process(numCycles, Player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Staneks gift
|
||||||
|
staneksGift.process(Player, numCycles);
|
||||||
|
|
||||||
// Corporation
|
// Corporation
|
||||||
if (Player.corporation instanceof Corporation) {
|
if (Player.corporation instanceof Corporation) {
|
||||||
// Stores cycles in a "buffer". Processed separately using Engine Counters
|
// Stores cycles in a "buffer". Processed separately using Engine Counters
|
||||||
@ -345,6 +349,8 @@ const Engine: {
|
|||||||
Player.bladeburner.storeCycles(numCyclesOffline);
|
Player.bladeburner.storeCycles(numCyclesOffline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
staneksGift.process(Player, numCyclesOffline);
|
||||||
|
|
||||||
// Sleeves offline progress
|
// Sleeves offline progress
|
||||||
for (let i = 0; i < Player.sleeves.length; ++i) {
|
for (let i = 0; i < Player.sleeves.length; ++i) {
|
||||||
if (Player.sleeves[i] instanceof Sleeve) {
|
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 { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
|
||||||
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
|
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
|
||||||
import { BitverseRoot } from "../BitNode/ui/BitverseRoot";
|
import { BitverseRoot } from "../BitNode/ui/BitverseRoot";
|
||||||
|
import { StaneksGiftRoot } from "../CotMG/ui/StaneksGiftRoot";
|
||||||
|
import { staneksGift } from "../CotMG/Helper";
|
||||||
import { CharacterOverview } from "./React/CharacterOverview";
|
import { CharacterOverview } from "./React/CharacterOverview";
|
||||||
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
|
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
|
||||||
import { workerScripts } from "../Netscript/WorkerScripts";
|
import { workerScripts } from "../Netscript/WorkerScripts";
|
||||||
@ -181,6 +183,9 @@ export let Router: IRouter = {
|
|||||||
toLocation: () => {
|
toLocation: () => {
|
||||||
throw new Error("Router called before initialization");
|
throw new Error("Router called before initialization");
|
||||||
},
|
},
|
||||||
|
toStaneksGift: () => {
|
||||||
|
throw new Error("Router called before initialization");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function determineStartPage(player: IPlayer): Page {
|
function determineStartPage(player: IPlayer): Page {
|
||||||
@ -279,6 +284,9 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
setLocation(location);
|
setLocation(location);
|
||||||
setPage(Page.Location);
|
setPage(Page.Location);
|
||||||
},
|
},
|
||||||
|
toStaneksGift: () => {
|
||||||
|
setPage(Page.StaneksGift);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -316,6 +324,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
<TerminalRoot terminal={terminal} router={Router} player={player} />
|
<TerminalRoot terminal={terminal} router={Router} player={player} />
|
||||||
) : page === Page.Sleeves ? (
|
) : page === Page.Sleeves ? (
|
||||||
<SleeveRoot />
|
<SleeveRoot />
|
||||||
|
) : page === Page.StaneksGift ? (
|
||||||
|
<StaneksGiftRoot staneksGift={staneksGift} />
|
||||||
) : page === Page.Stats ? (
|
) : page === Page.Stats ? (
|
||||||
<CharacterStats />
|
<CharacterStats />
|
||||||
) : page === Page.ScriptEditor ? (
|
) : page === Page.ScriptEditor ? (
|
||||||
|
@ -641,6 +641,9 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
<Link href="https://www.reddit.com/r/bitburner" target="_blank">
|
<Link href="https://www.reddit.com/r/bitburner" target="_blank">
|
||||||
<Typography>Reddit</Typography>
|
<Typography>Reddit</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link href="https://plaza.dsolver.ca/games/bitburner" target="_blank">
|
||||||
|
<Typography>Incremental game plaza</Typography>
|
||||||
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -34,6 +34,7 @@ export enum Page {
|
|||||||
BladeburnerCinematic,
|
BladeburnerCinematic,
|
||||||
Location,
|
Location,
|
||||||
Loading,
|
Loading,
|
||||||
|
StaneksGift,
|
||||||
Recovery,
|
Recovery,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,4 +75,5 @@ export interface IRouter {
|
|||||||
toWork(): void;
|
toWork(): void;
|
||||||
toBladeburnerCinematic(): void;
|
toBladeburnerCinematic(): void;
|
||||||
toLocation(location: Location): void;
|
toLocation(location: Location): void;
|
||||||
|
toStaneksGift(): void;
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,18 @@ class NumeralFormatter {
|
|||||||
return this.format(n, "0,0");
|
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 {
|
parseMoney(s: string): number {
|
||||||
// numeral library does not handle formats like 1e10 well (returns 110),
|
// numeral library does not handle formats like 1e10 well (returns 110),
|
||||||
// so if both return a valid number, return the biggest one
|
// so if both return a valid number, return the biggest one
|
||||||
|
Loading…
Reference in New Issue
Block a user