Compare commits

...

6 Commits

Author SHA1 Message Date
hydroflame
925e96345d v0.51.2 (#838)
* infiltration use buttons instead of a links

* minor accessibility patch

* Hospitalization will not cost more than 10% of the players money.

* Adde hospitalization netscript function

* Removed the suggestion that the combat path will lead to Daedalus, it still will. But new players should not be told that this is a viable path to completing a BitNode.

* getMemberInformation now returns everything about the member.

* New netscript function to get the players hacknet server hash capacity

* yesno dialog box will not keep older messages anymore

* v0.51.1

* Casino part 1

* Discord link in options, documentation for getMemberInformation updated, dev menu has more money options, tech vendors now handle max cores or max ram better

* Removed text under Factiosn referencing rejected factions.

* Removed html element forgotten in plain text

* Casino implementation

* v0.51.2
2021-04-09 18:12:31 -04:00
hydroflame
db2bf79e3b v0.51.1 (#835)
* infiltration use buttons instead of a links

* minor accessibility patch

* Hospitalization will not cost more than 10% of the players money.

* Adde hospitalization netscript function

* Removed the suggestion that the combat path will lead to Daedalus, it still will. But new players should not be told that this is a viable path to completing a BitNode.

* getMemberInformation now returns everything about the member.

* New netscript function to get the players hacknet server hash capacity

* yesno dialog box will not keep older messages anymore

* v0.51.1
2021-04-06 03:50:09 -04:00
hydroflame
6f330efc44 Added Disable ASCII art to options (#832)
* hotfix getPlayer missing factions

* Added ability to disable ascii art in options. ASCII art is impossible to deal with for screenreaders.
2021-04-02 20:14:35 -04:00
hydroflame
708c73fa0f hotfix getPlayer missing factions (#831) 2021-04-01 12:55:43 -04:00
hydroflame
c7febd5551 Failed crime no longer pretend you gained money. (#830) 2021-04-01 00:06:00 -04:00
hydroflame
ddbdf66d00 hotfix analyze (#828)
* hotfix analyze
2021-03-31 11:45:55 -04:00
51 changed files with 1189 additions and 149 deletions

10
README_contribution.md Normal file
View File

@@ -0,0 +1,10 @@
Deploying a new version
-----------------------
Update the following
- `src/Constants.ts` `Version` and `LatestUpdate`
- `package.json` `version`
- `doc/source/conf.py` `version` and `release`
- `doc/source/changelog.rst`
- post to discord
- post to reddit.com/r/Bitburner

View File

@@ -184,5 +184,4 @@
#infiltration-buttons .a-link-button { #infiltration-buttons .a-link-button {
display: inline; display: inline;
width: 25%;
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([395,0]),o()}({338:function(n,t,o){},340:function(n,t,o){},342:function(n,t,o){},344:function(n,t,o){},346:function(n,t,o){},348:function(n,t,o){},350:function(n,t,o){},352:function(n,t,o){},354:function(n,t,o){},356:function(n,t,o){},358:function(n,t,o){},360:function(n,t,o){},362:function(n,t,o){},364:function(n,t,o){},366:function(n,t,o){},368:function(n,t,o){},370:function(n,t,o){},372:function(n,t,o){},374:function(n,t,o){},376:function(n,t,o){},378:function(n,t,o){},380:function(n,t,o){},382:function(n,t,o){},384:function(n,t,o){},386:function(n,t,o){},388:function(n,t,o){},390:function(n,t,o){},392:function(n,t,o){},395:function(n,t,o){"use strict";o.r(t);o(394),o(392),o(390),o(388),o(386),o(384),o(382),o(380),o(378),o(376),o(374),o(372),o(370),o(368),o(366),o(364),o(362),o(360),o(358),o(356),o(354),o(352),o(350),o(348),o(346),o(344),o(342),o(340),o(338)}}); !function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([398,0]),o()}({341:function(n,t,o){},343:function(n,t,o){},345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},361:function(n,t,o){},363:function(n,t,o){},365:function(n,t,o){},367:function(n,t,o){},369:function(n,t,o){},371:function(n,t,o){},373:function(n,t,o){},375:function(n,t,o){},377:function(n,t,o){},379:function(n,t,o){},381:function(n,t,o){},383:function(n,t,o){},385:function(n,t,o){},387:function(n,t,o){},389:function(n,t,o){},391:function(n,t,o){},393:function(n,t,o){},395:function(n,t,o){},398:function(n,t,o){"use strict";o.r(t);o(397),o(395),o(393),o(391),o(389),o(387),o(385),o(383),o(381),o(379),o(377),o(375),o(373),o(371),o(369),o(367),o(365),o(363),o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345),o(343),o(341)}});
//# sourceMappingURL=engineStyle.bundle.js.map //# sourceMappingURL=engineStyle.bundle.js.map

View File

@@ -1374,8 +1374,7 @@ button {
margin-top: 20px; } margin-top: 20px; }
#infiltration-buttons .a-link-button { #infiltration-buttons .a-link-button {
display: inline; display: inline; }
width: 25%; }
/** /**
* Styling for the Augmentations UI. This is the page that displays all of the * Styling for the Augmentations UI. This is the page that displays all of the

26
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,51 @@
Changelog Changelog
========= =========
v0.51.2 - 2021-04-09 Vegas, Baby! (hydroflame)
----------------------------------------------
**New location: The Iker Molina Casino**
* A casino opened in Aevum. However the house is rumored to cheat. If only
we could give them a taste of their own medicine.
**Misc.**
* Link to discord added under options
* 'getMemberInformation' doc updated, oops
* tech vendor now handle max ram and cores.
v0.51.1 - 2021-04-06 Bugfixes because the author of the last patch sucks (it's hydroflame)
------------------------------------------------------------------------------------------
**Netscript**
* 'getPlayer' returns players faction and tor
* 'hospitalization' is a new singularity function.
* 'gang.getMemberInformation' now returns more information.
* 'hacknet.hashCapacity' is a new hacknet function that returns the maximum hash capacity.
**Hospitalization**
* Now only cost at most 10% of your money.
**Bugfix**
* confirmation dialog box no longer use previous text
**Accessibility**
* The game is a little easier to handle for screen readers (yes, there's an
absolute legend playing this game with a screen reader)
* Infiltration use buttons instead of a-links
* New option to disable ASCII art. This will make the metro map and world
map display as a list of buttons.
**Misc.**
* 'fl1ght.exe' will no longer suggest the combat path. Related faction
requirements unchanged.
v0.51.0 - 2021-03-31 Formulas (hydroflame) v0.51.0 - 2021-03-31 Formulas (hydroflame)
------------------------------------------ ------------------------------------------

View File

@@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
# The short X.Y version. # The short X.Y version.
version = '0.51' version = '0.51'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.51.0' release = '0.51.2'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@@ -5,7 +5,7 @@ calculateExp() Netscript Function
:RAM cost: 0 GB :RAM cost: 0 GB
:param number skillLevel: ``skillLevel`` to convert to exp. :param number skillLevel: ``skillLevel`` to convert to exp.
:param number mult: Assume a specific skill multipler. :param number mult: Assume a specific skill multipler (not exp multiplier).
:returns: number of exp required to reach given ``skillLevel`` with that multiplier. :returns: number of exp required to reach given ``skillLevel`` with that multiplier.
You must have Source-File 5-1 in order to use this function. You must have Source-File 5-1 in order to use this function.

View File

@@ -5,7 +5,7 @@ calculateSkill() Netscript Function
:RAM cost: 0 GB :RAM cost: 0 GB
:param number exp: ``exp`` to convert to skillLevel. :param number exp: ``exp`` to convert to skillLevel.
:param number mult: Assume a specific skill multipler. :param number mult: Assume a specific skill multipler (not exp multiplier).
:returns: skillLevel that ``exp`` would reach with that multiplier. :returns: skillLevel that ``exp`` would reach with that multiplier.
You must have Source-File 5-1 in order to use this function. You must have Source-File 5-1 in order to use this function.

View File

@@ -10,27 +10,35 @@ getMemberInformation() Netscript Function
The object has the following structure:: The object has the following structure::
{ {
agility: Agility stat name: Name of this member.
agilityEquipMult: Agility multiplier from equipment. Decimal form task: Name of currently assigned task.
agilityAscensionMult: Agility multiplier from ascension. Decimal form earnedRespect: Total amount of respect earned by this member.
augmentations: Array of names of all owned Augmentations hack: Hacking stat
charisma: Charisma stat str: Strength stat
charismaEquipMult: Charisma multiplier from equipment. Decimal form def: Defense stat
charismaAscensionMult: Charisma multiplier from ascension. Decimal form dex: Dexterity stat
defense: Defense stat agi: Agility stat
defenseEquipMult: Defense multiplier from equipment. Decimal form cha: Charisma stat
defenseAscensionMult: Defense multiplier from ascension. Decimal form hack_exp: Hacking experience
dexterity: Dexterity stat str_exp: Strength experience
dexterityEquipMult: Dexterity multiplier from equipment. Decimal form def_exp: Defense experience
dexterityAscensionMult: Dexterity multiplier from ascension. Decimal form dex_exp: Dexterity experience
equipment: Array of names of all owned Non-Augmentation Equipment agi_exp: Agility experience
hacking: Hacking stat cha_exp: Charisma experience
hackingEquipMult: Hacking multiplier from equipment. Decimal form hack_mult: Hacking multiplier from equipment. Decimal form
hackingAscensionMult: Hacking multiplier from ascension. Decimal form str_mult: Strength multiplier from equipment. Decimal form
strength: Strength stat def_mult: Defense multiplier from equipment. Decimal form
strengthEquipMult: Strength multiplier from equipment. Decimal form dex_mult: Dexterity multiplier from equipment. Decimal form
strengthAscensionMult: Strength multiplier from ascension. Decimal form agi_mult: Agility multiplier from equipment. Decimal form
task: Name of currently assigned task cha_mult: Charisma multiplier from equipment. Decimal form
hack_asc_mult: Hacking multiplier from ascension. Decimal form
str_asc_mult: Strength multiplier from ascension. Decimal form
def_asc_mult: Defense multiplier from ascension. Decimal form
dex_asc_mult: Dexterity multiplier from ascension. Decimal form
agi_asc_mult: Agility multiplier from ascension. Decimal form
cha_asc_mult: Charisma multiplier from ascension. Decimal form
upgrades: Array of names of all owned Non-Augmentation Equipment
augmentations: Array of names of all owned Augmentations
} }
Get stat and equipment-related information about a Gang Member Get stat and equipment-related information about a Gang Member

View File

@@ -0,0 +1,12 @@
hashCapacity() Netscript Function
=================================
.. warning:: This page contains spoilers for the game
.. js:function:: hashCapacity()
:RAM cost: 0 GB
:returns: The players maximum hash capacity.
.. note:: This function is only applicable for Hacknet Servers (the upgraded version of
a Hacknet Node).

View File

@@ -46,6 +46,7 @@ In :ref:`netscriptjs`::
upgradeCache() <hacknetnodeapi/upgradeCache> upgradeCache() <hacknetnodeapi/upgradeCache>
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost> getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
numHashes() <hacknetnodeapi/numHashes> numHashes() <hacknetnodeapi/numHashes>
hashCapacity() <hacknetnodeapi/hashCapacity>
hashCost() <hacknetnodeapi/hashCost> hashCost() <hacknetnodeapi/hashCost>
spendHashes() <hacknetnodeapi/spendHashes> spendHashes() <hacknetnodeapi/spendHashes>
getHashUpgradeLevel() <hacknetnodeapi/getHashUpgradeLevel> getHashUpgradeLevel() <hacknetnodeapi/getHashUpgradeLevel>

View File

@@ -28,6 +28,7 @@ level 3, then you will be able to access all of the Singularity Functions.
connect() <singularityfunctions/connect> connect() <singularityfunctions/connect>
manualHack() <singularityfunctions/manualHack> manualHack() <singularityfunctions/manualHack>
getPlayer() <singularityfunctions/getPlayer> getPlayer() <singularityfunctions/getPlayer>
hospitalize() <singularityfunctions/hospitalize>
isBusy() <singularityfunctions/isBusy> isBusy() <singularityfunctions/isBusy>
stopAction() <singularityfunctions/stopAction> stopAction() <singularityfunctions/stopAction>
upgradeHomeRam() <singularityfunctions/upgradeHomeRam> upgradeHomeRam() <singularityfunctions/upgradeHomeRam>

View File

@@ -93,6 +93,8 @@ getPlayer() Netscript Function
playtimeSinceLastAug playtimeSinceLastAug
playtimeSinceLastBitnode playtimeSinceLastBitnode
jobs jobs
factions
tor
} }
Example:: Example::

View File

@@ -0,0 +1,11 @@
hospitalize() Netscript Function
===================================
.. js:function:: hospitalize()
:RAM cost: 1 GB
:returns: The cost of your visit to the hospital.
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
Hospitalize yourself. Recovering all lost hp.

View File

@@ -261,16 +261,16 @@
<div id="infiltration-left-panel"> <div id="infiltration-left-panel">
<p id="infiltration-level-text"> </p> <p id="infiltration-level-text"> </p>
<div id="infiltration-buttons"> <div id="infiltration-buttons">
<a class="a-link-button tooltip" id="infiltration-kill"> </a> <button class="a-link-button tooltip" id="infiltration-kill"> </button>
<a class="a-link-button tooltip" id="infiltration-knockout"> </a> <button class="a-link-button tooltip" id="infiltration-knockout"> </button>
<a class="a-link-button tooltip" id="infiltration-stealthknockout"> </a> <button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
<a class="a-link-button tooltip" id="infiltration-assassinate"> </a> <button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
<a class="a-link-button tooltip" id="infiltration-hacksecurity"> </a> <button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-destroysecurity"> </a> <button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-sneak"> </a> <button class="a-link-button tooltip" id="infiltration-sneak"> </button>
<a class="a-link-button tooltip" id="infiltration-pickdoor"> </a> <button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
<a class="a-link-button tooltip" id="infiltration-bribe"> </a> <button class="a-link-button tooltip" id="infiltration-bribe"> </button>
<a class="a-link-button tooltip" id="infiltration-escape"> </a> <button class="a-link-button tooltip" id="infiltration-escape"> </button>
</div> </div>
</div> </div>
<div id="infiltration-right-panel"> <div id="infiltration-right-panel">
@@ -387,7 +387,7 @@
<!-- Game Options --> <!-- Game Options -->
<div id="game-options-container" class="popup-box-container"> <div id="game-options-container" class="popup-box-container">
<div id="game-options-content" class="game-options-box"> <div id="game-options-content" class="game-options-box">
<button id="game-options-close-button">&times;</button> <button id="game-options-close-button" aria-label="close options dialog">&times;</button>
<h1> Game Options </h1> <h1> Game Options </h1>
<br/> <br/>
<div id="game-options-left-panel"> <div id="game-options-left-panel">
@@ -511,6 +511,16 @@
<input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys"> <input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</fieldset> </fieldset>
<!-- View city as list of buttons instead of ASCII art. -->
<fieldset>
<label for="settingsDisableASCIIArt" class="tooltip">Disable ASCII art:
<span class="tooltiptexthigh">
If this is set all ASCII art will be disabled.
</span>
</label>
<input class="optionCheckbox" type="checkbox" name="settingsDisableASCIIArt" id="settingsDisableASCIIArt">
</fieldset>
<!-- Locale for displaying numbers --> <!-- Locale for displaying numbers -->
<fieldset> <fieldset>
<label for="settingsLocale" class="tooltip">Locale: <label for="settingsLocale" class="tooltip">Locale:
@@ -549,6 +559,7 @@
<div id="game-options-right-panel"> <div id="game-options-right-panel">
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a>
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a>
<a class="a-link-button" href="https://discord.gg/TFc3hKD" target="_blank">Discord</a>
<a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a> <a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a>
<button id="save-game-link" class="a-link-button"> Save Game </button> <button id="save-game-link" class="a-link-button"> Save Game </button>
<button id="delete-game-link" class="a-link-button"> Delete Game </button> <button id="delete-game-link" class="a-link-button"> Delete Game </button>

View File

@@ -121,5 +121,5 @@
"watch": "webpack --watch --mode production", "watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development" "watch:dev": "webpack --watch --mode development"
}, },
"version": "0.50.1" "version": "0.51.1"
} }

View File

@@ -8,6 +8,7 @@ import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers"; import { joinFaction, displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player"; import { Player } from "./Player";
import { hackWorldDaemon, redPillFlag } from "./RedPill"; import { hackWorldDaemon, redPillFlag } from "./RedPill";
import { calculateHospitalizationCost } from "./Hospital/Hospital";
import { Page, routing } from "./ui/navigationTracking"; import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat"; import { numeralWrapper } from "./ui/numeralFormat";
@@ -728,9 +729,10 @@ Bladeburner.prototype.completeAction = function() {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage; this.hpLost += damage;
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) { if (Player.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); this.moneyLost += cost;
} }
} }
var logLossText = ""; var logLossText = "";
@@ -800,9 +802,10 @@ Bladeburner.prototype.completeAction = function() {
if (action.hpLoss) { if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) { if (Player.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += (CONSTANTS.HospitalCostPerHp * Player.max_hp); this.moneyLost += cost;
} }
} }
teamLossMax = Math.floor(teamCount); teamLossMax = Math.floor(teamCount);
@@ -1771,7 +1774,7 @@ Bladeburner.prototype.updateOverviewContent = function() {
Stamina Penalty: {formatNumber((1-this.calculateStaminaPenalty())*100, 1)}%<br /><br /> Stamina Penalty: {formatNumber((1-this.calculateStaminaPenalty())*100, 1)}%<br /><br />
Team Size: {formatNumber(this.teamSize, 0)}<br /> Team Size: {formatNumber(this.teamSize, 0)}<br />
Team Members Lost: {formatNumber(this.teamLost, 0)}<br /><br /> Team Members Lost: {formatNumber(this.teamLost, 0)}<br /><br />
Num Times Hospitalized: this.numHosp<br /> Num Times Hospitalized: {this.numHosp}<br />
Money Lost From Hospitalizations: {Money(this.moneyLost)}<br /><br /> Money Lost From Hospitalizations: {Money(this.moneyLost)}<br /><br />
Current City: {this.city}<br /> Current City: {this.city}<br />
</>, DomElems.overviewGen1); </>, DomElems.overviewGen1);

89
src/Casino/CoinFlip.tsx Normal file
View File

@@ -0,0 +1,89 @@
/**
* React Subcomponent for displaying a location's UI, when that location is a gym
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { BadRNG } from "./RNG";
import { Game } from "./Game";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
result: any;
status: string;
}
const maxPlay = 10e3;
export class CoinFlip extends Game<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
investment: 1000,
result: <span> </span>,
status: '',
};
this.play = this.play.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = 1000;
}
if (investment > maxPlay) {
investment = maxPlay;
}
this.setState({investment: investment});
}
play(guess: string) {
if(this.reachedLimit(this.props.p)) return;
const v = BadRNG.random();
let letter: string;
if (v < 0.5) {
letter = 'H';
} else {
letter = 'T';
}
const correct: boolean = guess===letter;
this.setState({
result: <span className={correct ? "text" : "failure"}>{letter}</span>,
status: correct ? " win!" : "lose!",
});
if (correct) {
this.win(this.props.p, this.state.investment);
} else {
this.win(this.props.p, -this.state.investment);
}
if(this.reachedLimit(this.props.p)) return;
}
render() {
return <>
<pre>
++<br />
| | | |<br />
| | {this.state.result} | |<br />
| | | |<br />
++<br />
</pre>
<span className="text">Play for: </span><input type="number" className='text-input' onChange={this.updateInvestment} value={this.state.investment} /><br />
<StdButton onClick={() => this.play('H')} text={"Head!"} />
<StdButton onClick={() => this.play('T')} text={"Tail!"} />
<h1>{this.state.status}</h1>
</>
}
}

20
src/Casino/Game.tsx Normal file
View File

@@ -0,0 +1,20 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../utils/DialogBox";
const gainLimit = 10e9;
export class Game<T,U> extends React.Component<T, U> {
win(p: IPlayer, n: number) {
p.gainMoney(n);
p.recordMoneySource(n, "casino");
}
reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if(reached) {
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
}
return reached;
}
}

64
src/Casino/RNG.ts Normal file
View File

@@ -0,0 +1,64 @@
export interface RNG {
random(): number
}
/*
* very bad RNG, meant to be used as introduction to RNG manipulation. It has a
* period of 1024.
*/
class RNG0 implements RNG {
x: number;
m: number = 1024;
a: number = 341;
c: number = 1;
constructor() {
this.x = 0;
this.reset();
}
step() {
this.x = (this.a*this.x+this.c) % this.m;
}
random(): number {
this.step();
return this.x/this.m;
}
reset() {
this.x = (new Date()).getTime() % this.m;
}
}
export const BadRNG: RNG0 = new RNG0();
/*
* WichmannHill PRNG
* The period is 6e12.
*/
export class WHRNG implements RNG {
s1: number = 0;
s2: number = 0;
s3: number = 0;
constructor(totalPlaytime: number) {
// This one is seeded by the players total play time.
const v: number = (totalPlaytime/1000)%30000;
this.s1 = v;
this.s2 = v;
this.s3 = v;
}
step() {
this.s1 = (171 * this.s1) % 30269;
this.s2 = (172 * this.s2) % 30307;
this.s3 = (170 * this.s3) % 30323;
}
random(): number {
this.step();
return (this.s1/30269.0 + this.s2/30307.0 + this.s3/30323.0)%1.0;
}
}

