mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-30 18:07:33 +01:00
update BN13 for new UI
This commit is contained in:
parent
3aacab504b
commit
793d9b34ce
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;
|
||||||
|
}
|
@ -30,5 +30,6 @@ to reach out to the developer!
|
|||||||
Gang API <netscript/netscriptgangapi>
|
Gang API <netscript/netscriptgangapi>
|
||||||
Coding Contract API <netscript/netscriptcodingcontractapi>
|
Coding Contract API <netscript/netscriptcodingcontractapi>
|
||||||
Sleeve API <netscript/netscriptsleeveapi>
|
Sleeve API <netscript/netscriptsleeveapi>
|
||||||
|
Stanek API <netscript/netscriptstanekapi>
|
||||||
Formulas API <netscript/netscriptformulasapi>
|
Formulas API <netscript/netscriptformulasapi>
|
||||||
Miscellaneous <netscript/netscriptmisc>
|
Miscellaneous <netscript/netscriptmisc>
|
||||||
|
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>
|
15
doc/source/netscript/stanekapi/canPlace.rst
Normal file
15
doc/source/netscript/stanekapi/canPlace.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
canPlace() Netscript Function
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. js:function:: canPlace(worldX, worldY, 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 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
|
15
doc/source/netscript/stanekapi/place.rst
Normal file
15
doc/source/netscript/stanekapi/place.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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 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
|
27
doc/source/netscript/stanekapi/placedFragments.rst
Normal file
27
doc/source/netscript/stanekapi/placedFragments.rst
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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;
|
||||||
|
heat: number;
|
||||||
|
charge: number;
|
||||||
|
id: number;
|
||||||
|
shape: boolean[][];
|
||||||
|
type: string;
|
||||||
|
magnitude: number;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
var myFragments = placedFragments();
|
@ -2344,6 +2344,142 @@ 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.<br><br>" +
|
||||||
|
"Its unstable nature decreases all your stats by 10%",
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
|
resetAugmentation(StaneksGift1);
|
||||||
|
|
||||||
|
const StaneksGift2 = new Augmentation({
|
||||||
|
name: AugmentationNames.StaneksGift2,
|
||||||
|
repCost: 1000,
|
||||||
|
moneyCost: 0,
|
||||||
|
info:
|
||||||
|
'TODO, something about Mother being bullshit and you get more control over her "gift"<br><br>' +
|
||||||
|
"The penalty for the gift is only 5%",
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
|
resetAugmentation(StaneksGift2);
|
||||||
|
|
||||||
|
const StaneksGift3 = new Augmentation({
|
||||||
|
name: AugmentationNames.StaneksGift3,
|
||||||
|
repCost: 10000,
|
||||||
|
moneyCost: 0,
|
||||||
|
info:
|
||||||
|
"TODO, learn more about Allisons scheme, gain full control over the gift.<br><br>" +
|
||||||
|
"Finally freed from the penalty of the gift.",
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
|
resetAugmentation(StaneksGift3);
|
||||||
|
|
||||||
|
const StaneksGiftAscension4 = new Augmentation({
|
||||||
|
name: AugmentationNames.StaneksGift4,
|
||||||
|
repCost: 500000000,
|
||||||
|
moneyCost: 0,
|
||||||
|
info:
|
||||||
|
"Allow Allison to install an Ascension port in her Gift. Allowing you to connect with the Machine God.<br><br>" +
|
||||||
|
"(hydro notes: Finishes the BN, eventually)",
|
||||||
|
prereqs: [AugmentationNames.StaneksGift3],
|
||||||
|
isSpecial: true,
|
||||||
|
});
|
||||||
|
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
|
resetAugmentation(StaneksGiftAscension4);
|
||||||
|
}
|
||||||
|
|
||||||
// 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]],
|
||||||
|
@ -110,6 +110,11 @@ export const AugmentationNames: IMap<string> = {
|
|||||||
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",
|
||||||
|
StaneksGift4: "Stanek's Gift - Ascension",
|
||||||
|
|
||||||
//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
|
||||||
|
@ -521,8 +521,48 @@ 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 Chongquin and gain her trust.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
In this BitNode:
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Every 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 />
|
||||||
|
This Source-File also increases Stanek's Gift multipliers by:
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Level 1: 8%
|
||||||
|
<br />
|
||||||
|
Level 2: 12%
|
||||||
|
<br />
|
||||||
|
Level 3: 14%
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Each level of this Source-File also increases the size of the 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");
|
||||||
@ -784,6 +824,45 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
|||||||
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 13: {
|
||||||
|
BitNodeMultipliers.DaedalusAugsRequirement = 100;
|
||||||
|
|
||||||
|
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||||
|
BitNodeMultipliers.StrengthLevelMultiplier = 0.2;
|
||||||
|
BitNodeMultipliers.DefenseLevelMultiplier = 0.2;
|
||||||
|
BitNodeMultipliers.DexterityLevelMultiplier = 0.2;
|
||||||
|
BitNodeMultipliers.AgilityLevelMultiplier = 0.2;
|
||||||
|
BitNodeMultipliers.CharismaLevelMultiplier = 0.2;
|
||||||
|
|
||||||
|
BitNodeMultipliers.ServerMaxMoney = 0.15;
|
||||||
|
BitNodeMultipliers.ServerStartingMoney = 0.75;
|
||||||
|
|
||||||
|
BitNodeMultipliers.ServerStartingSecurity = 2;
|
||||||
|
|
||||||
|
BitNodeMultipliers.ScriptHackMoney = 0.2;
|
||||||
|
BitNodeMultipliers.CompanyWorkMoney = 0.2;
|
||||||
|
BitNodeMultipliers.CrimeMoney = 0.2;
|
||||||
|
BitNodeMultipliers.HacknetNodeMoney = 0.2;
|
||||||
|
BitNodeMultipliers.CodingContractMoney = 0.2;
|
||||||
|
|
||||||
|
BitNodeMultipliers.CompanyWorkExpGain = 0.1;
|
||||||
|
BitNodeMultipliers.ClassGymExpGain = 0.1;
|
||||||
|
BitNodeMultipliers.FactionWorkExpGain = 0.1;
|
||||||
|
BitNodeMultipliers.HackExpGain = 0.1;
|
||||||
|
BitNodeMultipliers.CrimeExpGain = 0.1;
|
||||||
|
|
||||||
|
BitNodeMultipliers.FactionWorkRepGain = 0.4;
|
||||||
|
|
||||||
|
BitNodeMultipliers.FourSigmaMarketDataCost = 10;
|
||||||
|
BitNodeMultipliers.FourSigmaMarketDataApiCost = 10;
|
||||||
|
|
||||||
|
BitNodeMultipliers.CorporationValuation = 0.001;
|
||||||
|
|
||||||
|
BitNodeMultipliers.BladeburnerRank = 0.1;
|
||||||
|
BitNodeMultipliers.BladeburnerSkillCost = 5;
|
||||||
|
BitNodeMultipliers.GangKarmaRequirement = 20;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn("Player.bitNodeN invalid");
|
console.warn("Player.bitNodeN invalid");
|
||||||
break;
|
break;
|
||||||
|
@ -126,7 +126,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
|
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
|
||||||
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
|
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
|
||||||
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
|
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
|
||||||
<pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre>
|
<pre> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </pre>
|
||||||
<pre> | | |_/ | | \| / | \_| | | </pre>
|
<pre> | | |_/ | | \| / | \_| | | </pre>
|
||||||
<pre> \| / \| | / / \ |/ </pre>
|
<pre> \| / \| | / / \ |/ </pre>
|
||||||
<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} /> | </pre>
|
<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} /> | </pre>
|
||||||
|
94
src/CotMG/ActiveFragment.ts
Normal file
94
src/CotMG/ActiveFragment.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { Fragment, FragmentById } from "./Fragment";
|
||||||
|
import { FragmentType } from "./FragmentType";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
|
const noCharge = [FragmentType.None, FragmentType.Delete, FragmentType.Booster, FragmentType.Cooling];
|
||||||
|
|
||||||
|
export interface IActiveFragmentParams {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
fragment: Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ActiveFragment {
|
||||||
|
id: number;
|
||||||
|
charge: number;
|
||||||
|
heat: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
|
||||||
|
constructor(params?: IActiveFragmentParams) {
|
||||||
|
if (params) {
|
||||||
|
this.id = params.fragment.id;
|
||||||
|
this.x = params.x;
|
||||||
|
this.y = params.y;
|
||||||
|
this.charge = 1;
|
||||||
|
if (noCharge.includes(params.fragment.type)) this.charge = 0;
|
||||||
|
this.heat = 1;
|
||||||
|
} else {
|
||||||
|
this.id = -1;
|
||||||
|
this.x = -1;
|
||||||
|
this.y = -1;
|
||||||
|
this.charge = -1;
|
||||||
|
this.heat = -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) && otherFragment.fullAt(i - dx, j - dy)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment(): Fragment {
|
||||||
|
const fragment = FragmentById(this.id);
|
||||||
|
if (fragment === null) throw "ActiveFragment id refers to unknown Fragment.";
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullAt(worldX: number, worldY: number): boolean {
|
||||||
|
return this.fragment().fullAt(worldX - this.x, worldY - this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
neighboors(): number[][] {
|
||||||
|
return this.fragment()
|
||||||
|
.neighboors()
|
||||||
|
.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 "ActiveFragment id refers to unknown Fragment.";
|
||||||
|
const c = new ActiveFragment({ x: this.x, y: this.y, fragment: fragment });
|
||||||
|
c.charge = this.charge;
|
||||||
|
c.heat = this.heat;
|
||||||
|
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;
|
290
src/CotMG/Fragment.ts
Normal file
290
src/CotMG/Fragment.ts
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
import { FragmentType } from "./FragmentType";
|
||||||
|
|
||||||
|
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): boolean {
|
||||||
|
if(y < 0) return false;
|
||||||
|
if(y >= this.shape.length) return false;
|
||||||
|
if(x < 0) return false;
|
||||||
|
if(x >= this.shape[y].length) return false;
|
||||||
|
// Yes it's ordered y first.
|
||||||
|
return this.shape[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
// check every line for robustness.
|
||||||
|
return Math.max(...this.shape.map(line => line.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
height() {
|
||||||
|
return this.shape.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of direct neighboors of this fragment.
|
||||||
|
neighboors(): number[][] {
|
||||||
|
let candidates: number[][] = [];
|
||||||
|
|
||||||
|
const add = (x: number, y: number): void => {
|
||||||
|
if(this.fullAt(x, y)) return;
|
||||||
|
if(candidates.some(coord => coord[0] === x && coord[1] === y)) return;
|
||||||
|
candidates.push([x, y]);
|
||||||
|
};
|
||||||
|
for(let y = 0; y < this.shape.length; y++) {
|
||||||
|
for(let x = 0; x < this.shape[y].length; x++) {
|
||||||
|
// This cell is full, add all it's neighboors.
|
||||||
|
if(!this.shape[y][x]) 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
|
||||||
|
[ // shape
|
||||||
|
[X,X,X],
|
||||||
|
[_,_,X],
|
||||||
|
[_,_,X],
|
||||||
|
],
|
||||||
|
FragmentType.Hacking, // type
|
||||||
|
10,
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
1, // id
|
||||||
|
[ // shape
|
||||||
|
[_,X,_],
|
||||||
|
[X,X,X],
|
||||||
|
[_,X,_],
|
||||||
|
],
|
||||||
|
FragmentType.Hacking, // type
|
||||||
|
10,
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
2, // id
|
||||||
|
[ // shape
|
||||||
|
[X,X,X],
|
||||||
|
[X,_,X],
|
||||||
|
[X,X,X],
|
||||||
|
],
|
||||||
|
FragmentType.Booster, // type
|
||||||
|
500,
|
||||||
|
3, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
3, // id
|
||||||
|
[ // shape
|
||||||
|
[X,X],
|
||||||
|
[X,X],
|
||||||
|
],
|
||||||
|
FragmentType.Cooling, // type
|
||||||
|
200,
|
||||||
|
Infinity, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
4, // id
|
||||||
|
[ // shape
|
||||||
|
[X],
|
||||||
|
],
|
||||||
|
FragmentType.Cooling, // type
|
||||||
|
50,
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
5, // id
|
||||||
|
[ // shape
|
||||||
|
[X, X],
|
||||||
|
],
|
||||||
|
FragmentType.HackingSpeed, // type
|
||||||
|
50,
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
6, // id
|
||||||
|
[
|
||||||
|
[X, _],
|
||||||
|
[X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.HackingMoney, // type
|
||||||
|
10, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
7, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
[X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.HackingGrow, // type
|
||||||
|
30, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
8, // id
|
||||||
|
[
|
||||||
|
[X, X, X],
|
||||||
|
[_, X, _],
|
||||||
|
[X, X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Hacking, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
10, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
[_, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Strength, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
12, // id
|
||||||
|
[
|
||||||
|
[_, X],
|
||||||
|
[X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Defense, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
14, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
[X, _],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Dexterity, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
16, // id
|
||||||
|
[
|
||||||
|
[X, _],
|
||||||
|
[X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Agility, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
18, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
[X, _],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Charisma, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
20, // id
|
||||||
|
[
|
||||||
|
[X, _, _],
|
||||||
|
[X, X, _],
|
||||||
|
[X, X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.HacknetMoney, // type
|
||||||
|
30, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
21, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
[_, X],
|
||||||
|
[_, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.HacknetCost, // type
|
||||||
|
-10, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
25, // id
|
||||||
|
[
|
||||||
|
[X, X, X],
|
||||||
|
[_, X, _],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Rep, // type
|
||||||
|
100, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
27, // id
|
||||||
|
[
|
||||||
|
[X, _],
|
||||||
|
[_, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.WorkMoney, // type
|
||||||
|
20, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
28, // id
|
||||||
|
[
|
||||||
|
[X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Crime, // type
|
||||||
|
20, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
Fragments.push(new Fragment(
|
||||||
|
30, // id
|
||||||
|
[
|
||||||
|
[X, X, X],
|
||||||
|
[X, X, X],
|
||||||
|
[X, X, X],
|
||||||
|
], // shape
|
||||||
|
FragmentType.Bladeburner, // type
|
||||||
|
50, // power
|
||||||
|
1, // limit
|
||||||
|
));
|
||||||
|
})();
|
||||||
|
|
||||||
|
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
|
||||||
|
export const DeleteFragment = new Fragment(-2, [], FragmentType.Delete, 0, Infinity);
|
27
src/CotMG/FragmentType.ts
Normal file
27
src/CotMG/FragmentType.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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,
|
||||||
|
Cooling,
|
||||||
|
}
|
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();
|
||||||
|
}
|
||||||
|
}
|
18
src/CotMG/IStaneksGift.ts
Normal file
18
src/CotMG/IStaneksGift.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { StaneksGift } from "./StaneksGift";
|
||||||
|
import { ActiveFragment } from "./ActiveFragment";
|
||||||
|
import { Fragment } from "./Fragment";
|
||||||
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
export interface IStaneksGift {
|
||||||
|
fragments: ActiveFragment[];
|
||||||
|
width(): number;
|
||||||
|
height(): number;
|
||||||
|
charge(worldX: number, worldY: number, ram: number): number;
|
||||||
|
process(p: IPlayer, n: number): void;
|
||||||
|
canPlace(x: number, y: number, fragment: Fragment): boolean;
|
||||||
|
place(x: number, y: number, fragment: Fragment): boolean;
|
||||||
|
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
|
||||||
|
deleteAt(worldX: number, worldY: number): boolean;
|
||||||
|
clear(): void;
|
||||||
|
count(fragment: Fragment): number;
|
||||||
|
};
|
209
src/CotMG/StaneksGift.ts
Normal file
209
src/CotMG/StaneksGift.ts
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import { Fragment, FragmentById } 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 { CalculateCharge } from "./formulas/charge";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
|
export class StaneksGift implements IStaneksGift {
|
||||||
|
fragments: ActiveFragment[] = [];
|
||||||
|
|
||||||
|
width(): number {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
height(): number {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
charge(worldX: number, worldY: number, ram: number): number {
|
||||||
|
const af = this.fragmentAt(worldX, worldY);
|
||||||
|
if (af === null) return 0;
|
||||||
|
|
||||||
|
// Find all the neighbooring cells
|
||||||
|
const cells = af.neighboors();
|
||||||
|
|
||||||
|
// find the neighbooring active fragments.
|
||||||
|
const maybeFragments = cells.map((n) => this.fragmentAt(n[0], n[1]));
|
||||||
|
|
||||||
|
// Filter out nulls with typescript "Type guard". Whatever
|
||||||
|
let neighboors = maybeFragments.filter((v: ActiveFragment | null): v is ActiveFragment => !!v);
|
||||||
|
|
||||||
|
// filter unique fragments
|
||||||
|
neighboors = neighboors.filter((value, index) => neighboors.indexOf(value) === index);
|
||||||
|
|
||||||
|
// count number of neighbooring boosts and cooling.
|
||||||
|
let boost = 1;
|
||||||
|
let cool = 1;
|
||||||
|
for (const neighboor of neighboors) {
|
||||||
|
const f = neighboor.fragment();
|
||||||
|
if (f.type === FragmentType.Cooling) cool *= 1 + f.power / 1000;
|
||||||
|
if (f.type === FragmentType.Booster) boost *= 1 + f.power / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [extraCharge, extraHeat] = CalculateCharge(ram, af.heat, boost, cool);
|
||||||
|
af.charge += extraCharge;
|
||||||
|
af.heat += extraHeat;
|
||||||
|
|
||||||
|
Factions["Church of the Machine God"].playerReputation += extraCharge;
|
||||||
|
|
||||||
|
return ram;
|
||||||
|
}
|
||||||
|
|
||||||
|
process(p: IPlayer, numCycles: number): void {
|
||||||
|
for (const activeFragment of this.fragments) {
|
||||||
|
const fragment = activeFragment.fragment();
|
||||||
|
|
||||||
|
// Boosters and cooling don't deal with heat.
|
||||||
|
if (fragment.type === FragmentType.Booster || fragment.type === FragmentType.Cooling) continue;
|
||||||
|
activeFragment.heat *= 0.98;
|
||||||
|
activeFragment.heat -= 1;
|
||||||
|
if (activeFragment.heat < 1) activeFragment.heat = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateMults(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
canPlace(x: number, y: number, fragment: Fragment): boolean {
|
||||||
|
if (x + fragment.width() > this.width()) return false;
|
||||||
|
if (y + fragment.height() > this.height()) return false;
|
||||||
|
if (this.count(fragment) >= fragment.limit) return false;
|
||||||
|
const newFrag = new ActiveFragment({ x: x, y: y, fragment: fragment });
|
||||||
|
for (const aFrag of this.fragments) {
|
||||||
|
if (aFrag.collide(newFrag)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
place(x: number, y: number, fragment: Fragment): boolean {
|
||||||
|
if (!this.canPlace(x, y, fragment)) return false;
|
||||||
|
this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentAt(worldX: number, worldY: number): ActiveFragment | null {
|
||||||
|
for (const aFrag of this.fragments) {
|
||||||
|
if (aFrag.fullAt(worldX, worldY)) {
|
||||||
|
return aFrag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
count(fragment: Fragment): number {
|
||||||
|
let amt = 0;
|
||||||
|
for (const aFrag of this.fragments) {
|
||||||
|
if (aFrag.fragment().id === fragment.id) amt++;
|
||||||
|
}
|
||||||
|
return amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAt(worldX: number, worldY: number): boolean {
|
||||||
|
for (let i = 0; i < this.fragments.length; i++) {
|
||||||
|
if (this.fragments[i].fullAt(worldX, worldY)) {
|
||||||
|
this.fragments.splice(i, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.fragments = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMults(p: IPlayer): void {
|
||||||
|
p.reapplyAllAugmentations(true);
|
||||||
|
p.reapplyAllSourceFiles();
|
||||||
|
|
||||||
|
for (const aFrag of this.fragments) {
|
||||||
|
const fragment = aFrag.fragment();
|
||||||
|
const power = CalculateEffect(aFrag.charge, fragment.power);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
6
src/CotMG/formulas/charge.ts
Normal file
6
src/CotMG/formulas/charge.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export function CalculateCharge(ram: number, currentHeat: number, boost: number, cool: number): number[] {
|
||||||
|
const heatPenalty = Math.log(1+currentHeat)/Math.log(2);
|
||||||
|
const extraCharge = ram*Math.pow(boost, 2)/(heatPenalty*cool);
|
||||||
|
const extraHeat = ram;
|
||||||
|
return [extraCharge, extraHeat];
|
||||||
|
}
|
3
src/CotMG/formulas/effect.ts
Normal file
3
src/CotMG/formulas/effect.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function CalculateEffect(charge: number, power: number): number {
|
||||||
|
return Math.pow((power/1000)+1, Math.log(charge+1)/Math.log(8))
|
||||||
|
}
|
18
src/CotMG/ui/Cell.tsx
Normal file
18
src/CotMG/ui/Cell.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
onMouseEnter?: () => void;
|
||||||
|
onClick?: () => void;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Cell(cellProps: IProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="staneksgift_cell"
|
||||||
|
style={{ backgroundColor: cellProps.color }}
|
||||||
|
onMouseEnter={cellProps.onMouseEnter}
|
||||||
|
onClick={cellProps.onClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
57
src/CotMG/ui/FragmentInspector.tsx
Normal file
57
src/CotMG/ui/FragmentInspector.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, {useState, useEffect} from "react";
|
||||||
|
import { ActiveFragment } from "../ActiveFragment";
|
||||||
|
import { FragmentType } from "../FragmentType";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { MuiPaper } from "../../ui/React/MuiPaper";
|
||||||
|
import { CalculateEffect } from "../formulas/effect";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
fragment: ActiveFragment | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FragmentInspector(props: IProps) {
|
||||||
|
const [, setC] = useState(new Date())
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => setC(new Date()), 250);
|
||||||
|
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if(props.fragment === null) {
|
||||||
|
return (<MuiPaper variant="outlined">
|
||||||
|
<pre className="text">
|
||||||
|
ID: N/A<br />
|
||||||
|
Type: N/A<br />
|
||||||
|
Magnitude: N/A<br />
|
||||||
|
Charge: N/A<br />
|
||||||
|
Heat: N/A<br />
|
||||||
|
Effect: N/A<br />
|
||||||
|
[X, Y] N/A<br />
|
||||||
|
</pre>
|
||||||
|
</MuiPaper>)
|
||||||
|
}
|
||||||
|
const f = props.fragment.fragment();
|
||||||
|
|
||||||
|
let charge = numeralWrapper.formatStaneksGiftCharge(props.fragment.charge);
|
||||||
|
let heat = numeralWrapper.formatStaneksGiftHeat(props.fragment.heat);
|
||||||
|
// Boosters and cooling don't deal with heat.
|
||||||
|
if(f.type === FragmentType.Booster ||
|
||||||
|
f.type === FragmentType.Cooling) {
|
||||||
|
charge = "N/A";
|
||||||
|
heat = "N/A";
|
||||||
|
}
|
||||||
|
const effect = numeralWrapper.format((CalculateEffect(props.fragment.charge, f.power)-1), "+0.00%");
|
||||||
|
|
||||||
|
return (<MuiPaper variant="outlined">
|
||||||
|
<pre className="text">
|
||||||
|
ID: {props.fragment.id}<br />
|
||||||
|
Type: {FragmentType[f.type]}<br />
|
||||||
|
Power: {numeralWrapper.formatStaneksGiftPower(f.power)}<br />
|
||||||
|
Charge: {charge}<br />
|
||||||
|
Heat: {heat}<br />
|
||||||
|
Effect: {effect}<br />
|
||||||
|
[X, Y] {props.fragment.x}, {props.fragment.y}<br />
|
||||||
|
</pre>
|
||||||
|
</MuiPaper>)
|
||||||
|
}
|
67
src/CotMG/ui/FragmentSelector.tsx
Normal file
67
src/CotMG/ui/FragmentSelector.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
Fragments,
|
||||||
|
Fragment,
|
||||||
|
NoneFragment,
|
||||||
|
DeleteFragment,
|
||||||
|
} from "../Fragment";
|
||||||
|
import { FragmentType } from "../FragmentType";
|
||||||
|
import { IStaneksGift } from "../IStaneksGift";
|
||||||
|
import { G } from "./G";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
import List from '@material-ui/core/List';
|
||||||
|
import ListItem, { ListItemProps } from '@material-ui/core/ListItem';
|
||||||
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
|
import Divider from '@material-ui/core/Divider';
|
||||||
|
|
||||||
|
|
||||||
|
type IOptionProps = {
|
||||||
|
gift: IStaneksGift;
|
||||||
|
fragment: Fragment;
|
||||||
|
selectFragment: (fragment: Fragment) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FragmentOption(props: IOptionProps) {
|
||||||
|
const remaining = props.fragment.limit !== Infinity ? (<>{props.fragment.limit - props.gift.count(props.fragment)} remaining</>) : (<></>);
|
||||||
|
return (<>
|
||||||
|
<ListItem button onClick={()=>props.selectFragment(props.fragment)} style={{backgroundColor: '#222'}}>
|
||||||
|
<p style={{marginBottom: '1em'}}>
|
||||||
|
{FragmentType[props.fragment.type]}<br />
|
||||||
|
power: {numeralWrapper.formatStaneksGiftPower(props.fragment.power)}<br />
|
||||||
|
{remaining}
|
||||||
|
</p><br />
|
||||||
|
<G width={props.fragment.width()}
|
||||||
|
height={props.fragment.height()}
|
||||||
|
colorAt={(x, y) => props.fragment.fullAt(x, y) ? "green" : ""} />
|
||||||
|
</ListItem>
|
||||||
|
<Divider />
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
gift: IStaneksGift;
|
||||||
|
selectFragment: (fragment: Fragment) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FragmentSelector(props: IProps) {
|
||||||
|
return (<List style={{maxHeight: '250px', overflow: 'auto'}}>
|
||||||
|
<ListItem button onClick={()=>props.selectFragment(NoneFragment)} style={{backgroundColor: '#222'}}>
|
||||||
|
<p style={{marginBottom: '1em'}}>
|
||||||
|
None
|
||||||
|
</p>
|
||||||
|
</ListItem><Divider />
|
||||||
|
<ListItem button onClick={()=>props.selectFragment(DeleteFragment)} style={{backgroundColor: '#222'}}>
|
||||||
|
<p style={{marginBottom: '1em'}}>
|
||||||
|
Delete
|
||||||
|
</p>
|
||||||
|
</ListItem><Divider />
|
||||||
|
{Fragments.map(fragment => <FragmentOption
|
||||||
|
key={fragment.id}
|
||||||
|
gift={props.gift}
|
||||||
|
selectFragment={props.selectFragment}
|
||||||
|
fragment={fragment}
|
||||||
|
/>)}
|
||||||
|
</List>)
|
||||||
|
}
|
||||||
|
|
26
src/CotMG/ui/G.tsx
Normal file
26
src/CotMG/ui/G.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Cell } from "./Cell";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
colorAt: (x: number, y: number) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function G(props: IProps) {
|
||||||
|
// 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(
|
||||||
|
<div key={j} className="staneksgift_row">
|
||||||
|
{cells}
|
||||||
|
</div>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div style={{ float: "left" }}>{elems}</div>;
|
||||||
|
}
|
141
src/CotMG/ui/Grid.tsx
Normal file
141
src/CotMG/ui/Grid.tsx
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Fragment, Fragments, NoneFragment } from "../Fragment";
|
||||||
|
import { ActiveFragment } from "../ActiveFragment";
|
||||||
|
import { FragmentType } from "../FragmentType";
|
||||||
|
import { IStaneksGift } from "../IStaneksGift";
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
import { Cell } from "./Cell";
|
||||||
|
import { FragmentInspector } from "./FragmentInspector";
|
||||||
|
import { FragmentSelector } from "./FragmentSelector";
|
||||||
|
|
||||||
|
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})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GridProps = {
|
||||||
|
gift: IStaneksGift;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Grid(props: GridProps): 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 === null) 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 [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
|
||||||
|
|
||||||
|
function moveGhost(worldX: number, worldY: number): void {
|
||||||
|
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||||
|
for (let i = 0; i < selectedFragment.shape.length; i++) {
|
||||||
|
for (let j = 0; j < selectedFragment.shape[i].length; j++) {
|
||||||
|
if (worldX + i > newgrid.length - 1) continue;
|
||||||
|
if (worldY + j > newgrid[worldX + i].length - 1) continue;
|
||||||
|
if (!selectedFragment.shape[i][j]) continue;
|
||||||
|
if (worldX + j > newgrid.length - 1) continue;
|
||||||
|
if (worldY + i > newgrid[worldX + j].length - 1) continue;
|
||||||
|
newgrid[worldX + j][worldY + i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setGhostGrid(newgrid);
|
||||||
|
setPos([worldX, worldY]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAt(worldX: number, worldY: number): boolean {
|
||||||
|
return props.gift.deleteAt(worldX, worldY);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, selectedFragment)) return;
|
||||||
|
props.gift.place(worldX, worldY, 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 === null) 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)} onClick={() => clickAt(i, j)} color={color(i, j)} />,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elems.push(
|
||||||
|
<div key={j} className="staneksgift_row">
|
||||||
|
{cells}
|
||||||
|
</div>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectedFragment(fragment: Fragment): void {
|
||||||
|
setSelectedFragment(fragment);
|
||||||
|
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||||
|
setGhostGrid(newgrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
||||||
|
<StdButton onClick={clear} text="Clear" />
|
||||||
|
<div style={{ float: "left" }}>{elems}</div>
|
||||||
|
<div>
|
||||||
|
<FragmentInspector fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
16
src/CotMG/ui/StaneksGiftRoot.tsx
Normal file
16
src/CotMG/ui/StaneksGiftRoot.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Grid } from "./Grid";
|
||||||
|
import { IStaneksGift } from "../IStaneksGift";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
staneksGift: IStaneksGift;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1>Stanek's Gift</h1>
|
||||||
|
<Grid gift={staneksGift} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -468,4 +468,46 @@ export const FactionInfos: IMap<FactionInfo> = {
|
|||||||
false,
|
false,
|
||||||
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,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
@ -84,8 +84,8 @@ Cities[CityName.Chongqing].asciiArt = `
|
|||||||
[world stock exchange] F |
|
[world stock exchange] F |
|
||||||
\\ o 78 [kuaigong international]
|
\\ o 78 [kuaigong international]
|
||||||
\\ /
|
\\ /
|
||||||
38 o----x--x------x------A---------
|
38 o----x--x------x------A------G--
|
||||||
/ 39 | 41
|
/ 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]
|
||||||
|
@ -29,6 +29,7 @@ export enum LocationName {
|
|||||||
// Chongqing locations
|
// Chongqing locations
|
||||||
ChongqingKuaiGongInternational = "KuaiGong International",
|
ChongqingKuaiGongInternational = "KuaiGong International",
|
||||||
ChongqingSolarisSpaceSystems = "Solaris Space Systems",
|
ChongqingSolarisSpaceSystems = "Solaris Space Systems",
|
||||||
|
ChongqingChurchOfTheMachineGod = "Church of the Machine God",
|
||||||
|
|
||||||
// Sector 12
|
// Sector 12
|
||||||
Sector12AlphaEnterprises = "Alpha Enterprises",
|
Sector12AlphaEnterprises = "Alpha Enterprises",
|
||||||
|
@ -440,4 +440,9 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
name: LocationName.WorldStockExchange,
|
name: LocationName.WorldStockExchange,
|
||||||
types: [LocationType.StockMarket],
|
types: [LocationType.StockMarket],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
city: CityName.Chongqing,
|
||||||
|
name: LocationName.ChongqingChurchOfTheMachineGod,
|
||||||
|
types: [LocationType.Special],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
@ -18,10 +18,14 @@ import { Location } from "../Location";
|
|||||||
import { CreateCorporationPopup } from "../../Corporation/ui/CreateCorporationPopup";
|
import { CreateCorporationPopup } from "../../Corporation/ui/CreateCorporationPopup";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
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";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { RadioButtonUncheckedRounded } from "@mui/icons-material";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -117,6 +121,109 @@ 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)
|
||||||
|
) {
|
||||||
|
player.queueAugmentation(AugmentationNames.StaneksGift1);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.toFaction(faction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCotMG(): React.ReactElement {
|
||||||
|
// prettier-ignore
|
||||||
|
const symbol = <pre>
|
||||||
|
{" `` "}<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 />
|
||||||
|
{" `..` "}</pre>
|
||||||
|
if (player.factions.includes("Church of the Machine God")) {
|
||||||
|
return (
|
||||||
|
<div style={{ width: "60%" }}>
|
||||||
|
<p>
|
||||||
|
<i className="text">Allison "Mother" Stanek: Welcome back my child!</i>
|
||||||
|
</p>
|
||||||
|
{symbol}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.canAccessCotMG()) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
<i className="text">
|
||||||
|
A decrepit altar stands in the middle of a dilapidated church.
|
||||||
|
<br />
|
||||||
|
<br />A symbol is carved in the altar.
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
{symbol}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0 ||
|
||||||
|
player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div style={{ width: "60%" }}>
|
||||||
|
<p>
|
||||||
|
<i className="text">
|
||||||
|
Allison "Mother" Stanek: Begone you filth! My gift must be the first modification that your body should
|
||||||
|
have!
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: "60%" }}>
|
||||||
|
<p>
|
||||||
|
<i className="text">
|
||||||
|
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
|
||||||
|
form? If you are, accept my gift.
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
|
||||||
|
{symbol}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
switch (props.loc.name) {
|
switch (props.loc.name) {
|
||||||
case LocationName.NewTokyoVitaLife: {
|
case LocationName.NewTokyoVitaLife: {
|
||||||
return renderResleeving();
|
return renderResleeving();
|
||||||
@ -130,6 +237,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
case LocationName.NewTokyoNoodleBar: {
|
case LocationName.NewTokyoNoodleBar: {
|
||||||
return renderNoodleBar();
|
return renderNoodleBar();
|
||||||
}
|
}
|
||||||
|
case LocationName.ChongqingChurchOfTheMachineGod: {
|
||||||
|
return renderCotMG();
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
||||||
return <></>;
|
return <></>;
|
||||||
|
@ -54,6 +54,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> = {
|
||||||
@ -313,6 +322,17 @@ export const RamCosts: IMap<any> = {
|
|||||||
purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
stanek: {
|
||||||
|
charge: () => RamCostConstants.ScriptStanekCharge,
|
||||||
|
fragmentDefinitions: () => RamCostConstants.ScriptStanekFragmentDefinitions,
|
||||||
|
placedFragments: () => RamCostConstants.ScriptStanekPlacedFragments,
|
||||||
|
clear: () => RamCostConstants.ScriptStanekClear,
|
||||||
|
canPlace: () => RamCostConstants.ScriptStanekCanPlace,
|
||||||
|
place: () => RamCostConstants.ScriptStanekPlace,
|
||||||
|
fragmentAt: () => RamCostConstants.ScriptStanekFragmentAt,
|
||||||
|
deleteAt: () => RamCostConstants.ScriptStanekDeleteAt,
|
||||||
|
},
|
||||||
|
|
||||||
heart: {
|
heart: {
|
||||||
// Easter egg function
|
// Easter egg function
|
||||||
break: () => 0,
|
break: () => 0,
|
||||||
|
@ -171,6 +171,9 @@ import { GangMemberTask } from "./Gang/GangMemberTask";
|
|||||||
import { Stock } from "./StockMarket/Stock";
|
import { Stock } from "./StockMarket/Stock";
|
||||||
import { BaseServer } from "./Server/BaseServer";
|
import { BaseServer } from "./Server/BaseServer";
|
||||||
|
|
||||||
|
import { staneksGift } from "./CotMG/Helper";
|
||||||
|
import { Fragments, FragmentById } from "./CotMG/Fragment";
|
||||||
|
|
||||||
const defaultInterpreter = new Interpreter("", () => undefined);
|
const defaultInterpreter = new Interpreter("", () => undefined);
|
||||||
|
|
||||||
// the acorn interpreter has a bug where it doesn't convert arrays correctly.
|
// the acorn interpreter has a bug where it doesn't convert arrays correctly.
|
||||||
@ -375,7 +378,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Utility function to get Hacknet Node object
|
// Utility function to get Hacknet Node object
|
||||||
const getHacknetNode = function (i: any, callingFn: string = ""): HacknetNode | HacknetServer {
|
const getHacknetNode = function (i: any, callingFn = ""): HacknetNode | HacknetServer {
|
||||||
if (isNaN(i)) {
|
if (isNaN(i)) {
|
||||||
throw makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
|
throw makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
|
||||||
}
|
}
|
||||||
@ -545,6 +548,15 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
return contract;
|
return contract;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkStanekAPIAccess = function (func: string): void {
|
||||||
|
if (Player.bitNodeN !== 13 && !SourceFileFlags[13]) {
|
||||||
|
throw makeRuntimeErrorMsg(
|
||||||
|
`stanek.${func}`,
|
||||||
|
"You do not currently have access to the Stanek API. This is either because you are not in BitNode-13 or because you do not have Source-File 13",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const checkGangApiAccess = function (func: any): void {
|
const checkGangApiAccess = function (func: any): void {
|
||||||
const gang = Player.gang;
|
const gang = Player.gang;
|
||||||
if (gang === null) throw new Error("Must have joined gang");
|
if (gang === null) throw new Error("Must have joined gang");
|
||||||
@ -5199,6 +5211,66 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
|
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
|
||||||
},
|
},
|
||||||
}, // End sleeve
|
}, // End sleeve
|
||||||
|
|
||||||
|
// Stanek's gift API
|
||||||
|
stanek: {
|
||||||
|
charge: function (worldX: any, worldY: any): any {
|
||||||
|
updateDynamicRam("charge", getRamCost("stanek", "charge"));
|
||||||
|
//checkStanekAPIAccess("charge");
|
||||||
|
const fragment = staneksGift.fragmentAt(worldX, worldY);
|
||||||
|
if (!fragment) throw makeRuntimeErrorMsg("stanek.charge", `No fragment at (${worldX}, ${worldY})`);
|
||||||
|
return netscriptDelay(1000, workerScript).then(function () {
|
||||||
|
if (workerScript.env.stopFlag) {
|
||||||
|
return Promise.reject(workerScript);
|
||||||
|
}
|
||||||
|
const ram = workerScript.scriptRef.ramUsage * workerScript.scriptRef.threads;
|
||||||
|
return Promise.resolve(staneksGift.charge(worldX, worldY, ram));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fragmentDefinitions: function () {
|
||||||
|
updateDynamicRam("fragmentDefinitions", getRamCost("stanek", "fragmentDefinitions"));
|
||||||
|
//checkStanekAPIAccess("fragmentDefinitions");
|
||||||
|
return Fragments.map((f) => f.copy());
|
||||||
|
},
|
||||||
|
placedFragments: function () {
|
||||||
|
updateDynamicRam("placedFragments", getRamCost("stanek", "placedFragments"));
|
||||||
|
//checkStanekAPIAccess("placedFragments");
|
||||||
|
return staneksGift.fragments.map((af) => {
|
||||||
|
return { ...af.copy(), ...af.fragment().copy() };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clear: function () {
|
||||||
|
updateDynamicRam("clear", getRamCost("stanek", "clear"));
|
||||||
|
//checkStanekAPIAccess("clear");
|
||||||
|
staneksGift.clear();
|
||||||
|
},
|
||||||
|
canPlace: function (worldX: any, worldY: any, fragmentId: any): any {
|
||||||
|
updateDynamicRam("canPlace", getRamCost("stanek", "canPlace"));
|
||||||
|
//checkStanekAPIAccess("canPlace");
|
||||||
|
const fragment = FragmentById(fragmentId);
|
||||||
|
if (!fragment) throw makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${fragmentId}`);
|
||||||
|
return staneksGift.canPlace(worldX, worldY, fragment);
|
||||||
|
},
|
||||||
|
place: function (worldX: any, worldY: any, fragmentId: any): any {
|
||||||
|
updateDynamicRam("place", getRamCost("stanek", "place"));
|
||||||
|
//checkStanekAPIAccess("place");
|
||||||
|
const fragment = FragmentById(fragmentId);
|
||||||
|
if (!fragment) throw makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
|
||||||
|
return staneksGift.place(worldX, worldY, fragment);
|
||||||
|
},
|
||||||
|
fragmentAt: function (worldX: any, worldY: any): any {
|
||||||
|
updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));
|
||||||
|
//checkStanekAPIAccess("fragmentAt");
|
||||||
|
const fragment = staneksGift.fragmentAt(worldX, worldY);
|
||||||
|
if (fragment !== null) return fragment.copy();
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
deleteAt: function (worldX: any, worldY: any): any {
|
||||||
|
updateDynamicRam("deleteAt", getRamCost("stanek", "deleteAt"));
|
||||||
|
//checkStanekAPIAccess("deleteAt");
|
||||||
|
return staneksGift.deleteAt(worldX, worldY);
|
||||||
|
},
|
||||||
|
}, // End stanek
|
||||||
formulas: {
|
formulas: {
|
||||||
basic: {
|
basic: {
|
||||||
calculateSkill: function (exp: any, mult: any = 1): any {
|
calculateSkill: function (exp: any, mult: any = 1): any {
|
||||||
|
@ -114,6 +114,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.
|
||||||
|
@ -192,7 +192,7 @@ export interface IPlayer {
|
|||||||
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
|
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
|
||||||
getUpgradeHomeRamCost(): number;
|
getUpgradeHomeRamCost(): number;
|
||||||
gotoLocation(to: LocationName): boolean;
|
gotoLocation(to: LocationName): boolean;
|
||||||
hasAugmentation(aug: Augmentation): boolean;
|
hasAugmentation(aug: string | Augmentation): boolean;
|
||||||
hasCorporation(): boolean;
|
hasCorporation(): boolean;
|
||||||
hasGangWith(facName: string): boolean;
|
hasGangWith(facName: string): boolean;
|
||||||
hasTorRouter(): boolean;
|
hasTorRouter(): boolean;
|
||||||
@ -274,4 +274,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;
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ export class PlayerObject implements IPlayer {
|
|||||||
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
|
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
|
||||||
getUpgradeHomeRamCost: () => number;
|
getUpgradeHomeRamCost: () => number;
|
||||||
gotoLocation: (to: LocationName) => boolean;
|
gotoLocation: (to: LocationName) => boolean;
|
||||||
hasAugmentation: (aug: Augmentation) => boolean;
|
hasAugmentation: (aug: string | Augmentation) => boolean;
|
||||||
hasCorporation: () => boolean;
|
hasCorporation: () => boolean;
|
||||||
hasGangWith: (facName: string) => boolean;
|
hasGangWith: (facName: string) => boolean;
|
||||||
hasTorRouter: () => boolean;
|
hasTorRouter: () => boolean;
|
||||||
@ -281,6 +281,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;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
//Skills and stats
|
//Skills and stats
|
||||||
@ -575,6 +576,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2632,3 +2632,7 @@ export function setMult(this: IPlayer, name: string, mult: number): void {
|
|||||||
if (!this.hasOwnProperty(name)) return;
|
if (!this.hasOwnProperty(name)) return;
|
||||||
(this as any)[name] = mult;
|
(this as any)[name] = mult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canAccessCotMG(this: IPlayer): boolean {
|
||||||
|
return this.bitNodeN === 13 || SourceFileFlags[13] > 0;
|
||||||
|
}
|
||||||
|
@ -143,6 +143,12 @@ function prestigeAugmentation(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (augmentationExists(AugmentationNames.StaneksGift) && Augmentations[AugmentationNames.StaneksGift].owned) {
|
||||||
|
// TODO(hydroflame): refactor faction names so we don't have to hard
|
||||||
|
// code strings.
|
||||||
|
joinFaction(Factions["Church of the Machine God"]);
|
||||||
|
}
|
||||||
|
|
||||||
resetPidCounter();
|
resetPidCounter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import { Settings } from "./Settings/Settings";
|
|||||||
import { loadSpecialServerIps, SpecialServerIps } from "./Server/SpecialServerIps";
|
import { loadSpecialServerIps, SpecialServerIps } from "./Server/SpecialServerIps";
|
||||||
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 { GameSavedEvents } from "./ui/React/Snackbar";
|
import { GameSavedEvents } 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);
|
||||||
@ -67,6 +69,7 @@ class BitburnerSaveObject {
|
|||||||
this.SettingsSave = JSON.stringify(Settings);
|
this.SettingsSave = JSON.stringify(Settings);
|
||||||
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -173,6 +176,12 @@ function loadGame(saveString: string): boolean {
|
|||||||
loadFactions(saveObj.FactionsSave);
|
loadFactions(saveObj.FactionsSave);
|
||||||
loadSpecialServerIps(saveObj.SpecialServerIpsSave);
|
loadSpecialServerIps(saveObj.SpecialServerIpsSave);
|
||||||
|
|
||||||
|
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);
|
||||||
|
@ -197,6 +197,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) {
|
||||||
|
@ -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 { inMission } from "../../Missions";
|
import { inMission } from "../../Missions";
|
||||||
import { KEY } from "../../utils/helpers/keyCodes";
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
@ -156,13 +157,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.hasAugmentation(AugmentationNames.StaneksGift1);
|
||||||
|
|
||||||
function clickTerminal(): void {
|
function clickTerminal(): void {
|
||||||
props.router.toTerminal();
|
props.router.toTerminal();
|
||||||
@ -187,6 +187,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();
|
||||||
}
|
}
|
||||||
@ -405,6 +409,25 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</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"}>
|
||||||
|
Staneks Gift
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)} */}
|
||||||
{canCreateProgram && (
|
{canCreateProgram && (
|
||||||
<ListItem
|
<ListItem
|
||||||
button
|
button
|
||||||
@ -426,6 +449,23 @@ 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"}>StaneksGift</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
|
||||||
@ -136,6 +137,9 @@ const Engine: {
|
|||||||
currMission.process(numCycles);
|
currMission.process(numCycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
@ -30,3 +30,4 @@ import "../css/dev-menu.css";
|
|||||||
import "../css/casino.scss";
|
import "../css/casino.scss";
|
||||||
import "../css/milestones.scss";
|
import "../css/milestones.scss";
|
||||||
import "../css/infiltration.scss";
|
import "../css/infiltration.scss";
|
||||||
|
import "../css/staneksgift.scss";
|
||||||
|
@ -62,6 +62,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";
|
||||||
@ -178,6 +180,9 @@ export let Router: IRouter = {
|
|||||||
toHackingMission: () => {
|
toHackingMission: () => {
|
||||||
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 {
|
||||||
@ -271,6 +276,9 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
setPage(Page.HackingMission);
|
setPage(Page.HackingMission);
|
||||||
setFaction(faction);
|
setFaction(faction);
|
||||||
},
|
},
|
||||||
|
toStaneksGift: () => {
|
||||||
|
setPage(Page.StaneksGift);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -309,6 +317,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
<SleeveRoot player={player} />
|
<SleeveRoot player={player} />
|
||||||
) : page === Page.Stats ? (
|
) : page === Page.Stats ? (
|
||||||
<CharacterStats />
|
<CharacterStats />
|
||||||
|
) : page === Page.StaneksGift ? (
|
||||||
|
<StaneksGiftRoot staneksGift={staneksGift} />
|
||||||
) : page === Page.CreateScript ? (
|
) : page === Page.CreateScript ? (
|
||||||
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
|
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
|
||||||
) : page === Page.ActiveScripts ? (
|
) : page === Page.ActiveScripts ? (
|
||||||
|
@ -35,6 +35,7 @@ export enum Page {
|
|||||||
Location,
|
Location,
|
||||||
HackingMission,
|
HackingMission,
|
||||||
Loading,
|
Loading,
|
||||||
|
StaneksGift,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,4 +76,5 @@ export interface IRouter {
|
|||||||
toBladeburnerCinematic(): void;
|
toBladeburnerCinematic(): void;
|
||||||
toLocation(location: Location): void;
|
toLocation(location: Location): void;
|
||||||
toHackingMission(faction: Faction): void;
|
toHackingMission(faction: Faction): void;
|
||||||
|
toStaneksGift(): void;
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,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");
|
||||||
|
}
|
||||||
|
|
||||||
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