mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 21:53:50 +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>
|
||||
Coding Contract API <netscript/netscriptcodingcontractapi>
|
||||
Sleeve API <netscript/netscriptsleeveapi>
|
||||
Stanek API <netscript/netscriptstanekapi>
|
||||
Formulas API <netscript/netscriptformulasapi>
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
mult = Math.pow(
|
||||
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",
|
||||
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
|
||||
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
||||
//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
|
||||
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
|
||||
@ -784,6 +824,45 @@ export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
BitNodeMultipliers.BladeburnerSkillCost = inc;
|
||||
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:
|
||||
console.warn("Player.bitNodeN invalid");
|
||||
break;
|
||||
|
@ -126,7 +126,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
|
||||
<pre>| | | |O / | | O / | O O | | \ O| | | |</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> | <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,
|
||||
),
|
||||
|
||||
// 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 |
|
||||
\\ o 78 [kuaigong international]
|
||||
\\ /
|
||||
38 o----x--x------x------A---------
|
||||
/ 39 | 41
|
||||
38 o----x--x------x------A------G--
|
||||
/ 39 | 41 [church]
|
||||
37 o + 79 o--x--x-C-0
|
||||
/ | /
|
||||
/ x-----+-----x-----0 [hospital]
|
||||
|
@ -29,6 +29,7 @@ export enum LocationName {
|
||||
// Chongqing locations
|
||||
ChongqingKuaiGongInternational = "KuaiGong International",
|
||||
ChongqingSolarisSpaceSystems = "Solaris Space Systems",
|
||||
ChongqingChurchOfTheMachineGod = "Church of the Machine God",
|
||||
|
||||
// Sector 12
|
||||
Sector12AlphaEnterprises = "Alpha Enterprises",
|
||||
|
@ -440,4 +440,9 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
name: LocationName.WorldStockExchange,
|
||||
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 { createPopup } from "../../ui/React/createPopup";
|
||||
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 { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { RadioButtonUncheckedRounded } from "@mui/icons-material";
|
||||
|
||||
type IProps = {
|
||||
loc: Location;
|
||||
@ -117,6 +121,109 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
return <Button onClick={handleResleeving}>Re-Sleeve</Button>;
|
||||
}
|
||||
|
||||
function handleCotMG(): void {
|
||||
const faction = Factions["Church of the Machine God"];
|
||||
if (!player.factions.includes("Church of the Machine God")) {
|
||||
joinFaction(faction);
|
||||
}
|
||||
if (
|
||||
!player.augmentations.some((a) => a.name === AugmentationNames.StaneksGift1) &&
|
||||
!player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
|
||||
) {
|
||||
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) {
|
||||
case LocationName.NewTokyoVitaLife: {
|
||||
return renderResleeving();
|
||||
@ -130,6 +237,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
case LocationName.NewTokyoNoodleBar: {
|
||||
return renderNoodleBar();
|
||||
}
|
||||
case LocationName.ChongqingChurchOfTheMachineGod: {
|
||||
return renderCotMG();
|
||||
}
|
||||
default:
|
||||
console.error(`Location ${props.loc.name} doesn't have any special properties`);
|
||||
return <></>;
|
||||
|
@ -54,6 +54,15 @@ export const RamCostConstants: IMap<number> = {
|
||||
ScriptGangApiBaseRamCost: 4,
|
||||
|
||||
ScriptBladeburnerApiBaseRamCost: 4,
|
||||
|
||||
ScriptStanekCharge: 0.4,
|
||||
ScriptStanekFragmentDefinitions: 0,
|
||||
ScriptStanekPlacedFragments: 5,
|
||||
ScriptStanekClear: 0,
|
||||
ScriptStanekCanPlace: 0.5,
|
||||
ScriptStanekPlace: 5,
|
||||
ScriptStanekFragmentAt: 2,
|
||||
ScriptStanekDeleteAt: 0.15,
|
||||
};
|
||||
|
||||
export const RamCosts: IMap<any> = {
|
||||
@ -313,6 +322,17 @@ export const RamCosts: IMap<any> = {
|
||||
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: {
|
||||
// Easter egg function
|
||||
break: () => 0,
|
||||
|
@ -171,6 +171,9 @@ import { GangMemberTask } from "./Gang/GangMemberTask";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { BaseServer } from "./Server/BaseServer";
|
||||
|
||||
import { staneksGift } from "./CotMG/Helper";
|
||||
import { Fragments, FragmentById } from "./CotMG/Fragment";
|
||||
|
||||
const defaultInterpreter = new Interpreter("", () => undefined);
|
||||
|
||||
// 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
|
||||
const getHacknetNode = function (i: any, callingFn: string = ""): HacknetNode | HacknetServer {
|
||||
const getHacknetNode = function (i: any, callingFn = ""): HacknetNode | HacknetServer {
|
||||
if (isNaN(i)) {
|
||||
throw makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
|
||||
}
|
||||
@ -545,6 +548,15 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
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 gang = Player.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);
|
||||
},
|
||||
}, // 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: {
|
||||
basic: {
|
||||
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;
|
||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
||||
}
|
||||
workerScript.env.vars.stanek.charge = wrap("stanek.prop", workerScript.env.vars.stanek.charge);
|
||||
|
||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||
// to that script, which env.vars does at this point.
|
||||
|
@ -192,7 +192,7 @@ export interface IPlayer {
|
||||
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
|
||||
getUpgradeHomeRamCost(): number;
|
||||
gotoLocation(to: LocationName): boolean;
|
||||
hasAugmentation(aug: Augmentation): boolean;
|
||||
hasAugmentation(aug: string | Augmentation): boolean;
|
||||
hasCorporation(): boolean;
|
||||
hasGangWith(facName: string): boolean;
|
||||
hasTorRouter(): boolean;
|
||||
@ -274,4 +274,6 @@ export interface IPlayer {
|
||||
setBitNodeNumber(n: number): void;
|
||||
getMult(name: string): number;
|
||||
setMult(name: string, mult: number): void;
|
||||
|
||||
canAccessCotMG(): boolean;
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ export class PlayerObject implements IPlayer {
|
||||
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
|
||||
getUpgradeHomeRamCost: () => number;
|
||||
gotoLocation: (to: LocationName) => boolean;
|
||||
hasAugmentation: (aug: Augmentation) => boolean;
|
||||
hasAugmentation: (aug: string | Augmentation) => boolean;
|
||||
hasCorporation: () => boolean;
|
||||
hasGangWith: (facName: string) => boolean;
|
||||
hasTorRouter: () => boolean;
|
||||
@ -281,6 +281,7 @@ export class PlayerObject implements IPlayer {
|
||||
setBitNodeNumber: (n: number) => void;
|
||||
getMult: (name: string) => number;
|
||||
setMult: (name: string, mult: number) => void;
|
||||
canAccessCotMG: () => boolean;
|
||||
|
||||
constructor() {
|
||||
//Skills and stats
|
||||
@ -575,6 +576,8 @@ export class PlayerObject implements IPlayer {
|
||||
|
||||
this.getMult = generalMethods.getMult;
|
||||
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;
|
||||
(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();
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { Settings } from "./Settings/Settings";
|
||||
import { loadSpecialServerIps, SpecialServerIps } from "./Server/SpecialServerIps";
|
||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
|
||||
import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
|
||||
|
||||
import { GameSavedEvents } from "./ui/React/Snackbar";
|
||||
|
||||
@ -38,6 +39,7 @@ class BitburnerSaveObject {
|
||||
VersionSave = "";
|
||||
AllGangsSave = "";
|
||||
LastExportBonus = "";
|
||||
StaneksGiftSave = "";
|
||||
|
||||
getSaveString(): string {
|
||||
this.PlayerSave = JSON.stringify(Player);
|
||||
@ -67,6 +69,7 @@ class BitburnerSaveObject {
|
||||
this.SettingsSave = JSON.stringify(Settings);
|
||||
this.VersionSave = JSON.stringify(CONSTANTS.Version);
|
||||
this.LastExportBonus = JSON.stringify(ExportBonus.LastExportBonus);
|
||||
this.StaneksGiftSave = JSON.stringify(staneksGift);
|
||||
if (Player.inGang()) {
|
||||
this.AllGangsSave = JSON.stringify(AllGangs);
|
||||
}
|
||||
@ -173,6 +176,12 @@ function loadGame(saveString: string): boolean {
|
||||
loadFactions(saveObj.FactionsSave);
|
||||
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")) {
|
||||
try {
|
||||
loadAliases(saveObj.AliasesSave);
|
||||
|
@ -197,6 +197,8 @@ async function parseOnlyRamCalculate(
|
||||
func = workerScript.env.vars.bladeburner[ref];
|
||||
} else if (ref in workerScript.env.vars.codingcontract) {
|
||||
func = workerScript.env.vars.codingcontract[ref];
|
||||
} else if (ref in workerScript.env.vars.stanek) {
|
||||
func = workerScript.env.vars.stanek[ref];
|
||||
} else if (ref in workerScript.env.vars.gang) {
|
||||
func = workerScript.env.vars.gang[ref];
|
||||
} else if (ref in workerScript.env.vars.sleeve) {
|
||||
|
@ -49,6 +49,7 @@ import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveT
|
||||
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { redPillFlag } from "../../RedPill";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { inMission } from "../../Missions";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
@ -156,13 +157,12 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
|
||||
const canOpenSleeves = props.player.sleeves.length > 0;
|
||||
|
||||
// TODO(hydroflame): these should not as any but right now the def is that it
|
||||
// can only be defined;
|
||||
const canCorporation = !!(props.player.corporation as any);
|
||||
const canGang = !!(props.player.gang as any);
|
||||
const canJob = props.player.companyName !== "";
|
||||
const canStockMarket = props.player.hasWseAccount;
|
||||
const canBladeburner = !!(props.player.bladeburner as any);
|
||||
const canStaneksGift = props.player.hasAugmentation(AugmentationNames.StaneksGift1);
|
||||
|
||||
function clickTerminal(): void {
|
||||
props.router.toTerminal();
|
||||
@ -187,6 +187,10 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
props.router.toCreateProgram();
|
||||
}
|
||||
|
||||
function clickStaneksGift(): void {
|
||||
props.router.toStaneksGift();
|
||||
}
|
||||
|
||||
function clickFactions(): void {
|
||||
props.router.toFactions();
|
||||
}
|
||||
@ -405,6 +409,25 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
{/* {canStaneksGift && (
|
||||
<ListItem
|
||||
button
|
||||
key={"Staneks Gift"}
|
||||
className={clsx({
|
||||
[classes.active]: props.page === Page.StaneksGift,
|
||||
})}
|
||||
onClick={clickStaneksGift}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DeveloperBoardIcon color={props.page !== Page.StaneksGift ? "secondary" : "primary"} />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography color={props.page !== Page.StaneksGift ? "secondary" : "primary"}>
|
||||
Staneks Gift
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)} */}
|
||||
{canCreateProgram && (
|
||||
<ListItem
|
||||
button
|
||||
@ -426,6 +449,23 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)}
|
||||
{canStaneksGift && (
|
||||
<ListItem
|
||||
button
|
||||
key={"Staneks Gift"}
|
||||
className={clsx({
|
||||
[classes.active]: props.page === Page.StaneksGift,
|
||||
})}
|
||||
onClick={clickStaneksGift}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<DeveloperBoardIcon color={props.page !== Page.StaneksGift ? "secondary" : "primary"} />
|
||||
</ListItemIcon>
|
||||
<ListItemText>
|
||||
<Typography color={props.page !== Page.StaneksGift ? "secondary" : "primary"}>StaneksGift</Typography>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Collapse>
|
||||
|
||||
|
@ -12,6 +12,7 @@ import { initCompanies } from "./Company/Companies";
|
||||
import { Corporation } from "./Corporation/Corporation";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Factions, initFactions } from "./Faction/Factions";
|
||||
import { staneksGift } from "./CotMG/Helper";
|
||||
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
|
||||
@ -136,6 +137,9 @@ const Engine: {
|
||||
currMission.process(numCycles);
|
||||
}
|
||||
|
||||
// Staneks gift
|
||||
staneksGift.process(Player, numCycles);
|
||||
|
||||
// Corporation
|
||||
if (Player.corporation instanceof Corporation) {
|
||||
// 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/milestones.scss";
|
||||
import "../css/infiltration.scss";
|
||||
import "../css/staneksgift.scss";
|
||||
|
@ -62,6 +62,8 @@ import { CharacterStats } from "./CharacterStats";
|
||||
import { TravelAgencyRoot } from "../Locations/ui/TravelAgencyRoot";
|
||||
import { StockMarketRoot } from "../StockMarket/ui/StockMarketRoot";
|
||||
import { BitverseRoot } from "../BitNode/ui/BitverseRoot";
|
||||
import { StaneksGiftRoot } from "../CotMG/ui/StaneksGiftRoot";
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { CharacterOverview } from "./React/CharacterOverview";
|
||||
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
|
||||
import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
@ -178,6 +180,9 @@ export let Router: IRouter = {
|
||||
toHackingMission: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
},
|
||||
};
|
||||
|
||||
function determineStartPage(player: IPlayer): Page {
|
||||
@ -271,6 +276,9 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
setPage(Page.HackingMission);
|
||||
setFaction(faction);
|
||||
},
|
||||
toStaneksGift: () => {
|
||||
setPage(Page.StaneksGift);
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -309,6 +317,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
<SleeveRoot player={player} />
|
||||
) : page === Page.Stats ? (
|
||||
<CharacterStats />
|
||||
) : page === Page.StaneksGift ? (
|
||||
<StaneksGiftRoot staneksGift={staneksGift} />
|
||||
) : page === Page.CreateScript ? (
|
||||
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
|
||||
) : page === Page.ActiveScripts ? (
|
||||
|
@ -35,6 +35,7 @@ export enum Page {
|
||||
Location,
|
||||
HackingMission,
|
||||
Loading,
|
||||
StaneksGift,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,4 +76,5 @@ export interface IRouter {
|
||||
toBladeburnerCinematic(): void;
|
||||
toLocation(location: Location): void;
|
||||
toHackingMission(faction: Faction): void;
|
||||
toStaneksGift(): void;
|
||||
}
|
||||
|
@ -165,6 +165,18 @@ class NumeralFormatter {
|
||||
return this.format(n, "0,0");
|
||||
}
|
||||
|
||||
formatStaneksGiftHeat(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatStaneksGiftCharge(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatStaneksGiftPower(n: number): string {
|
||||
return this.format(n, "0");
|
||||
}
|
||||
|
||||
parseMoney(s: string): number {
|
||||
// numeral library does not handle formats like 1e10 well (returns 110),
|
||||
// so if both return a valid number, return the biggest one
|
||||
|
Loading…
Reference in New Issue
Block a user