290
src/Casino/Roulette.tsx Normal file
View File

@@ -0,0 +1,290 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { Game } from "./Game";
import { WHRNG } from "./RNG";
type IProps = {
p: IPlayer;
}
type IState = {
investment: number;
canPlay: boolean;
status: string | JSX.Element;
n: number;
lock: boolean;
strategy: Strategy;
}
const maxPlay = 1e6;
function isRed(n: number): boolean {
return [1, 3, 5, 7, 9, 12, 14, 16, 18, 19,
21, 23, 25, 27, 30, 32, 34, 36].includes(n);
}
function isBlack(n: number): boolean {
return !isRed(n);
}
type Strategy = {
match: (n: number) => boolean;
payout: number;
}
const redNumbers: number[] = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19,
21, 23, 25, 27, 30, 32, 34, 36];
const strategies: {
Red: Strategy;
Black: Strategy;
Odd: Strategy;
Even: Strategy;
High: Strategy;
Low: Strategy;
Third1: Strategy;
Third2: Strategy;
Third3: Strategy;
} = {
Red: {
match: (n: number): boolean => {
if (n === 0) return false;
return redNumbers.includes(n);
},
payout: 1,
},
Black: {
match: (n: number): boolean => {
return !redNumbers.includes(n);
},
payout: 1,
},
Odd: {
match: (n: number): boolean => {
if (n === 0) return false;
return n%2 === 1;
},
payout: 1,
},
Even: {
match: (n: number): boolean => {
if (n === 0) return false;
return n%2 === 0;
},
payout: 1,
},
High: {
match: (n: number): boolean => {
if (n === 0) return false;
return n>18
},
payout: 1,
},
Low: {
match: (n: number): boolean => {
if (n === 0) return false;
return n<19;
},
payout: 1,
},
Third1: {
match: (n: number): boolean => {
if (n === 0) return false;
return n <= 12;
},
payout: 2,
},
Third2: {
match: (n: number): boolean => {
if (n === 0) return false;
return 13 <= n && n <= 24;
},
payout: 2,
},
Third3: {
match: (n: number): boolean => {
if (n === 0) return false;
return 25 <= n;
},
payout: 2,
},
}
function Single(s: number): Strategy {
return {
match: (n: number): boolean => {
return s === n;
},
payout: 36,
}
}
export class Roulette extends Game<IProps, IState> {
interval: number = -1;
rng: WHRNG;
constructor(props: IProps) {
super(props);
this.rng = new WHRNG((new Date()).getTime());
this.state = {
investment: 1000,
canPlay: true,
status: 'waiting',
n: 0,
lock: true,
strategy: {
payout: 0,
match: (n: number): boolean => { return false },
},
}
this.step = this.step.bind(this);
this.currentNumber = this.currentNumber.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount() {
this.interval = setInterval(this.step, 50);
}
step() {
if (!this.state.lock) {
this.setState({n: Math.floor(Math.random()*37)});
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = 1000;
}
if (investment > maxPlay) {
investment = maxPlay
}
this.setState({investment: investment});
}
currentNumber() {
if (this.state.n === 0) return '0';
const color = isRed(this.state.n) ? 'R' : 'B';
return `${this.state.n}${color}`;
}
play(s: Strategy) {
if(this.reachedLimit(this.props.p)) return;
this.setState({
canPlay: false,
lock: false,
status: 'playing',
strategy: s,
})
setTimeout(() => {
let n = Math.floor(this.rng.random()*37);
let status = <></>;
let gain = 0;
let playerWin = this.state.strategy.match(n)
// oh yeah, the house straight up cheats. Try finding the seed now!
if(playerWin && Math.random() > 0.9) {
playerWin = false;
while(this.state.strategy.match(n)) {
n++;
}
}
if(playerWin) {
gain = this.state.investment*this.state.strategy.payout;
status = <>won {Money(gain)}</>;
} else {
gain = -this.state.investment;
status = <>lost {Money(-gain)}</>;
}
this.win(this.props.p, gain);
this.setState({
canPlay: true,
lock: true,
status: status,
n: n,
});
this.reachedLimit(this.props.p);
}, 1600);
}
render() {
return <>
<h1>{this.currentNumber()}</h1>
<input type="number" className='text-input' onChange={this.updateInvestment} placeholder={"Amount to play"} value={this.state.investment} disabled={!this.state.canPlay} />
<h1>{this.state.status}</h1>
<table>
<tbody>
<tr>
<td><StdButton text={"3"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(3))} /></td>
<td><StdButton text={"6"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(6))} /></td>
<td><StdButton text={"9"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(9))} /></td>
<td><StdButton text={"12"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(12))} /></td>
<td><StdButton text={"15"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(15))} /></td>
<td><StdButton text={"18"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(18))} /></td>
<td><StdButton text={"21"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(21))} /></td>
<td><StdButton text={"24"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(24))} /></td>
<td><StdButton text={"27"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(27))} /></td>
<td><StdButton text={"30"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(30))} /></td>
<td><StdButton text={"33"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(33))} /></td>
<td><StdButton text={"36"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(36))} /></td>
</tr>
<tr>
<td><StdButton text={"2"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(2))} /></td>
<td><StdButton text={"5"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(5))} /></td>
<td><StdButton text={"8"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(8))} /></td>
<td><StdButton text={"11"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(11))} /></td>
<td><StdButton text={"14"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(14))} /></td>
<td><StdButton text={"17"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(17))} /></td>
<td><StdButton text={"20"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(20))} /></td>
<td><StdButton text={"23"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(23))} /></td>
<td><StdButton text={"26"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(26))} /></td>
<td><StdButton text={"29"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(29))} /></td>
<td><StdButton text={"32"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(32))} /></td>
<td><StdButton text={"35"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(35))} /></td>
</tr>
<tr>
<td><StdButton text={"1"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(1))} /></td>
<td><StdButton text={"4"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(4))} /></td>
<td><StdButton text={"7"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(7))} /></td>
<td><StdButton text={"10"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(10))} /></td>
<td><StdButton text={"13"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(13))} /></td>
<td><StdButton text={"16"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(16))} /></td>
<td><StdButton text={"19"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(19))} /></td>
<td><StdButton text={"22"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(22))} /></td>
<td><StdButton text={"25"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(25))} /></td>
<td><StdButton text={"28"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(28))} /></td>
<td><StdButton text={"31"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(31))} /></td>
<td><StdButton text={"34"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(34))} /></td>
</tr>
<tr>
<td colSpan={4}><StdButton text={"1 to 12"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third1)} /></td>
<td colSpan={4}><StdButton text={"13 to 24"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third2)} /></td>
<td colSpan={4}><StdButton text={"25 to 36"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Third3)} /></td>
</tr>
<tr>
<td colSpan={2}><StdButton text={"Red"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Red)} /></td>
<td colSpan={2}><StdButton text={"Black"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Black)} /></td>
<td colSpan={2}><StdButton text={"Odd"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Odd)} /></td>
<td colSpan={2}><StdButton text={"Even"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Even)} /></td>
<td colSpan={2}><StdButton text={"High"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.High)} /></td>
<td colSpan={2}><StdButton text={"Low"} disabled={!this.state.canPlay} onClick={()=>this.play(strategies.Low)} /></td>
</tr>
<tr>
<td><StdButton text={"0"} disabled={!this.state.canPlay} onClick={()=>this.play(Single(0))} /></td>
</tr>
</tbody>
</table>
</>
}
}

