Stock transactions can now influence forecast in addition to price. Several more minor bug/UI fixes

This commit is contained in:
danielyxie 2019-05-01 15:20:14 -07:00
parent 8726946d4a
commit 585e1ac7aa
18 changed files with 155 additions and 61 deletions

@ -73,6 +73,14 @@ be sold at $98.01, and so on.
This is an important concept to keep in mind if you are trying to purchase/sell a
large number of shares, as **it can negatively affect your profits**.
Transactions Influencing Stock Forecast
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to influencing stock price, buying or selling a large number of shares
of a stock will also influence that stock's "forecast". The forecast is the likelihood
that the stock will increase or decrease in price. The magnitude of this effect depends
on the number of shares being transacted. More shares will have a bigger effect on the
stock forecast.
.. _gameplay_stock_market_order_types:
Order Types

@ -27,6 +27,7 @@ secrets that you've been searching for.
Script Editors <scripteditors>
Game Frozen or Stuck? <gamefrozen>
Guides & Tips <guidesandtips>
Tools & Resources <toolsandresources>
Changelog <changelog>
Donate <https://paypal.me/danielyxie>

@ -0,0 +1,23 @@
Tools & Resources
=================
Official Script Repository
--------------------------
There are plans to create an official repository of Bitburner scripts. As of right now,
this is not a priority and has not been started. However, if you'd like
to contribute scripts now, you can find the repository
`here <https://github.com/bitburner-official/bitburner-scripts>`_ and submit pull requests.
Visual Studio Code Extension
----------------------------
One user created a Bitburner extension for the Visual Studio Code (VSCode) editor.
This extension includes several features such as:
* Dynamic RAM calculation
* RAM Usage breakdown
* Typescript definition files with jsdoc comments
* Netscript syntax highlighting
You can find more information and download links
`on this reddit post <https://www.reddit.com/r/Bitburner/comments/bh48y2/visual_studio_code_ram_calculator_extra/>`_.