234
src/Casino/SlotMachine.tsx Normal file
View File

@@ -0,0 +1,234 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { WHRNG } from "./RNG";
import { Game } from "./Game";
type IProps = {
p: IPlayer;
}
type IState = {
index: number[];
locks: number[];
investment: number;
canPlay: boolean;
status: string | JSX.Element;
}
// statically shuffled array of symbols.
let symbols = ["D", "C", "$", "?", "♥", "A", "C", "B", "C", "E", "B", "E", "C",
"*", "D", "♥", "B", "A", "A", "A", "C", "A", "D", "B", "E", "?", "D", "*",
"@", "♥", "B", "E", "?"];
function getPayout(s: string, n: number): number {
switch (s) {
case "$":
return [20, 200, 1000][n];
case "@":
return [8, 80, 400][n];
case "♥":
case "?":
return [6, 20, 150][n];
case "D":
case "E":
return [1, 8, 30][n];
default:
return [1, 5, 20][n];
}
}
const payLines = [
// lines
[[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]],
[[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]],
[[2, 0], [2, 1], [2, 2], [2, 3], [2, 4]],
// Vs
[[2, 0], [1, 1], [0, 2], [1, 3], [2, 4]],
[[0, 0], [1, 1], [2, 2], [1, 3], [0, 4]],
// rest
[[0, 0], [1, 1], [1, 2], [1, 3], [0, 4]],
[[2, 0], [1, 1], [1, 2], [1, 3], [2, 4]],
[[1, 0], [0, 1], [0, 2], [0, 3], [1, 4]],
[[1, 0], [2, 1], [2, 2], [2, 3], [1, 4]],
];
const maxPlay = 1e6;
export class SlotMachine extends Game<IProps, IState> {
rng: WHRNG;
interval: number = -1;
constructor(props: IProps) {
super(props);
this.rng = new WHRNG(this.props.p.totalPlaytime);
this.state = {
index: [0, 0, 0, 0, 0],
investment: 1000,
locks: [0, 0, 0, 0, 0],
canPlay: true,
status: 'waiting',
};
this.play = this.play.bind(this);
this.lock = this.lock.bind(this);
this.unlock = this.unlock.bind(this);
this.step = this.step.bind(this);
this.checkWinnings = this.checkWinnings.bind(this);
this.getTable = this.getTable.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount() {
this.interval = setInterval(this.step, 50);
}
step() {
let stoppedOne = false;
const index = this.state.index.slice();
for(const i in index) {
if (index[i] === this.state.locks[i] && !stoppedOne) continue;
index[i] = (index[i] + 1) % symbols.length;
stoppedOne = true;
}
this.setState({index: index});
if(stoppedOne && index.every((e, i) => e === this.state.locks[i])) {
this.checkWinnings();
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
getTable(): string[][] {
return [
[symbols[(this.state.index[0]+symbols.length-1)%symbols.length], symbols[(this.state.index[1]+symbols.length-1)%symbols.length], symbols[(this.state.index[2]+symbols.length-1)%symbols.length], symbols[(this.state.index[3]+symbols.length-1)%symbols.length], symbols[(this.state.index[4]+symbols.length-1)%symbols.length]],
[symbols[this.state.index[0]], symbols[this.state.index[1]], symbols[this.state.index[2]], symbols[this.state.index[3]], symbols[this.state.index[4]]],
[symbols[(this.state.index[0]+1)%symbols.length], symbols[(this.state.index[1]+1)%symbols.length], symbols[(this.state.index[2]+1)%symbols.length], symbols[(this.state.index[3]+1)%symbols.length], symbols[(this.state.index[4]+1)%symbols.length]],
];
}
play() {
if(this.reachedLimit(this.props.p)) return;
this.setState({status: 'playing'});
this.win(this.props.p, -this.state.investment);
if(!this.state.canPlay) return;
this.unlock();
setTimeout(this.lock, this.rng.random()*2000+1000);
}
lock() {
this.setState({
locks: [
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
Math.floor(this.rng.random()*symbols.length),
],
})
}
checkWinnings() {
const t = this.getTable();
const getPaylineData = function(payline: number[][]): string[] {
let data = [];
for(const point of payline) {
data.push(t[point[0]][point[1]]);
}
return data;
}
const countSequence = function(data: string[]): number {
let count = 1;
for(let i = 1; i < data.length; i++) {
if (data[i]!==data[i-1]) break;
count++;
}
return count;
}
let gains = -this.state.investment;
for (const payline of payLines) {
const data = getPaylineData(payline);
const count = countSequence(data);
if (count < 3) continue;
const payout = getPayout(data[0], count-3);
gains += this.state.investment*payout;
this.win(this.props.p, this.state.investment*payout);
}
this.setState({
status: <>{gains>0?"gained":"lost"} {Money(Math.abs(gains))}</>,
canPlay: true,
})
if(this.reachedLimit(this.props.p)) return;
}
unlock() {
this.setState({
locks: [-1, -1, -1, -1, -1],
canPlay: false,
})
}
updateInvestment(e: React.FormEvent<HTMLInputElement>) {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = 1000;
}
if (investment > maxPlay) {
investment = maxPlay;
}
this.setState({investment: investment});
}
render() {
const t = this.getTable();
return <>
<pre>
++<br />
| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |<br />
| | | | | | | |<br />
| | {symbols[this.state.index[0]]} | {symbols[this.state.index[1]]} | {symbols[this.state.index[2]]} | {symbols[this.state.index[3]]} | {symbols[this.state.index[4]]} | |<br />
| | | | | | | |<br />
| | {symbols[(this.state.index[0]+1)%symbols.length]} | {symbols[(this.state.index[1]+1)%symbols.length]} | {symbols[(this.state.index[2]+1)%symbols.length]} | {symbols[(this.state.index[3]+1)%symbols.length]} | {symbols[(this.state.index[4]+1)%symbols.length]} | |<br />
++<br />
</pre>
<input type="number" className='text-input' onChange={this.updateInvestment} placeholder={"Amount to play"} value={this.state.investment} disabled={!this.state.canPlay} />
<StdButton onClick={this.play} text={"Spin!"} disabled={!this.state.canPlay} />
<h1>{this.state.status}</h1>
<h2>Pay lines</h2>
<pre>
----- ····· ····· <br />
····· ----- ····· <br />
····· ····· ----- <br />
</pre>
<br />
<pre>
··^·· \···/ \···/<br />
·/·\· ·\·/· ·---·<br />
/···\ ··v·· ·····<br />
</pre>
<br />
<pre>
····· ·---· ·····<br />
·---· /···\ \···/<br />
/···\ ····· ·---·<br />
</pre>
</>
}
}
// https://felgo.com/doc/how-to-make-a-slot-game-tutorial/

View File

@@ -6,7 +6,7 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.51.0", Version: "0.51.2",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -228,25 +228,16 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.51.0 - 2021-03-31 Formulas (hydroflame) v0.51.2 - 2021-04-09 Vegas, Baby! (hydroflame)
------- -------
Formulas API New location: The Iker Molina Casino
* A new API is introduced, this gives players access to various formulas used in the game. * A casino opened in Aevum. However the house is rumored to cheat. If only
It'll help you make more informed decisions. we could give them a taste of their own medicine.
Netscript Misc.
* 'getServer' is a new function meant to be used with the formulas API. * Link to discord added under options
* 'getPlayer' is a new function meant to be used with the formulas API. * 'getMemberInformation' doc updated, oops
* 'getStats' and 'getCharacterInformation' are deprecated in favor of 'getPlayer' * tech vendor now handle max ram and cores.
* 'getCurrentServer' is a new function that returns the server the player is currently connected.
Display
* All money should now consistently be orange.
* All rep should now consistently be light-yellow.
* Most numbers should display consistently now (aka all money is formatted the same).
Click to copy
* Certain UI elements are now 'click-to-copy'
` `
} }

View File

@@ -708,8 +708,11 @@ class DevMenuComponent extends Component {
<h2>Generic</h2> <h2>Generic</h2>
</div> </div>
<div className="row"> <div className="row">
<button className="std-button" onClick={this.addMoney(1e6)}>Add $1m</button>
<button className="std-button" onClick={this.addMoney(1e9)}>Add $1b</button>
<button className="std-button" onClick={this.addMoney(1e12)}>Add $1t</button> <button className="std-button" onClick={this.addMoney(1e12)}>Add $1t</button>
<button className="std-button" onClick={this.addMoney(1e15)}>Add $1000t</button> <button className="std-button" onClick={this.addMoney(1e15)}>Add $1000t</button>
<button className="std-button" onClick={this.addMoney(1e27)}>Add $1e27</button>
<button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button> <button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button>
</div> </div>
<div className="row"> <div className="row">

View File

@@ -42,11 +42,9 @@ export class GeneralInfo extends React.Component {
hackers all around the world to anonymously share computing power and hackers all around the world to anonymously share computing power and
perform distributed cyberattacks without the fear of being traced. perform distributed cyberattacks without the fear of being traced.
</p> </p>
<br />
<p className={"hacknet-general-info"}> <p className={"hacknet-general-info"}>
{this.getSecondParagraph()} {this.getSecondParagraph()}
</p> </p>
<br />
<p className={"hacknet-general-info"}> <p className={"hacknet-general-info"}>
{this.getThirdParagraph()} {this.getThirdParagraph()}
</p> </p>

View File

@@ -134,19 +134,22 @@ export class HacknetNode extends React.Component {
</span> </span>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Level:</p><span className={"text upgradable-info"}>{node.level}</span> <p>Level:</p>
<span className={"text upgradable-info"}>{node.level}</span>
<button className={upgradeLevelClass} onClick={upgradeLevelOnClick}> <button className={upgradeLevelClass} onClick={upgradeLevelOnClick}>
{upgradeLevelContent} {upgradeLevelContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>RAM:</p><span className={"text upgradable-info"}>{node.ram}GB</span> <p>RAM:</p>
<span className={"text upgradable-info"}>{node.ram}GB</span>
<button className={upgradeRamClass} onClick={upgradeRamOnClick}> <button className={upgradeRamClass} onClick={upgradeRamOnClick}>
{upgradeRamContent} {upgradeRamContent}
</button> </button>
</div> </div>
<div className={"row"}> <div className={"row"}>
<p>Cores:</p><span className={"text upgradable-info"}>{node.cores}</span> <p>Cores:</p>
<span className={"text upgradable-info"}>{node.cores}</span>
<button className={upgradeCoresClass} onClick={upgradeCoresOnClick}> <button className={upgradeCoresClass} onClick={upgradeCoresOnClick}>
{upgradeCoresContent} {upgradeCoresContent}
</button> </button>

26
src/Hospital/Hospital.ts Normal file
View File

@@ -0,0 +1,26 @@
import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"
export function getHospitalizationCost(p: IPlayer): number {
let money;
if (typeof p.money === 'number') {
money = p.money;
} else {
money = p.money.toNumber();
}
if (money < 0) {
return 0;
}
return Math.min(money*0.1, (p.max_hp - p.hp) * CONSTANTS.HospitalCostPerHp);
}
export function calculateHospitalizationCost(p: IPlayer, damage: number): number {
const oldhp = p.hp;
p.hp -= damage
if (p.hp < 0) p.hp = 0;
const cost = getHospitalizationCost(p);
p.hp = oldhp;
return cost;
}

View File

@@ -11,4 +11,5 @@ export enum LocationType {
TechVendor, TechVendor,
TravelAgency, TravelAgency,
University, University,
Casino,
} }

View File

@@ -47,14 +47,14 @@ Cities[CityName.Aevum].asciiArt = `
\\ 56 B \\ 56 B
x \\ [summit university] x \\ [summit university]
\\ \\ 28 \\ \\ 28
\\ [snap fitness gym] x o--L------------ \\ [snap fitness gym] x o--L-----------N
K \\ / K \\ /
\\ \\ P \\ \\ Q [casino]
x 58 \\ / [travel agency] x 58 \\ / [travel agency]
\\ 94 95 o \\ 94 95 o
90 x 59 o------o | 90 x 59 o------o |
\\ / \\ | 98 102 103 \\ / \\ | 98 102 103
o--------N------x----o 93 96 o-----+------------o o----o o--------O------x----o 93 96 o-----+------------o o----o
\\ | \\ / \\ | \\ /
[hospital] \\ 61 [ecorp] x 31 99 o-F-o 101 [hospital] \\ 61 [ecorp] x 31 99 o-F-o 101
o | o |
@@ -69,13 +69,13 @@ Cities[CityName.Aevum].asciiArt = `
| 34 x \\ | 34 x \\
[clarke inc.] C | \\ [world stock exchange] [clarke inc.] C | \\ [world stock exchange]
| | \\ | | \\
| | o-M-------Q--------o | | o-M-------R--------o
[galactic cybersystems] G 35 x [galactic cybersystems] G 35 x
| [watchdog security] | [watchdog security]
| |
67 o 67 o
[the slums] O ` [the slums] P `
Cities[CityName.Chongqing].asciiArt = ` Cities[CityName.Chongqing].asciiArt = `
| |
75 o 75 o

View File

@@ -198,7 +198,12 @@ export function createStartCorporationPopup(p: IPlayer) {
*/ */
export function createUpgradeHomeCoresPopup(p: IPlayer) { export function createUpgradeHomeCoresPopup(p: IPlayer) {
const currentCores = p.getHomeComputer().cpuCores; const currentCores = p.getHomeComputer().cpuCores;
if (currentCores >= 8) { return; } // Max of 8 cores if (currentCores >= 8) {
dialogBoxCreate(<>
You've have the maximum amount of CPU cores on your home computer.
</>);
return;
}
// Cost of purchasing another cost is found by indexing this array with number of current cores // Cost of purchasing another cost is found by indexing this array with number of current cores
const allCosts = [ const allCosts = [
@@ -255,6 +260,14 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
const noBtn = yesNoBoxGetNoButton(); const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) { return; } if (yesBtn == null || noBtn == null) { return; }
const homeComputer = p.getHomeComputer();
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
dialogBoxCreate(<>
You've have the maximum amount of RAM on your home computer.
</>);
return;
}
yesBtn.innerText = "Purchase"; yesBtn.innerText = "Purchase";
yesBtn.addEventListener("click", ()=>{ yesBtn.addEventListener("click", ()=>{
purchaseRamForHomeComputer(cost, p); purchaseRamForHomeComputer(cost, p);

View File

@@ -24,6 +24,7 @@ export enum LocationName {
AevumSnapFitnessGym = "Snap Fitness Gym", AevumSnapFitnessGym = "Snap Fitness Gym",
AevumSummitUniversity = "Summit University", AevumSummitUniversity = "Summit University",
AevumWatchdogSecurity = "Watchdog Security", AevumWatchdogSecurity = "Watchdog Security",
AevumCasino = "Iker Molina Casino",
// Chongqing locations // Chongqing locations
ChongqingKuaiGongInternational = "KuaiGong International", ChongqingKuaiGongInternational = "KuaiGong International",

View File

@@ -145,6 +145,11 @@ export const LocationsMetadata: IConstructorParams[] = [
name: LocationName.AevumWatchdogSecurity, name: LocationName.AevumWatchdogSecurity,
types: [LocationType.Company], types: [LocationType.Company],
}, },
{
city: CityName.Aevum,
name: LocationName.AevumCasino,
types: [LocationType.Casino],
},
{ {
city: CityName.Chongqing, city: CityName.Chongqing,
infiltrationData: { infiltrationData: {

View File

@@ -0,0 +1,89 @@
/**
* React Subcomponent for displaying a location's UI, when that location is a gym
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
import { SlotMachine } from "../../Casino/SlotMachine";
import { CoinFlip } from "../../Casino/CoinFlip";
import { Roulette } from "../../Casino/Roulette";
type IProps = {
p: IPlayer;
}
type IState = {
game: string;
}
export class CasinoLocation extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
game: '',
}
this.updateGame = this.updateGame.bind(this);
}
updateGame(game: string) {
this.setState({
game: game,
});
}
renderGames() {
return (<>
<StdButton
onClick={() => this.updateGame('coin')}
text={"Play coin flip"}
/><br />
<StdButton
onClick={() => this.updateGame('slots')}
text={"Play slots"}
/><br />
<StdButton
onClick={() => this.updateGame('roulette')}
text={"Play roulette"}
/>
</>)
}
renderGame() {
let elem;
switch(this.state.game) {
case 'coin':
elem = <CoinFlip p={this.props.p} />
break;
case 'slots':
elem = <SlotMachine p={this.props.p} />
break;
case 'roulette':
elem = <Roulette p={this.props.p} />
break;
}
return (<>
<StdButton onClick={() => this.updateGame('')} text={"Stop playing"} />
{elem}
</>)
}
render() {
if(!this.state.game) {
return this.renderGames();
} else {
return this.renderGame();
}
}
}

View File

@@ -7,6 +7,7 @@ import * as React from "react";
import { City } from "../City"; import { City } from "../City";
import { LocationName } from "../data/LocationNames"; import { LocationName } from "../data/LocationNames";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@@ -16,7 +17,7 @@ type IProps = {
} }
export class LocationCity extends React.Component<IProps, any> { export class LocationCity extends React.Component<IProps, any> {
render() { asciiCity() {
const thiscity = this; const thiscity = this;
const topprop = this.props const topprop = this.props
@@ -66,9 +67,29 @@ export class LocationCity extends React.Component<IProps, any> {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>) elems.push(<pre key={i}>{lineElems(lines[i])}</pre>)
} }
return elems;
}
listCity() {
const locationButtons = this.props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={this.props.enterLocation.bind(this, locName)} text={locName} />
</li>
)
});
return (
<ul>
{locationButtons}
</ul>
)
}
render() {
return ( return (
<> <>
{elems} {Settings.DisableASCIIArt ? this.listCity() : this.asciiCity()}
</> </>
) )
} }

View File

@@ -14,6 +14,7 @@ import { SpecialLocation } from "./SpecialLocation";
import { TechVendorLocation } from "./TechVendorLocation"; import { TechVendorLocation } from "./TechVendorLocation";
import { TravelAgencyLocation } from "./TravelAgencyLocation"; import { TravelAgencyLocation } from "./TravelAgencyLocation";
import { UniversityLocation } from "./UniversityLocation"; import { UniversityLocation } from "./UniversityLocation";
import { CasinoLocation } from "./CasinoLocation";
import { Location } from "../Location"; import { Location } from "../Location";
import { LocationType } from "../LocationTypeEnum"; import { LocationType } from "../LocationTypeEnum";
@@ -131,6 +132,15 @@ export class GenericLocation extends React.Component<IProps, any> {
) )
} }
if (this.props.loc.types.includes(LocationType.Casino)) {
content.push(
<CasinoLocation
key={"casinoLocation"}
p={this.props.p}
/>
)
}
return content; return content;
} }

View File

@@ -7,6 +7,7 @@ import * as React from "react";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { getHospitalizationCost } from "../../Hospital/Hospital";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton"; import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
@@ -42,7 +43,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
} }
getCost(): number { getCost(): number {
return (this.props.p.max_hp - this.props.p.hp) * CONSTANTS.HospitalCostPerHp; return getHospitalizationCost(this.props.p);
} }
getHealed(e: React.MouseEvent<HTMLElement>): void { getHealed(e: React.MouseEvent<HTMLElement>): void {

View File

@@ -10,6 +10,7 @@ import { createTravelPopup } from "../LocationsHelpers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@@ -32,7 +33,7 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
this.btnStyle = { display: "block" }; this.btnStyle = { display: "block" };
} }
render() { asciiWorldMap() {
const thisTravelAgencyLocation = this; const thisTravelAgencyLocation = this;
function LocationLetter(props: any) { function LocationLetter(props: any) {
@@ -76,4 +77,42 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
</div> </div>
) )
} }
listWorldMap() {
const travelBtns: React.ReactNode[] = [];
for (const key in CityName) {
const city = CityName[key];
// Skip current city
if (city === this.props.p.city) { continue; }
travelBtns.push(
<StdButton
key={city}
onClick={createTravelPopup.bind(null, city, this.props.travel)}
style={this.btnStyle}
text={`Travel to ${city}`}
/>
)
}
return (
<div>
<p>
From here, you can travel to any other city! A ticket
costs {Money(CONSTANTS.TravelCost)}.
</p>
{travelBtns}
</div>
)
}
render() {
if (Settings.DisableASCIIArt) {
return this.listWorldMap();
} else {
return this.asciiWorldMap();
}
}
} }

View File

@@ -183,6 +183,7 @@ export const RamCosts: IMap<any> = {
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getPlayer: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, getPlayer: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
hospitalize: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2, stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2,
upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost, upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost,

View File

@@ -766,6 +766,10 @@ function NetscriptFunctions(workerScript) {
if (!hasHacknetServers()) { return 0; } if (!hasHacknetServers()) { return 0; }
return Player.hashManager.hashes; return Player.hashManager.hashes;
}, },
hashCapacity: function() {
if (!hasHacknetServers()) { return 0; }
return Player.hashManager.capacity;
},
hashCost : function(upgName) { hashCost : function(upgName) {
if (!hasHacknetServers()) { return Infinity; } if (!hasHacknetServers()) { return Infinity; }
@@ -2970,10 +2974,17 @@ function NetscriptFunctions(workerScript) {
playtimeSinceLastAug: Player.playtimeSinceLastAug, playtimeSinceLastAug: Player.playtimeSinceLastAug,
playtimeSinceLastBitnode: Player.playtimeSinceLastBitnode, playtimeSinceLastBitnode: Player.playtimeSinceLastBitnode,
jobs: {}, jobs: {},
factions: Player.factions.slice(),
tor: SpecialServerIps.hasOwnProperty("Darkweb Server"),
}; };
Object.assign(data.jobs, Player.jobs); Object.assign(data.jobs, Player.jobs);
return data; return data;
}, },
hospitalize: function() {
updateDynamicRam("hospitalize", getRamCost("hospitalize"));
checkSingularityAccess("hospitalize", 1);
return Player.hospitalize();
},
isBusy: function() { isBusy: function() {
updateDynamicRam("isBusy", getRamCost("isBusy")); updateDynamicRam("isBusy", getRamCost("isBusy"));
checkSingularityAccess("isBusy", 1); checkSingularityAccess("isBusy", 1);
@@ -3574,27 +3585,35 @@ function NetscriptFunctions(workerScript) {
checkGangApiAccess("getMemberInformation"); checkGangApiAccess("getMemberInformation");
const member = getGangMember("getMemberInformation", name); const member = getGangMember("getMemberInformation", name);
return { return {
agility: member.agi, name: member.name,
agilityEquipMult: member.agi_mult, task: member.task,
agilityAscensionMult: member.agi_asc_mult, earnedRespect: member.earnedRespect,
augmentations: member.augmentations.slice(), hack: member.hack,
charisma: member.cha, str: member.str,
charismaEquipMult: member.cha_mult, def: member.def,
charismaAscensionMult: member.cha_asc_mult, dex: member.dex,
defense: member.def, agi: member.agi,
defenseEquipMult: member.def_mult, cha: member.cha,
defenseAscensionMult: member.def_asc_mult, hack_exp: member.hack_exp,
dexterity: member.dex, str_exp: member.str_exp,
dexterityEquipMult: member.dex_mult, def_exp: member.def_exp,
dexterityAscensionMult: member.dex_asc_mult, dex_exp: member.dex_exp,
equipment: member.upgrades.slice(), agi_exp: member.agi_exp,
hacking: member.hack, cha_exp: member.cha_exp,
hackingEquipMult: member.hack_mult, hack_mult: member.hack_mult,
hackingAscensionMult: member.hack_asc_mult, str_mult: member.str_mult,
strength: member.str, def_mult: member.def_mult,
strengthEquipMult: member.str_mult, dex_mult: member.dex_mult,
strengthAscensionMult: member.str_asc_mult, agi_mult: member.agi_mult,
task: member.task, cha_mult: member.cha_mult,
hack_asc_mult: member.hack_asc_mult,
str_asc_mult: member.str_asc_mult,
def_asc_mult: member.def_asc_mult,
dex_asc_mult: member.dex_asc_mult,
agi_asc_mult: member.agi_asc_mult,
cha_asc_mult: member.cha_asc_mult,
upgrades: member.upgrades.slice(),
augmentations: member.augmentations.slice(),
} }
}, },
canRecruitMember: function() { canRecruitMember: function() {

View File

@@ -179,4 +179,5 @@ export interface IPlayer {
giveExploit(exploit: Exploit): void; giveExploit(exploit: Exploit): void;
queryStatFromString(str: string): number; queryStatFromString(str: string): number;
getIntelligenceBonus(weight: number): number; getIntelligenceBonus(weight: number): number;
getCasinoWinnings(): number;
} }

View File

@@ -43,6 +43,7 @@ import { applyExploit } from "../../Exploits/applyExploits";
import { SourceFiles } from "../../SourceFile/SourceFiles"; import { SourceFiles } from "../../SourceFile/SourceFiles";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing"; import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing";
import { getHospitalizationCost } from "../../Hospital/Hospital";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
@@ -1504,7 +1505,6 @@ export function finishCrime(cancelled) {
dialogBoxCreate(<> dialogBoxCreate(<>
Crime failed!<br /><br /> Crime failed!<br /><br />
You gained:<br /> You gained:<br />
{Money(this.workMoneyGained)}<br />
{numeralWrapper.formatExp(this.workHackExpGained)} hacking experience <br /> {numeralWrapper.formatExp(this.workHackExpGained)} hacking experience <br />
{numeralWrapper.formatExp(this.workStrExpGained)} strength experience<br /> {numeralWrapper.formatExp(this.workStrExpGained)} strength experience<br />
{numeralWrapper.formatExp(this.workDefExpGained)} defense experience<br /> {numeralWrapper.formatExp(this.workDefExpGained)} defense experience<br />
@@ -1589,18 +1589,19 @@ export function regenerateHp(amt) {
} }
export function hospitalize() { export function hospitalize() {
const cost = getHospitalizationCost(this);
if (Settings.SuppressHospitalizationPopup === false) { if (Settings.SuppressHospitalizationPopup === false) {
dialogBoxCreate(<> dialogBoxCreate(<>
You were in critical condition! You were taken to the hospital where You were in critical condition! You were taken to the hospital where
luckily they were able to save your life. You were charged&nbsp; luckily they were able to save your life. You were charged&nbsp;
{Money(this.max_hp * CONSTANTS.HospitalCostPerHp)} {Money(cost)}
</>); </>);
} }
const cost = this.max_hp * CONSTANTS.HospitalCostPerHp
this.loseMoney(cost); this.loseMoney(cost);
this.recordMoneySource(-1 * cost, "hospitalization"); this.recordMoneySource(-1 * cost, "hospitalization");
this.hp = this.max_hp; this.hp = this.max_hp;
return cost;
} }
/********* Company job application **********/ /********* Company job application **********/
@@ -2334,7 +2335,10 @@ export function giveExploit(exploit) {
} }
} }
export function getIntelligenceBonus(weight) { export function getIntelligenceBonus(weight) {
return calculateIntelligenceBonus(this.intelligence, weight); return calculateIntelligenceBonus(this.intelligence, weight);
}
export function getCasinoWinnings() {
return this.moneySourceA.casino;
} }

View File

@@ -20,6 +20,11 @@ interface IDefaultSettings {
*/ */
CodeInstructionRunTime: number; CodeInstructionRunTime: number;
/**
* Render city as list of buttons.
*/
DisableASCIIArt: boolean;
/** /**
* Whether global keyboard shortcuts should be recognized throughout the game. * Whether global keyboard shortcuts should be recognized throughout the game.
*/ */
@@ -101,6 +106,7 @@ interface ISettings extends IDefaultSettings {
const defaultSettings: IDefaultSettings = { const defaultSettings: IDefaultSettings = {
AutosaveInterval: 60, AutosaveInterval: 60,
CodeInstructionRunTime: 50, CodeInstructionRunTime: 50,
DisableASCIIArt: false,
DisableHotkeys: false, DisableHotkeys: false,
Locale: "en", Locale: "en",
MaxLogCapacity: 50, MaxLogCapacity: 50,
@@ -119,6 +125,7 @@ const defaultSettings: IDefaultSettings = {
export const Settings: ISettings & ISelfInitializer & ISelfLoading = { export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
AutosaveInterval: defaultSettings.AutosaveInterval, AutosaveInterval: defaultSettings.AutosaveInterval,
CodeInstructionRunTime: 25, CodeInstructionRunTime: 25,
DisableASCIIArt: defaultSettings.DisableASCIIArt,
DisableHotkeys: defaultSettings.DisableHotkeys, DisableHotkeys: defaultSettings.DisableHotkeys,
Editor: EditorSetting.Ace, Editor: EditorSetting.Ace,
EditorKeybinding: AceKeybindingSetting.Ace, EditorKeybinding: AceKeybindingSetting.Ace,

View File

@@ -574,7 +574,7 @@ let Terminal = {
if (!isHacknet) { post("Required hacking skill: " + currServ.requiredHackingSkill); } if (!isHacknet) { post("Required hacking skill: " + currServ.requiredHackingSkill); }
post("Server security level: " + numeralWrapper.formatServerSecurity(currServ.hackDifficulty)); post("Server security level: " + numeralWrapper.formatServerSecurity(currServ.hackDifficulty));
post("Chance to hack: " + numeralWrapper.formatPercentage(calculateHackingChance(currServ, Player))); post("Chance to hack: " + numeralWrapper.formatPercentage(calculateHackingChance(currServ, Player)));
post("Time to hack: " + convertTimeMsToTimeElapsedString(calculateHackingTime(currServ)*1000)); post("Time to hack: " + convertTimeMsToTimeElapsedString(calculateHackingTime(currServ, Player)*1000));
postElement(<>Total money available on server: {Money(currServ.moneyAvailable)}</>); postElement(<>Total money available on server: {Money(currServ.moneyAvailable)}</>);
if (!isHacknet) { post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired); } if (!isHacknet) { post("Required number of open ports for NUKE: " + currServ.numOpenPortsRequired); }
@@ -2139,23 +2139,11 @@ let Terminal = {
const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement*30) const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement*30)
const fulfilled = Player.augmentations.length >= numAugReq && const fulfilled = Player.augmentations.length >= numAugReq &&
Player.money.gt(1e11) && Player.money.gt(1e11) &&
((Player.hacking_skill >= 2500)|| Player.hacking_skill >= 2500;
(Player.strength >= 1500 &&
Player.defense >= 1500 &&
Player.dexterity >= 1500 &&
Player.agility >= 1500));
if(!fulfilled) { if(!fulfilled) {
post(`Augmentations: ${Player.augmentations.length} / ${numAugReq}`); post(`Augmentations: ${Player.augmentations.length} / ${numAugReq}`);
postElement(<>Money: {Money(Player.money.toNumber())} / {Money(1e11)}</>); postElement(<>Money: {Money(Player.money.toNumber())} / {Money(1e11)}</>);
post("One path below must be fulfilled...");
post("----------HACKING PATH----------");
post(`Hacking skill: ${Player.hacking_skill} / 2500`); post(`Hacking skill: ${Player.hacking_skill} / 2500`);
post("----------COMBAT PATH----------");
post(`Strength: ${Player.strength} / 1500`);
post(`Defense: ${Player.defense} / 1500`);
post(`Dexterity: ${Player.dexterity} / 1500`);
post(`Agility: ${Player.agility} / 1500`);
return; return;
} }

View File

@@ -612,8 +612,7 @@ const Engine = {
})); }));
Engine.Display.factionsContent.appendChild(createElement("p", { Engine.Display.factionsContent.appendChild(createElement("p", {
width:"70%", width:"70%",
innerText:"Lists factions you have been invited to, as well as " + innerText:"Lists factions you have been invited to. You can accept " +
"factions you have previously rejected. You can accept " +
"these faction invitations at any time." "these faction invitations at any time."
})); }));
var invitationsList = createElement("ul"); var invitationsList = createElement("ul");

View File

@@ -274,16 +274,16 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="infiltration-left-panel"> <div id="infiltration-left-panel">
<p id="infiltration-level-text"> </p> <p id="infiltration-level-text"> </p>
<div id="infiltration-buttons"> <div id="infiltration-buttons">
<a class="a-link-button tooltip" id="infiltration-kill"> </a> <button class="a-link-button tooltip" id="infiltration-kill"> </button>
<a class="a-link-button tooltip" id="infiltration-knockout"> </a> <button class="a-link-button tooltip" id="infiltration-knockout"> </button>
<a class="a-link-button tooltip" id="infiltration-stealthknockout"> </a> <button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
<a class="a-link-button tooltip" id="infiltration-assassinate"> </a> <button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
<a class="a-link-button tooltip" id="infiltration-hacksecurity"> </a> <button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-destroysecurity"> </a> <button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
<a class="a-link-button tooltip" id="infiltration-sneak"> </a> <button class="a-link-button tooltip" id="infiltration-sneak"> </button>
<a class="a-link-button tooltip" id="infiltration-pickdoor"> </a> <button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
<a class="a-link-button tooltip" id="infiltration-bribe"> </a> <button class="a-link-button tooltip" id="infiltration-bribe"> </button>
<a class="a-link-button tooltip" id="infiltration-escape"> </a> <button class="a-link-button tooltip" id="infiltration-escape"> </button>
</div> </div>
</div> </div>
<div id="infiltration-right-panel"> <div id="infiltration-right-panel">
@@ -400,7 +400,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<!-- Game Options --> <!-- Game Options -->
<div id="game-options-container" class="popup-box-container"> <div id="game-options-container" class="popup-box-container">
<div id="game-options-content" class="game-options-box"> <div id="game-options-content" class="game-options-box">
<button id="game-options-close-button">&times;</button> <button id="game-options-close-button" aria-label="close options dialog">&times;</button>
<h1> Game Options </h1> <h1> Game Options </h1>
<br /> <br />
<div id="game-options-left-panel"> <div id="game-options-left-panel">
@@ -524,6 +524,16 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys"> <input class="optionCheckbox" type="checkbox" name="settingsDisableHotkeys" id="settingsDisableHotkeys">
</fieldset> </fieldset>
<!-- View city as list of buttons instead of ASCII art. -->
<fieldset>
<label for="settingsDisableASCIIArt" class="tooltip">Disable ASCII art:
<span class="tooltiptexthigh">
If this is set all ASCII art will be disabled.
</span>
</label>
<input class="optionCheckbox" type="checkbox" name="settingsDisableASCIIArt" id="settingsDisableASCIIArt">
</fieldset>
<!-- Locale for displaying numbers --> <!-- Locale for displaying numbers -->
<fieldset> <fieldset>
<label for="settingsLocale" class="tooltip">Locale: <label for="settingsLocale" class="tooltip">Locale:
@@ -562,6 +572,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="game-options-right-panel"> <div id="game-options-right-panel">
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank"> Changelog </a>
<a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a> <a class="a-link-button" href="https://bitburner.readthedocs.io/en/latest/index.html" target="_blank">Documentation</a>
<a class="a-link-button" href="https://discord.gg/TFc3hKD" target="_blank">Discord</a>
<a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a> <a class="a-link-button" href="https://www.reddit.com/r/bitburner" target="_blank">Subreddit</a>
<button id="save-game-link" class="a-link-button"> Save Game </button> <button id="save-game-link" class="a-link-button"> Save Game </button>
<button id="delete-game-link" class="a-link-button"> Delete Game </button> <button id="delete-game-link" class="a-link-button"> Delete Game </button>

View File

@@ -40,9 +40,9 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
function Hacknet(): React.ReactElement { function Hacknet(): React.ReactElement {
// Can't import HacknetHelpers for some reason. // Can't import HacknetHelpers for some reason.
if(!(p.bitNodeN === 9 || SourceFileFlags[9] > 0)) { if(!(p.bitNodeN === 9 || SourceFileFlags[9] > 0)) {
return <><span>Hacknet Nodes owned: {p.hacknetNodes.length}</span><br /></> return <><span>{`Hacknet Nodes owned: ${p.hacknetNodes.length}</span>`}</span><br /></>
} else { } else {
return <><span>Hacknet Servers owned: {p.hacknetNodes.length} / {HacknetServerConstants.MaxServers}</span><br /></> return <><span>{`Hacknet Servers owned: ${p.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`}</span><br /></>
} }
} }
@@ -59,6 +59,7 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
if (src.hospitalization) { parts.push([`Hospitalization:`, Money(src.hospitalization)]) }; if (src.hospitalization) { parts.push([`Hospitalization:`, Money(src.hospitalization)]) };
if (src.infiltration) { parts.push([`Infiltration:`, Money(src.infiltration)]) }; if (src.infiltration) { parts.push([`Infiltration:`, Money(src.infiltration)]) };
if (src.stock) { parts.push([`Stock Market:`, Money(src.stock)]) }; if (src.stock) { parts.push([`Stock Market:`, Money(src.stock)]) };
if (src.casino) { parts.push([`Casino:`, Money(src.casino)]) };
return StatsTable(parts, ""); return StatsTable(parts, "");
} }
@@ -97,8 +98,8 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<table> <table>
<tbody> <tbody>
{props.rows.map((r: any) => <tr key={r[0]}> {props.rows.map((r: any) => <tr key={r[0]}>
<td key='0'>{r[0]} multiplier: </td> <td key='0'>{`${r[0]} multiplier:`}</td>
<td key='1' style={{textAlign: 'right'}}>{numeralWrapper.formatPercentage(r[1])}</td> <td key='1' style={{textAlign: 'right', paddingLeft: '5px'}}>{numeralWrapper.formatPercentage(r[1])}</td>
{bn5Stat(r)} {bn5Stat(r)}
</tr>)} </tr>)}
</tbody> </tbody>
@@ -234,9 +235,9 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
]} /><br /><br /> ]} /><br /><br />
<b>Misc.</b><br /><br /> <b>Misc.</b><br /><br />
<span>Servers owned: {p.purchasedServers.length} / {getPurchaseServerLimit()}</span><br /> <span>{`Servers owned: ${p.purchasedServers.length} / ${getPurchaseServerLimit()}`}</span><br />
<Hacknet /> <Hacknet />
<span>Augmentations installed: {p.augmentations.length}</span><br /><br /> <span>{`Augmentations installed: ${p.augmentations.length}`}</span><br /><br />
{StatsTable(timeRows, null)} {StatsTable(timeRows, null)}
<br /> <br />
<CurrentBitNode /> <CurrentBitNode />

View File

@@ -23,6 +23,7 @@ function setSettingsLabels() {
const suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup"); const suppressHospitalizationPopup = document.getElementById("settingsSuppressHospitalizationPopup");
const autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel"); const autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel");
const disableHotkeys = document.getElementById("settingsDisableHotkeys"); const disableHotkeys = document.getElementById("settingsDisableHotkeys");
const disableASCIIArt = document.getElementById("settingsDisableASCIIArt");
const locale = document.getElementById("settingsLocale"); const locale = document.getElementById("settingsLocale");
//Initialize values on labels //Initialize values on labels
@@ -36,6 +37,7 @@ function setSettingsLabels() {
suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup; suppressHospitalizationPopup.checked = Settings.SuppressHospitalizationPopup;
setAutosaveLabel(autosaveInterval); setAutosaveLabel(autosaveInterval);
disableHotkeys.checked = Settings.DisableHotkeys; disableHotkeys.checked = Settings.DisableHotkeys;
disableASCIIArt.checked = Settings.CityListView;
locale.value = Settings.Locale; locale.value = Settings.Locale;
numeralWrapper.updateLocale(Settings.Locale); //Initialize locale numeralWrapper.updateLocale(Settings.Locale); //Initialize locale
@@ -99,6 +101,10 @@ function setSettingsLabels() {
Settings.DisableHotkeys = this.checked; Settings.DisableHotkeys = this.checked;
} }
disableASCIIArt.onclick = function() {
Settings.DisableASCIIArt = this.checked;
}
//Locale selector //Locale selector
locale.onchange = function() { locale.onchange = function() {
if (!numeralWrapper.updateLocale(locale.value)) { if (!numeralWrapper.updateLocale(locale.value)) {

View File

@@ -22,6 +22,7 @@ export class MoneySourceTracker {
stock: number = 0; stock: number = 0;
total: number = 0; total: number = 0;
work: number = 0; work: number = 0;
casino: number = 0;
[key: string]: number | Function; [key: string]: number | Function;

View File

@@ -134,7 +134,9 @@ export function yesNoTxtInpBoxGetInput(): string {
export function yesNoTxtInpBoxCreate(txt: string | JSX.Element) { export function yesNoTxtInpBoxCreate(txt: string | JSX.Element) {
yesNoBoxOpen = true; yesNoBoxOpen = true;
if (yesNoTextInputBoxTextElement) { if (yesNoTextInputBoxTextElement) {
yesNoTextInputBoxTextElement.innerHTML = '';
if(typeof txt === 'string') { if(typeof txt === 'string') {
yesNoTextInputBoxTextElement.innerHTML = txt; yesNoTextInputBoxTextElement.innerHTML = txt;
} else { } else {