@ -2105,17 +2105,6 @@ function augmentationExists(name) {
return Augmentations.hasOwnProperty(name);
}
//Used for testing balance
function giveAllAugmentations() {
for (var name in Augmentations) {
var aug = Augmentations[name];
if (aug == null) {continue;}
var ownedAug = new PlayerOwnedAugmentation(name);
Player.augmentations.push(ownedAug);
}
Player.reapplyAllAugmentations();
}
function displayAugmentationsContent(contentEl) {
removeChildrenFromElement(contentEl);
contentEl.appendChild(createElement("h1", {

@ -205,6 +205,7 @@ export class CodingContract {
}
},
placeholder: "Enter Solution here",
width: "50%",
}) as HTMLInputElement;
solveBtn = createElement("a", {
class: "a-link-button",

@ -278,7 +278,7 @@ export let CONSTANTS: IMap<any> = {
v0.47.0
* Stock Market changes:
** Implemented spread. Stock's now have bid and ask prices at which transactions occur
** Large transactions will now influence a stock's price.
** Large transactions will now influence a stock's price and forecast
** This "influencing" can take effect in the middle of a transaction
** See documentation for more details on these changes
** Added getStockAskPrice(), getStockBidPrice() Netscript functions to the TIX API
@ -286,12 +286,16 @@ export let CONSTANTS: IMap<any> = {
* Re-sleeves can no longer have the NeuroFlux Governor augmentation
** This is just a temporary patch until the mechanic gets re-worked
* Corporation employees no longer have an "age" stat
* Bug Fix: Corporation employees stats should no longer become negative
* Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios
* Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server
* Bug Fix: Duplicate Sleeves now properly have access to all Augmentations if you have a gang
* Bug Fix: getAugmentationsFromFaction() & purchaseAugmentation() functions should now work properly if you have a gang
* Bug Fix: Fixed issue that caused messages (.msg) to be sent when refreshing/reloading the game
* Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes
* Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads
* Bug Fix: Faction UI should now automatically update reputation
`
}

@ -30,10 +30,24 @@ const infoStyleMarkup = {
}
export class Info extends React.Component<IProps, any> {
render() {
constructor(props: IProps) {
super(props);
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
this.getFavorGainText = this.getFavorGainText.bind(this);
this.getReputationText = this.getReputationText.bind(this);
}
getFavorGainText(): string {
const favorGain = this.props.faction.getFavorGain()[0];
return `You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`
}
getReputationText(): string {
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
return `Reputation: ${formattedRep}`
}
render() {
const favorTooltip = "Faction favor increases the rate at which you earn reputation for " +
"this faction by 1% per favor. Faction favor is gained whenever you " +
"reset after installing an Augmentation. The amount of " +
@ -51,8 +65,8 @@ export class Info extends React.Component<IProps, any> {
<p style={blockStyleMarkup}>-------------------------</p>
<AutoupdatingParagraph
intervalTime={5e3}
text={`Reputation: ${formattedRep}`}
tooltip={`You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`}
getText={this.getReputationText}
getTooltip={this.getFavorGainText}
/>
<p style={blockStyleMarkup}>-------------------------</p>
<ParagraphWithTooltip

@ -158,14 +158,14 @@ export function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
//Run a script from inside a script using run() command
export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
//Check if the script is already running
var runningScriptObj = findRunningScript(scriptname, args, server);
let runningScriptObj = findRunningScript(scriptname, args, server);
if (runningScriptObj != null) {
workerScript.scriptRef.log(scriptname + " is already running on " + server.hostname);
return Promise.resolve(false);
}
//'null/undefined' arguments are not allowed
for (var i = 0; i < args.length; ++i) {
for (let i = 0; i < args.length; ++i) {
if (args[i] == null) {
workerScript.scriptRef.log("ERROR: Cannot execute a script with null/undefined as an argument");
return Promise.resolve(false);
@ -191,10 +191,10 @@ export function runScriptFromScript(server, scriptname, args, workerScript, thre
return Promise.resolve(false);
} else {
//Able to run script
if(workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) {
workerScript.scriptRef.log("Running script: " + scriptname + " on " + server.hostname + " with " + threads + " threads and args: " + arrayToString(args) + ". May take a few seconds to start up...");
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.exec == null && workerScript.disableLogs.run == null && workerScript.disableLogs.spawn == null) {
workerScript.scriptRef.log(`Running script: ${scriptname} on ${server.hostname} with ${threads} threads and args: ${arrayToString(args)}. May take a few seconds to start up...`);
}
var runningScriptObj = new RunningScript(script, args);
let runningScriptObj = new RunningScript(script, args);
runningScriptObj.threads = threads;
addWorkerScript(runningScriptObj, server);

@ -3692,17 +3692,26 @@ function NetscriptFunctions(workerScript) {
}
}
if (!factionExists(facname)) {
const fac = Factions[facname];
if (!(fac instanceof Faction)) {
workerScript.scriptRef.log("ERROR: getAugmentationsFromFaction() failed. Invalid faction name passed in (this is case-sensitive): " + facname);
return [];
}
var fac = Factions[facname];
var res = [];
for (var i = 0; i < fac.augmentations.length; ++i) {
res.push(fac.augmentations[i]);
// If player has a gang with this faction, return all factions
if (Player.hasGangWith(facname)) {
const res = [];
for (const augName in Augmentations) {
const aug = Augmentations[augName];
if (!aug.isSpecial) {
res.push(augName);
}
}
return res;
}
return res;
return fac.augmentations.slice();
},
getAugmentationPrereq : function(name) {
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
@ -3762,50 +3771,58 @@ function NetscriptFunctions(workerScript) {
}
}
var fac = Factions[faction];
if (fac == null || !(fac instanceof Faction)) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because of invalid faction name: " + faction);
const fac = Factions[faction];
if (!(fac instanceof Faction)) {
workerScript.log("ERROR: purchaseAugmentation() failed because of invalid faction name: " + faction);
return false;
}
if (!fac.augmentations.includes(name)) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because the faction " + faction + " does not contain the " + name + " augmentation");
let augs = [];
if (Player.hasGangWith(faction)) {
for (const augName in Augmentations) {
const tempAug = Augmentations[augName];
if (!tempAug.isSpecial) {
augs.push(augName);
}
}
} else {
augs = fac.augmentations;
}
if (!augs.includes(name)) {
workerScript.log("ERROR: purchaseAugmentation() failed because the faction " + faction + " does not contain the " + name + " augmentation");
return false;
}
var aug = Augmentations[name];
if (aug == null || !(aug instanceof Augmentation)) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because of invalid augmentation name: " + name);
const aug = Augmentations[name];
if (!(aug instanceof Augmentation)) {
workerScript.log("ERROR: purchaseAugmentation() failed because of invalid augmentation name: " + name);
return false;
}
var isNeuroflux = false;
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
isNeuroflux = true;
}
let isNeuroflux = (aug.name === AugmentationNames.NeuroFluxGovernor);
if (!isNeuroflux) {
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === aug.name) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because you already have " + name);
workerScript.log("ERROR: purchaseAugmentation() failed because you already have " + name);
return false;
}
}
for (var j = 0; j < Player.augmentations.length; ++j) {
for (let j = 0; j < Player.augmentations.length; ++j) {
if (Player.augmentations[j].name === aug.name) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because you already have " + name);
workerScript.log("ERROR: purchaseAugmentation() failed because you already have " + name);
return false;
}
}
}
if (fac.playerReputation < aug.baseRepRequirement) {
workerScript.scriptRef.log("ERROR: purchaseAugmentation() failed because you do not have enough reputation with " + fac.name);
workerScript.log("ERROR: purchaseAugmentation() failed because you do not have enough reputation with " + fac.name);
return false;
}
var res = purchaseAugmentation(aug, fac, true);
workerScript.scriptRef.log(res);
workerScript.log(res);
if (isString(res) && res.startsWith("You purchased")) {
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
return true;

@ -136,6 +136,7 @@ export interface IPlayer {
getUpgradeHomeRamCost(): number;
gotoLocation(to: LocationName): boolean;
hasCorporation(): boolean;
hasGangWith(facName: string): boolean;
hasTorRouter(): boolean;
inBladeburner(): boolean;
inGang(): boolean;

@ -23,7 +23,11 @@ export function getGangFaction() {
}
export function getGangName() {
return this.gang.facName;
return this.inGang() ? this.gang.facName : "";
}
export function hasGangWith(facName) {
return this.inGang() && this.gang.facName === facName;
}
export function inGang() {

@ -424,7 +424,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
//designated server, and false otherwise
export function findRunningScript(filename, args, server) {
for (var i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].filename == filename &&
if (server.runningScripts[i].filename === filename &&
compareArrays(server.runningScripts[i].args, args)) {
return server.runningScripts[i];
}

@ -206,6 +206,7 @@ export function stockMarketCycle() {
if (stock.b) { thresh = 0.4; }
if (Math.random() < thresh) {
stock.b = !stock.b;
if (stock.otlkMag < 10) { stock.otlkMag += 0.4; }
}
}
}

@ -2,6 +2,9 @@ import { Stock } from "./Stock";
import { PositionTypes } from "./data/PositionTypes";
import { CONSTANTS } from "../Constants";
// Amount by which a stock's forecast changes during each price movement
const forecastChangePerPriceMovement = 0.4;
/**
* Given a stock, calculates the amount by which the stock price is multiplied
* for an 'upward' price movement. This does not actually increase the stock's price,
@ -86,7 +89,7 @@ export function getBuyTransactionCost(stock: Stock, shares: number, posType: Pos
}
/**
* Processes a buy transaction's resulting price movement.
* Processes a buy transaction's resulting price AND forecast movement.
* @param {Stock} stock - Stock being purchased
* @param {number} shares - Number of shares being transacted
* @param {PositionTypes} posType - Long or short position
@ -125,6 +128,10 @@ export function processBuyTransactionPriceMovement(stock: Stock, shares: number,
stock.price = currPrice;
stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement);
// Forecast always decreases in magnitude
const forecastChange = Math.min(5, forecastChangePerPriceMovement * numIterations);
stock.otlkMag -= forecastChange;
}
/**
@ -228,6 +235,10 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number
stock.price = currPrice;
stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement);
// Forecast always decreases in magnitude
const forecastChange = Math.min(5, forecastChangePerPriceMovement * numIterations);
stock.otlkMag -= forecastChange;
}
/**

@ -25,7 +25,7 @@ export class StockTickerPositionText extends React.Component<IProps, any> {
// Caculate total returns
const totalCost = stock.playerShares * stock.playerAvgPx;
const gains = (stock.price - stock.playerAvgPx) * stock.playerShares;
const gains = (stock.getBidPrice() - stock.playerAvgPx) * stock.playerShares;
let percentageGains = gains / totalCost;
if (isNaN(percentageGains)) { percentageGains = 0; }
@ -56,7 +56,7 @@ export class StockTickerPositionText extends React.Component<IProps, any> {
// Caculate total returns
const totalCost = stock.playerShortShares * stock.playerAvgShortPx;
const gains = (stock.playerAvgShortPx - stock.price) * stock.playerShortShares;
const gains = (stock.playerAvgShortPx - stock.getAskPrice()) * stock.playerShortShares;
let percentageGains = gains / totalCost;
if (isNaN(percentageGains)) { percentageGains = 0; }

@ -8,8 +8,8 @@ import * as React from "react";
interface IProps {
intervalTime?: number;
style?: object;
text: string;
tooltip?: string;
getText: () => string;
getTooltip?: () => string;
}
interface IState {
@ -49,7 +49,14 @@ export class AutoupdatingParagraph extends React.Component<IProps, IState> {
}
render() {
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
let hasTooltip = this.props.getTooltip != null;
let tooltip: string | null;
if (hasTooltip) {
tooltip = this.props.getTooltip!();
if (tooltip === "") {
hasTooltip = false;
}
}
const className = "tooltip";
@ -57,13 +64,13 @@ export class AutoupdatingParagraph extends React.Component<IProps, IState> {
let tooltipMarkup: IInnerHTMLMarkup | null;
if (hasTooltip) {
tooltipMarkup = {
__html: this.props.tooltip!
__html: tooltip!
}
}
return (
<p className={className} style={this.props.style}>
{this.props.text}
{this.props.getText()}
{
hasTooltip &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>

@ -9,11 +9,13 @@ export function arrayToString<T>(a: T[]) {
const vals: any[] = [];
for (let i = 0; i < a.length; ++i) {
let elem: any = a[i];
if (typeof elem === "string") {
if (Array.isArray(elem)) {
elem = arrayToString(elem);
} else if (typeof elem === "string") {
elem = `"${elem}"`;
}
vals.push(elem);
}
return `[${vals.join(", ")}]`;
}

@ -9,8 +9,19 @@ export function compareArrays<T>(a1: T[], a2: T[]) {
return false;
}
for (let i: number = 0; i < a1.length; ++i) {
if (a1[i] !== a2[i]) {
for (let i = 0; i < a1.length; ++i) {
if (Array.isArray(a1[i])) {
// If the other element is not an array, then these cannot be equal
if (!Array.isArray(a2[i])) {
return false;
}
const elem1 = <any[]><any>a1[i];
const elem2 = <any[]><any>a2[i];
if (!compareArrays(elem1, elem2)) {
return false;
}
} else if (a1[i] !== a2[i]) {
return false;
}
}