mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-17 23:08:36 +02:00
bladeburner slowly being converted to typescript, added ScriptHackMoneyGain multiplier which is the money you actually gain from script hacks, not money drained, important for BN8
This commit is contained in:
@@ -353,7 +353,8 @@ export function initBitNodeMultipliers(p: IPlayer) {
|
|||||||
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
|
||||||
break;
|
break;
|
||||||
case 8: // Ghost of Wall Street
|
case 8: // Ghost of Wall Street
|
||||||
BitNodeMultipliers.ScriptHackMoney = 0;
|
BitNodeMultipliers.ScriptHackMoney = 0.3;
|
||||||
|
BitNodeMultipliers.ScriptHackMoneyGain = 0;
|
||||||
BitNodeMultipliers.ManualHackMoney = 0;
|
BitNodeMultipliers.ManualHackMoney = 0;
|
||||||
BitNodeMultipliers.CompanyWorkMoney = 0;
|
BitNodeMultipliers.CompanyWorkMoney = 0;
|
||||||
BitNodeMultipliers.CrimeMoney = 0;
|
BitNodeMultipliers.CrimeMoney = 0;
|
||||||
|
|||||||
@@ -170,6 +170,13 @@ interface IBitNodeMultipliers {
|
|||||||
*/
|
*/
|
||||||
ScriptHackMoney: number;
|
ScriptHackMoney: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of money actually gained when script hack a server. This is
|
||||||
|
* different than the above because you can reduce the amount of money but
|
||||||
|
* not gain that same amount.
|
||||||
|
*/
|
||||||
|
ScriptHackMoneyGain: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Influences the growth percentage per cycle against a server.
|
* Influences the growth percentage per cycle against a server.
|
||||||
*/
|
*/
|
||||||
@@ -233,6 +240,7 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
|
|||||||
HacknetNodeMoney: 1,
|
HacknetNodeMoney: 1,
|
||||||
ManualHackMoney: 1,
|
ManualHackMoney: 1,
|
||||||
ScriptHackMoney: 1,
|
ScriptHackMoney: 1,
|
||||||
|
ScriptHackMoneyGain: 1,
|
||||||
CodingContractMoney: 1,
|
CodingContractMoney: 1,
|
||||||
|
|
||||||
ClassGymExpGain: 1,
|
ClassGymExpGain: 1,
|
||||||
|
|||||||
1207
src/Bladeburner.js
1207
src/Bladeburner.js
File diff suppressed because it is too large
Load Diff
269
src/Bladeburner/Action.ts
Normal file
269
src/Bladeburner/Action.ts
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
import { Player } from "../Player";
|
||||||
|
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||||
|
import { addOffset } from "../../utils/helpers/addOffset";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
|
// import { Contract } from "./Contract";
|
||||||
|
// import { Operation } from "./Operation";
|
||||||
|
// import { BlackOperation } from "./BlackOperation";
|
||||||
|
|
||||||
|
class StatsMultiplier {
|
||||||
|
hack: number = 0;
|
||||||
|
str: number = 0;
|
||||||
|
def: number = 0;
|
||||||
|
dex: number = 0;
|
||||||
|
agi: number = 0;
|
||||||
|
cha: number = 0;
|
||||||
|
int: number = 0;
|
||||||
|
|
||||||
|
[key: string]: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IActionParams {
|
||||||
|
name?: string;
|
||||||
|
desc?: string;
|
||||||
|
level?: number;
|
||||||
|
maxLevel?: number;
|
||||||
|
autoLevel?: boolean;
|
||||||
|
baseDifficulty?: number;
|
||||||
|
difficultyFac?: number;
|
||||||
|
rewardFac?: number;
|
||||||
|
successes?: number;
|
||||||
|
failures?: number;
|
||||||
|
rankGain?: number;
|
||||||
|
rankLoss?: number;
|
||||||
|
hpLoss?: number;
|
||||||
|
hpLost?: number;
|
||||||
|
isStealth?: boolean;
|
||||||
|
isKill?: boolean;
|
||||||
|
count?: number;
|
||||||
|
countGrowth?: number;
|
||||||
|
weights?: StatsMultiplier;
|
||||||
|
decays?: StatsMultiplier;
|
||||||
|
teamCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Action {
|
||||||
|
name: string = "";
|
||||||
|
desc: string = "";
|
||||||
|
|
||||||
|
// Difficulty scales with level. See getDifficulty() method
|
||||||
|
level: number = 1;
|
||||||
|
maxLevel: number = 1;
|
||||||
|
autoLevel: boolean = true;
|
||||||
|
baseDifficulty: number = 100;
|
||||||
|
difficultyFac: number = 1.01;
|
||||||
|
|
||||||
|
// Rank increase/decrease is affected by this exponent
|
||||||
|
rewardFac: number = 1.02;
|
||||||
|
|
||||||
|
successes: number = 0;
|
||||||
|
failures: number = 0;
|
||||||
|
|
||||||
|
// All of these scale with level/difficulty
|
||||||
|
rankGain: number = 0;
|
||||||
|
rankLoss: number = 0;
|
||||||
|
hpLoss: number = 0;
|
||||||
|
hpLost: number = 0;
|
||||||
|
|
||||||
|
// Action Category. Current categories are stealth and kill
|
||||||
|
isStealth: boolean = false;
|
||||||
|
isKill: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of this contract remaining, and its growth rate
|
||||||
|
* Growth rate is an integer and the count will increase by that integer every "cycle"
|
||||||
|
*/
|
||||||
|
count: number = getRandomInt(1e3, 25e3);
|
||||||
|
countGrowth: number = getRandomInt(1, 5);
|
||||||
|
|
||||||
|
// Weighting of each stat in determining action success rate
|
||||||
|
weights: StatsMultiplier = {hack:1/7,str:1/7,def:1/7,dex:1/7,agi:1/7,cha:1/7,int:1/7};
|
||||||
|
// Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1)
|
||||||
|
decays: StatsMultiplier = { hack: 0.9, str: 0.9, def: 0.9, dex: 0.9, agi: 0.9, cha: 0.9, int: 0.9 };
|
||||||
|
teamCount: number = 0;
|
||||||
|
|
||||||
|
// Base Class for Contracts, Operations, and BlackOps
|
||||||
|
constructor(params: IActionParams| null = null) { // | null = null
|
||||||
|
if(params && params.name) this.name = params.name;
|
||||||
|
if(params && params.desc) this.desc = params.desc;
|
||||||
|
|
||||||
|
if(params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||||
|
if(params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
||||||
|
|
||||||
|
if(params && params.rewardFac) this.rewardFac = params.rewardFac;
|
||||||
|
if(params && params.rankGain) this.rankGain = params.rankGain;
|
||||||
|
if(params && params.rankLoss) this.rankLoss = params.rankLoss;
|
||||||
|
if(params && params.hpLoss) this.hpLoss = params.hpLoss;
|
||||||
|
|
||||||
|
if(params && params.isStealth) this.isStealth = params.isStealth;
|
||||||
|
if(params && params.isKill) this.isKill = params.isKill;
|
||||||
|
|
||||||
|
if(params && params.count) this.count = params.count;
|
||||||
|
if(params && params.countGrowth) this.countGrowth = params.countGrowth;
|
||||||
|
|
||||||
|
if(params && params.weights) this.weights = params.weights;
|
||||||
|
if(params && params.decays) this.decays = params.decays;
|
||||||
|
|
||||||
|
// Check to make sure weights are summed properly
|
||||||
|
let sum = 0;
|
||||||
|
for (const weight in this.weights) {
|
||||||
|
if (this.weights.hasOwnProperty(weight)) {
|
||||||
|
sum += this.weights[weight];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sum - 1 >= 10 * Number.EPSILON) {
|
||||||
|
throw new Error("Invalid weights when constructing Action " + this.name +
|
||||||
|
". The weights should sum up to 1. They sum up to :" + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const decay in this.decays) {
|
||||||
|
if (this.decays.hasOwnProperty(decay)) {
|
||||||
|
if (this.decays[decay] > 1) {
|
||||||
|
throw new Error("Invalid decays when constructing " +
|
||||||
|
"Action " + this.name + ". " +
|
||||||
|
"Decay value cannot be greater than 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDifficulty(): number {
|
||||||
|
const difficulty = this.baseDifficulty * Math.pow(this.difficultyFac, this.level-1);
|
||||||
|
if (isNaN(difficulty)) {throw new Error("Calculated NaN in Action.getDifficulty()");}
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for success. Should be called when an action has completed
|
||||||
|
* @param inst {Bladeburner} - Bladeburner instance
|
||||||
|
*/
|
||||||
|
attempt(inst: any): boolean {
|
||||||
|
return (Math.random() < this.getSuccessChance(inst));
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be implemented by subtypes
|
||||||
|
getActionTimePenalty(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionTime(inst: any): number {
|
||||||
|
const difficulty = this.getDifficulty();
|
||||||
|
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
|
||||||
|
const skillFac = inst.skillMultipliers.actionTime; // Always < 1
|
||||||
|
|
||||||
|
const effAgility = Player.agility * inst.skillMultipliers.effAgi;
|
||||||
|
const effDexterity = Player.dexterity * inst.skillMultipliers.effDex;
|
||||||
|
const statFac = 0.5 * (Math.pow(effAgility, BladeburnerConstants.EffAgiExponentialFactor) +
|
||||||
|
Math.pow(effDexterity, BladeburnerConstants.EffDexExponentialFactor) +
|
||||||
|
(effAgility / BladeburnerConstants.EffAgiLinearFactor) +
|
||||||
|
(effDexterity / BladeburnerConstants.EffDexLinearFactor)); // Always > 1
|
||||||
|
|
||||||
|
baseTime = Math.max(1, baseTime * skillFac / statFac);
|
||||||
|
|
||||||
|
return Math.ceil(baseTime*this.getActionTimePenalty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// For actions that have teams. To be implemented by subtypes.
|
||||||
|
getTeamSuccessBonus(inst: any): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionTypeSkillSuccessBonus(inst: any): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChaosCompetencePenalty(inst: any, params: any): number {
|
||||||
|
const city = inst.getCurrentCity();
|
||||||
|
if (params.est) {
|
||||||
|
return Math.pow((city.popEst / BladeburnerConstants.PopulationThreshold), BladeburnerConstants.PopulationExponent);
|
||||||
|
} else {
|
||||||
|
return Math.pow((city.pop / BladeburnerConstants.PopulationThreshold), BladeburnerConstants.PopulationExponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChaosDifficultyBonus(inst: any, params: any): number {
|
||||||
|
const city = inst.getCurrentCity();
|
||||||
|
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||||
|
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||||
|
const mult = Math.pow(diff, 0.1);
|
||||||
|
return mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inst - Bladeburner Object
|
||||||
|
* @params - options:
|
||||||
|
* est (bool): Get success chance estimate instead of real success chance
|
||||||
|
*/
|
||||||
|
getSuccessChance(inst: any, params: any={}) {
|
||||||
|
if (inst == null) {throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");}
|
||||||
|
let difficulty = this.getDifficulty();
|
||||||
|
let competence = 0;
|
||||||
|
for (let stat in this.weights) {
|
||||||
|
if (this.weights.hasOwnProperty(stat)) {
|
||||||
|
let playerStatLvl = Player.queryStatFromString(stat);
|
||||||
|
let key = "eff" + stat.charAt(0).toUpperCase() + stat.slice(1);
|
||||||
|
let effMultiplier = inst.skillMultipliers[key];
|
||||||
|
if (effMultiplier == null) {
|
||||||
|
console.error(`Failed to find Bladeburner Skill multiplier for: ${stat}`);
|
||||||
|
effMultiplier = 1;
|
||||||
|
}
|
||||||
|
competence += (this.weights[stat] * Math.pow(effMultiplier*playerStatLvl, this.decays[stat]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
competence *= inst.calculateStaminaPenalty();
|
||||||
|
|
||||||
|
competence *= this.getTeamSuccessBonus(inst);
|
||||||
|
|
||||||
|
competence *= this.getChaosCompetencePenalty(inst, params);
|
||||||
|
difficulty *= this.getChaosDifficultyBonus(inst, params);
|
||||||
|
|
||||||
|
if(this.name == "Raid" && inst.getCurrentCity().comms <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factor skill multipliers into success chance
|
||||||
|
competence *= inst.skillMultipliers.successChanceAll;
|
||||||
|
competence *= this.getActionTypeSkillSuccessBonus(inst);
|
||||||
|
if (this.isStealth) {
|
||||||
|
competence *= inst.skillMultipliers.successChanceStealth;
|
||||||
|
}
|
||||||
|
if (this.isKill) {
|
||||||
|
competence *= inst.skillMultipliers.successChanceKill;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Augmentation multiplier
|
||||||
|
competence *= Player.bladeburner_success_chance_mult;
|
||||||
|
|
||||||
|
if (isNaN(competence)) {throw new Error("Competence calculated as NaN in Action.getSuccessChance()");}
|
||||||
|
return Math.min(1, competence / difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number {
|
||||||
|
return Math.ceil((0.5) * (this.maxLevel) * (2 * baseSuccessesPerLevel + (this.maxLevel-1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaxLevel(baseSuccessesPerLevel: number): void {
|
||||||
|
if (this.successes >= this.getSuccessesNeededForNextLevel(baseSuccessesPerLevel)) {
|
||||||
|
++this.maxLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(value: any): Action {
|
||||||
|
return Generic_fromJSON(Action, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): any {
|
||||||
|
return Generic_toJSON("Action", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reviver.constructors.Action = Action;
|
||||||
33
src/Bladeburner/BlackOperation.ts
Normal file
33
src/Bladeburner/BlackOperation.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Operation, IOperationParams } from "./Operation";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
|
||||||
|
export class BlackOperation extends Operation {
|
||||||
|
constructor(params: IOperationParams | null = null) {
|
||||||
|
super(params);
|
||||||
|
this.count = 1;
|
||||||
|
this.countGrowth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be implemented by subtypes
|
||||||
|
getActionTimePenalty(): number {
|
||||||
|
return 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChaosCompetencePenalty(inst: any, params: any): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChaosDifficultyBonus(inst: any, params: any): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(value: any): Operation {
|
||||||
|
return Generic_fromJSON(BlackOperation, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): any {
|
||||||
|
return Generic_toJSON("BlackOperation", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.BlackOperation = BlackOperation;
|
||||||
345
src/Bladeburner/BlackOperations.ts
Normal file
345
src/Bladeburner/BlackOperations.ts
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
import { BlackOperation } from "./BlackOperation";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const BlackOperations: IMap<BlackOperation> = {};
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
BlackOperations["Operation Typhoon"] = new BlackOperation({
|
||||||
|
name:"Operation Typhoon",
|
||||||
|
desc:"Obadiah Zenyatta is the leader of a RedWater PMC. It has long " +
|
||||||
|
"been known among the intelligence community that Zenyatta, along " +
|
||||||
|
"with the rest of the PMC, is a Synthoid.<br><br>" +
|
||||||
|
"The goal of Operation Typhoon is to find and eliminate " +
|
||||||
|
"Zenyatta and RedWater by any means necessary. After the task " +
|
||||||
|
"is completed, the actions must be covered up from the general public.",
|
||||||
|
baseDifficulty:2000, reqdRank:2.5e3,
|
||||||
|
rankGain:50, rankLoss:10, hpLoss:100,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Zero"] = new BlackOperation({
|
||||||
|
name:"Operation Zero",
|
||||||
|
desc:"AeroCorp is one of the world's largest defense contractors. " +
|
||||||
|
"It's leader, Steve Watataki, is thought to be a supporter of " +
|
||||||
|
"Synthoid rights. He must be removed.<br><br>" +
|
||||||
|
"The goal of Operation Zero is to covertly infiltrate AeroCorp and " +
|
||||||
|
"uncover any incriminating evidence or " +
|
||||||
|
"information against Watataki that will cause him to be removed " +
|
||||||
|
"from his position at AeroCorp. Incriminating evidence can be " +
|
||||||
|
"fabricated as a last resort. Be warned that AeroCorp has some of " +
|
||||||
|
"the most advanced security measures in the world.",
|
||||||
|
baseDifficulty:2500, reqdRank:5e3,
|
||||||
|
rankGain:60, rankLoss:15, hpLoss:50,
|
||||||
|
weights:{hack:0.2,str:0.15,def:0.15,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isStealth:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation X"] = new BlackOperation({
|
||||||
|
name:"Operation X",
|
||||||
|
desc:"We have recently discovered an underground publication " +
|
||||||
|
"group called Samizdat. Even though most of their publications " +
|
||||||
|
"are nonsensical conspiracy theories, the average human is " +
|
||||||
|
"gullible enough to believe them. Many of their works discuss " +
|
||||||
|
"Synthoids and pose a threat to society. The publications are spreading " +
|
||||||
|
"rapidly in China and other Eastern countries.<br><br>" +
|
||||||
|
"Samizdat has done a good job of keeping hidden and anonymous. " +
|
||||||
|
"However, we've just received intelligence that their base of " +
|
||||||
|
"operations is in Ishima's underground sewer systems. Your task is to " +
|
||||||
|
"investigate the sewer systems, and eliminate Samizdat. They must " +
|
||||||
|
"never publish anything again.",
|
||||||
|
baseDifficulty:3000, reqdRank:7.5e3,
|
||||||
|
rankGain:75, rankLoss:15, hpLoss:100,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Titan"] = new BlackOperation({
|
||||||
|
name:"Operation Titan",
|
||||||
|
desc:"Several months ago Titan Laboratories' Bioengineering department " +
|
||||||
|
"was infiltrated by Synthoids. As far as we know, Titan Laboratories' " +
|
||||||
|
"management has no knowledge about this. We don't know what the " +
|
||||||
|
"Synthoids are up to, but the research that they could " +
|
||||||
|
"be conducting using Titan Laboraties' vast resources is potentially " +
|
||||||
|
"very dangerous.<br><br>" +
|
||||||
|
"Your goal is to enter and destroy the Bioengineering department's " +
|
||||||
|
"facility in Aevum. The task is not just to retire the Synthoids there, but " +
|
||||||
|
"also to destroy any information or research at the facility that " +
|
||||||
|
"is relevant to the Synthoids and their goals.",
|
||||||
|
baseDifficulty:4000, reqdRank:10e3,
|
||||||
|
rankGain:100, rankLoss:20, hpLoss:100,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ares"] = new BlackOperation({
|
||||||
|
name:"Operation Ares",
|
||||||
|
desc:"One of our undercover agents, Agent Carter, has informed us of a " +
|
||||||
|
"massive weapons deal going down in Dubai between rogue Russian " +
|
||||||
|
"militants and a radical Synthoid community. These weapons are next-gen " +
|
||||||
|
"plasma and energy weapons. It is critical for the safety of humanity " +
|
||||||
|
"that this deal does not happen.<br><br>" +
|
||||||
|
"Your task is to intercept the deal. Leave no survivors.",
|
||||||
|
baseDifficulty:5000, reqdRank:12.5e3,
|
||||||
|
rankGain:125, rankLoss:20, hpLoss:200,
|
||||||
|
weights:{hack:0,str:0.25,def:0.25,dex:0.25,agi:0.25,cha:0, int:0},
|
||||||
|
decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Archangel"] = new BlackOperation({
|
||||||
|
name:"Operation Archangel",
|
||||||
|
desc:"Our analysts have discovered that the popular Red Rabbit brothel in " +
|
||||||
|
"Amsterdam is run and 'staffed' by MK-VI Synthoids. Intelligence " +
|
||||||
|
"suggests that the profit from this brothel is used to fund a large " +
|
||||||
|
"black market arms trafficking operation.<br><br>" +
|
||||||
|
"The goal of this operation is to take out the leaders that are running " +
|
||||||
|
"the Red Rabbit brothel. Try to limit the number of other casualties, " +
|
||||||
|
"but do what you must to complete the mission.",
|
||||||
|
baseDifficulty:7500, reqdRank:15e3,
|
||||||
|
rankGain:200, rankLoss:20, hpLoss:25,
|
||||||
|
weights:{hack:0,str:0.2,def:0.2,dex:0.3,agi:0.3,cha:0, int:0},
|
||||||
|
decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Juggernaut"] = new BlackOperation({
|
||||||
|
name:"Operation Juggernaut",
|
||||||
|
desc:"The CIA has just encountered a new security threat. A new " +
|
||||||
|
"criminal group, lead by a shadowy operative who calls himself " +
|
||||||
|
"Juggernaut, has been smuggling drugs and weapons (including " +
|
||||||
|
"suspected bioweapons) into Sector-12. We also have reason " +
|
||||||
|
"to believe the tried to break into one of Universal Energy's " +
|
||||||
|
"facilities in order to cause a city-wide blackout. The CIA " +
|
||||||
|
"suspects that Juggernaut is a heavily-augmented Synthoid, and " +
|
||||||
|
"have thus enlisted our help.<br><br>" +
|
||||||
|
"Your mission is to eradicate Juggernaut and his followers.",
|
||||||
|
baseDifficulty:10e3, reqdRank:20e3,
|
||||||
|
rankGain:300, rankLoss:40, hpLoss:300,
|
||||||
|
weights:{hack:0,str:0.25,def:0.25,dex:0.25,agi:0.25,cha:0, int:0},
|
||||||
|
decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Red Dragon"] = new BlackOperation({
|
||||||
|
name:"Operation Red Dragon",
|
||||||
|
desc:"The Tetrads criminal organization is suspected of " +
|
||||||
|
"reverse-engineering the MK-VI Synthoid design. We believe " +
|
||||||
|
"they altered and possibly improved the design and began " +
|
||||||
|
"manufacturing their own Synthoid models in order to bolster " +
|
||||||
|
"their criminal activities.<br><br>" +
|
||||||
|
"Your task is to infiltrate and destroy the Tetrads' base of operations " +
|
||||||
|
"in Los Angeles. Intelligence tells us that their base houses " +
|
||||||
|
"one of their Synthoid manufacturing units.",
|
||||||
|
baseDifficulty:12.5e3, reqdRank:25e3,
|
||||||
|
rankGain:500, rankLoss:50, hpLoss:500,
|
||||||
|
weights:{hack:0.05,str:0.2,def:0.2,dex:0.25,agi:0.25,cha:0, int:0.05},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation K"] = new BlackOperation({
|
||||||
|
name:"Operation K",
|
||||||
|
desc:"CODE RED SITUATION. Our intelligence tells us that VitaLife " +
|
||||||
|
"has discovered a new android cloning technology. This technology " +
|
||||||
|
"is supposedly capable of cloning Synthoid, not only physically " +
|
||||||
|
"but also their advanced AI modules. We do not believe that " +
|
||||||
|
"VitaLife is trying to use this technology illegally or " +
|
||||||
|
"maliciously, but if any Synthoids were able to infiltrate the " +
|
||||||
|
"corporation and take advantage of this technology then the " +
|
||||||
|
"results would be catastrophic.<br><br>" +
|
||||||
|
"We do not have the power or jurisdiction to shutdown this down " +
|
||||||
|
"through legal or political means, so we must resort to a covert " +
|
||||||
|
"operation. Your goal is to destroy this technology and eliminate " +
|
||||||
|
"anyone who was involved in its creation.",
|
||||||
|
baseDifficulty:15e3, reqdRank:30e3,
|
||||||
|
rankGain:750, rankLoss:60, hpLoss:1000,
|
||||||
|
weights:{hack:0.05,str:0.2,def:0.2,dex:0.25,agi:0.25,cha:0, int:0.05},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Deckard"] = new BlackOperation({
|
||||||
|
name:"Operation Deckard",
|
||||||
|
desc:"Despite your success in eliminating VitaLife's new android-replicating " +
|
||||||
|
"technology in Operation K, we've discovered that a small group of " +
|
||||||
|
"MK-VI Synthoids were able to make off with the schematics and design " +
|
||||||
|
"of the technology before the Operation. It is almost a certainty that " +
|
||||||
|
"these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising." +
|
||||||
|
"The goal of Operation Deckard is to hunt down these Synthoids and retire " +
|
||||||
|
"them. I don't need to tell you how critical this mission is.",
|
||||||
|
baseDifficulty:20e3, reqdRank:40e3,
|
||||||
|
rankGain:1e3, rankLoss:75, hpLoss:200,
|
||||||
|
weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04},
|
||||||
|
decays:{hack:0,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Tyrell"] = new BlackOperation({
|
||||||
|
name:"Operation Tyrell",
|
||||||
|
desc:"A week ago Blade Industries reported a small break-in at one " +
|
||||||
|
"of their Aevum Augmentation storage facitilities. We figured out " +
|
||||||
|
"that The Dark Army was behind the heist, and didn't think any more " +
|
||||||
|
"of it. However, we've just discovered that several known MK-VI Synthoids " +
|
||||||
|
"were part of that break-in group.<br><br>" +
|
||||||
|
"We cannot have Synthoids upgrading their already-enhanced abilities " +
|
||||||
|
"with Augmentations. Your task is to hunt down the associated Dark Army " +
|
||||||
|
"members and eliminate them.",
|
||||||
|
baseDifficulty:25e3, reqdRank:50e3,
|
||||||
|
rankGain:1.5e3, rankLoss:100, hpLoss:500,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Wallace"] = new BlackOperation({
|
||||||
|
name:"Operation Wallace",
|
||||||
|
desc:"Based on information gathered from Operation Tyrell, we've discovered " +
|
||||||
|
"that The Dark Army was well aware that there were Synthoids amongst " +
|
||||||
|
"their ranks. Even worse, we believe that The Dark Army is working " +
|
||||||
|
"together with other criminal organizations such as The Syndicate and " +
|
||||||
|
"that they are planning some sort of large-scale takeover of multiple major " +
|
||||||
|
"cities, most notably Aevum. We suspect that Synthoids have infiltrated " +
|
||||||
|
"the ranks of these criminal factions and are trying to stage another " +
|
||||||
|
"Synthoid uprising.<br><br>" +
|
||||||
|
"The best way to deal with this is to prevent it before it even happens. " +
|
||||||
|
"The goal of Operation Wallace is to destroy the Dark Army and " +
|
||||||
|
"Syndicate factions in Aevum immediately. Leave no survivors.",
|
||||||
|
baseDifficulty:30e3, reqdRank:75e3,
|
||||||
|
rankGain:2e3, rankLoss:150, hpLoss:1500,
|
||||||
|
weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Shoulder of Orion"] = new BlackOperation({
|
||||||
|
name:"Operation Shoulder of Orion",
|
||||||
|
desc:"China's Solaris Space Systems is secretly launching the first " +
|
||||||
|
"manned spacecraft in over a decade using Synthoids. We believe " +
|
||||||
|
"China is trying to establish the first off-world colonies.<br><br>" +
|
||||||
|
"The mission is to prevent this launch without instigating an " +
|
||||||
|
"international conflict. When you accept this mission you will be " +
|
||||||
|
"officially disavowed by the NSA and the national government until after you " +
|
||||||
|
"successfully return. In the event of failure, all of the operation's " +
|
||||||
|
"team members must not let themselves be captured alive.",
|
||||||
|
baseDifficulty:35e3, reqdRank:100e3,
|
||||||
|
rankGain:2.5e3, rankLoss:500, hpLoss:1500,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isStealth:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Hyron"] = new BlackOperation({
|
||||||
|
name:"Operation Hyron",
|
||||||
|
desc:"Our intelligence tells us that Fulcrum Technologies is developing " +
|
||||||
|
"a quantum supercomputer using human brains as core " +
|
||||||
|
"processors. This supercomputer " +
|
||||||
|
"is rumored to be able to store vast amounts of data and " +
|
||||||
|
"perform computations unmatched by any other supercomputer on the " +
|
||||||
|
"planet. But more importantly, the use of organic human brains " +
|
||||||
|
"means that the supercomputer may be able to reason abstractly " +
|
||||||
|
"and become self-aware.<br><br>" +
|
||||||
|
"I do not need to remind you why sentient-level AIs pose a serious " +
|
||||||
|
"thread to all of mankind.<br><br>" +
|
||||||
|
"The research for this project is being conducted at one of Fulcrum " +
|
||||||
|
"Technologies secret facilities in Aevum, codenamed 'Alpha Ranch'. " +
|
||||||
|
"Infiltrate the compound, delete and destroy the work, and then find and kill the " +
|
||||||
|
"project lead.",
|
||||||
|
baseDifficulty:40e3, reqdRank:125e3,
|
||||||
|
rankGain:3e3, rankLoss:1e3, hpLoss:500,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Morpheus"] = new BlackOperation({
|
||||||
|
name:"Operation Morpheus",
|
||||||
|
desc:"DreamSense Technologies is an advertising company that uses " +
|
||||||
|
"special technology to transmit their ads into the peoples " +
|
||||||
|
"dreams and subconcious. They do this using broadcast transmitter " +
|
||||||
|
"towers. Based on information from our agents and informants in " +
|
||||||
|
"Chonqging, we have reason to believe that one of the broadcast " +
|
||||||
|
"towers there has been compromised by Synthoids and is being used " +
|
||||||
|
"to spread pro-Synthoid propaganda.<br><br>" +
|
||||||
|
"The mission is to destroy this broadcast tower. Speed and " +
|
||||||
|
"stealth are of the upmost important for this.",
|
||||||
|
baseDifficulty:45e3, reqdRank:150e3,
|
||||||
|
rankGain:4e3, rankLoss:1e3, hpLoss:100,
|
||||||
|
weights:{hack:0.05,str:0.15,def:0.15,dex:0.3,agi:0.3,cha:0, int:0.05},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isStealth:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ion Storm"] = new BlackOperation({
|
||||||
|
name:"Operation Ion Storm",
|
||||||
|
desc:"Our analysts have uncovered a gathering of MK-VI Synthoids " +
|
||||||
|
"that have taken up residence in the Sector-12 Slums. We " +
|
||||||
|
"don't know if they are rogue Synthoids from the Uprising, " +
|
||||||
|
"but we do know that they have been stockpiling " +
|
||||||
|
"weapons, money, and other resources. This makes them dangerous.<br><br>" +
|
||||||
|
"This is a full-scale assault operation to find and retire all of these " +
|
||||||
|
"Synthoids in the Sector-12 Slums.",
|
||||||
|
baseDifficulty:50e3, reqdRank:175e3,
|
||||||
|
rankGain:5e3, rankLoss:1e3, hpLoss:5000,
|
||||||
|
weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Annihilus"] = new BlackOperation({
|
||||||
|
name:"Operation Annihilus",
|
||||||
|
desc:"Our superiors have ordered us to eradicate everything and everyone " +
|
||||||
|
"in an underground facility located in Aevum. They tell us " +
|
||||||
|
"that the facility houses many dangerous Synthoids and " +
|
||||||
|
"belongs to a terrorist organization called " +
|
||||||
|
"'The Covenant'. We have no prior intelligence about this " +
|
||||||
|
"organization, so you are going in blind.",
|
||||||
|
baseDifficulty:55e3, reqdRank:200e3,
|
||||||
|
rankGain:7.5e3, rankLoss:1e3, hpLoss:10e3,
|
||||||
|
weights:{hack:0,str:0.24,def:0.24,dex:0.24,agi:0.24,cha:0, int:0.04},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ultron"] = new BlackOperation({
|
||||||
|
name:"Operation Ultron",
|
||||||
|
desc:"OmniTek Incorporated, the original designer and manufacturer of Synthoids, " +
|
||||||
|
"has notified us of a malfunction in their AI design. This malfunction, " +
|
||||||
|
"when triggered, causes MK-VI Synthoids to become radicalized and seek out " +
|
||||||
|
"the destruction of humanity. They say that this bug affects all MK-VI Synthoids, " +
|
||||||
|
"not just the rogue ones from the Uprising.<br><br>" +
|
||||||
|
"OmniTek has also told us they they believe someone has triggered this " +
|
||||||
|
"malfunction in a large group of MK-VI Synthoids, and that these newly-radicalized Synthoids " +
|
||||||
|
"are now amassing in Volhaven to form a terrorist group called Ultron.<br><br>" +
|
||||||
|
"Intelligence suggests Ultron is heavily armed and that their members are " +
|
||||||
|
"augmented. We believe Ultron is making moves to take control of " +
|
||||||
|
"and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).<br><br>" +
|
||||||
|
"Your task is to find and destroy Ultron.",
|
||||||
|
baseDifficulty:60e3, reqdRank:250e3,
|
||||||
|
rankGain:10e3, rankLoss:2e3, hpLoss:10e3,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
isKill:true
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Centurion"] = new BlackOperation({
|
||||||
|
name:"Operation Centurion",
|
||||||
|
desc:"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
||||||
|
"Throughout all of humanity's history, we have relied on " +
|
||||||
|
"technology to survive, conquer, and progress. Its advancement became our primary goal. " +
|
||||||
|
"And at the peak of human civilization technology turned into " +
|
||||||
|
"power. Global, absolute power.<br><br>" +
|
||||||
|
"It seems that the universe is not without a sense of irony.<br><br>" +
|
||||||
|
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
||||||
|
baseDifficulty:70e3, reqdRank:300e3,
|
||||||
|
rankGain:15e3, rankLoss:5e3, hpLoss:10e3,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Vindictus"] = new BlackOperation({
|
||||||
|
name:"Operation Vindictus",
|
||||||
|
desc:"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
||||||
|
"The bits are all around us. The daemons that hold the Node " +
|
||||||
|
"together can manifest themselves in many different ways.<br><br>" +
|
||||||
|
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
||||||
|
baseDifficulty:75e3, reqdRank:350e3,
|
||||||
|
rankGain:20e3, rankLoss:20e3, hpLoss:20e3,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Daedalus"] = new BlackOperation({
|
||||||
|
name:"Operation Daedalus",
|
||||||
|
desc:"Yesterday we obeyed kings and bent our neck to emperors. " +
|
||||||
|
"Today we kneel only to truth.",
|
||||||
|
baseDifficulty:80e3, reqdRank:400e3,
|
||||||
|
rankGain:40e3, rankLoss:10e3, hpLoss:100e3,
|
||||||
|
weights:{hack:0.1,str:0.2,def:0.2,dex:0.2,agi:0.2,cha:0, int:0.1},
|
||||||
|
decays:{hack:0.6,str:0.8,def:0.8,dex:0.8,agi:0.8,cha:0, int:0.75},
|
||||||
|
});
|
||||||
|
})()
|
||||||
172
src/Bladeburner/City.ts
Normal file
172
src/Bladeburner/City.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
|
||||||
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
|
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
import { addOffset } from "../../utils/helpers/addOffset";
|
||||||
|
|
||||||
|
export class ChangePopulationByCountParams {
|
||||||
|
estChange: number = 0;
|
||||||
|
estOffset: number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangePopulationByPercentageParams {
|
||||||
|
nonZero: boolean = false;
|
||||||
|
changeEstEqually: boolean = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class City {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the city.
|
||||||
|
*/
|
||||||
|
name: string = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Population of the city.
|
||||||
|
*/
|
||||||
|
pop: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Population estimation of the city.
|
||||||
|
*/
|
||||||
|
popEst: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of communities in the city.
|
||||||
|
*/
|
||||||
|
comms: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estimated number of communities in the city.
|
||||||
|
*/
|
||||||
|
commsEst: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chaos level of the city.
|
||||||
|
*/
|
||||||
|
chaos: number = 0;
|
||||||
|
|
||||||
|
constructor(name: string = BladeburnerConstants.CityNames[2]) {
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
// Synthoid population and estimate
|
||||||
|
this.pop = getRandomInt(BladeburnerConstants.PopulationThreshold, 1.5 * BladeburnerConstants.PopulationThreshold);
|
||||||
|
this.popEst = this.pop * (Math.random() + 0.5);
|
||||||
|
|
||||||
|
// Number of Synthoid communities population and estimate
|
||||||
|
this.comms = getRandomInt(5, 150)
|
||||||
|
this.commsEst = this.comms + getRandomInt(-5, 5);
|
||||||
|
if (this.commsEst < 0) this.commsEst = 0;
|
||||||
|
this.chaos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* p is the percentage, not the multiplier (e.g. pass in p = 5 for 5%)
|
||||||
|
*/
|
||||||
|
changeChaosByPercentage(p: number): void {
|
||||||
|
if (isNaN(p)) {throw new Error("NaN passed into City.chaosChaosByPercentage()");}
|
||||||
|
if (p === 0) {return;}
|
||||||
|
this.chaos += this.chaos * (p/100);
|
||||||
|
if (this.chaos < 0) {this.chaos = 0;}
|
||||||
|
}
|
||||||
|
|
||||||
|
improvePopulationEstimateByCount(n: number): void {
|
||||||
|
if (isNaN(n)) {throw new Error("NaN passeed into City.improvePopulationEstimateByCount()");}
|
||||||
|
if (this.popEst < this.pop) {
|
||||||
|
this.popEst += n;
|
||||||
|
if (this.popEst > this.pop) {this.popEst = this.pop;}
|
||||||
|
} else if (this.popEst > this.pop) {
|
||||||
|
this.popEst -= n;
|
||||||
|
if (this.popEst < this.pop) {this.popEst = this.pop;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* p is the percentage, not the multiplier (e.g. pass in p = 5 for 5%)
|
||||||
|
*/
|
||||||
|
improvePopulationEstimateByPercentage(p: number, skillMult: number=1): void {
|
||||||
|
p = p*skillMult;
|
||||||
|
if (isNaN(p)) {throw new Error("NaN passed into City.improvePopulationEstimateByPercentage()");}
|
||||||
|
if (this.popEst < this.pop) {
|
||||||
|
++this.popEst; // In case estimate is 0
|
||||||
|
this.popEst *= (1 + (p/100));
|
||||||
|
if (this.popEst > this.pop) {this.popEst = this.pop;}
|
||||||
|
} else if (this.popEst > this.pop) {
|
||||||
|
this.popEst *= (1 - (p/100));
|
||||||
|
if (this.popEst < this.pop) {this.popEst = this.pop;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
improveCommunityEstimate(n: number=1): void {
|
||||||
|
if (isNaN(n)) {throw new Error("NaN passed into City.improveCommunityEstimate()");}
|
||||||
|
if (this.commsEst < this.comms) {
|
||||||
|
this.commsEst += n;
|
||||||
|
if (this.commsEst > this.comms) {this.commsEst = this.comms;}
|
||||||
|
} else if (this.commsEst > this.comms) {
|
||||||
|
this.commsEst -= n;
|
||||||
|
if (this.commsEst < this.comms) {this.commsEst = this.comms;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @params options:
|
||||||
|
* estChange(int): How much the estimate should change by
|
||||||
|
* estOffset(int): Add offset to estimate (offset by percentage)
|
||||||
|
*/
|
||||||
|
changePopulationByCount(n: number, params: ChangePopulationByCountParams=new ChangePopulationByCountParams()): void {
|
||||||
|
if (isNaN(n)) {throw new Error("NaN passed into City.changePopulationByCount()");}
|
||||||
|
this.pop += n;
|
||||||
|
if (params.estChange && !isNaN(params.estChange)) {this.popEst += params.estChange;}
|
||||||
|
if (params.estOffset) {
|
||||||
|
this.popEst = addOffset(this.popEst, params.estOffset);
|
||||||
|
}
|
||||||
|
this.popEst = Math.max(this.popEst, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @p is the percentage, not the multiplier. e.g. pass in p = 5 for 5%
|
||||||
|
* @params options:
|
||||||
|
* changeEstEqually(bool) - Change the population estimate by an equal amount
|
||||||
|
* nonZero (bool) - Set to true to ensure that population always changes by at least 1
|
||||||
|
*/
|
||||||
|
changePopulationByPercentage(p: number, params: ChangePopulationByPercentageParams=new ChangePopulationByPercentageParams()): number {
|
||||||
|
if (isNaN(p)) {throw new Error("NaN passed into City.changePopulationByPercentage()");}
|
||||||
|
if (p === 0) {return 0;}
|
||||||
|
let change = Math.round(this.pop * (p/100));
|
||||||
|
|
||||||
|
// Population always changes by at least 1
|
||||||
|
if (params.nonZero && change === 0) {
|
||||||
|
p > 0 ? change = 1 : change = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pop += change;
|
||||||
|
if (params.changeEstEqually) {
|
||||||
|
this.popEst += change;
|
||||||
|
if (this.popEst < 0) {this.popEst = 0;}
|
||||||
|
}
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeChaosByCount(n: number): void {
|
||||||
|
if (isNaN(n)) {throw new Error("NaN passed into City.changeChaosByCount()");}
|
||||||
|
if (n === 0) {return;}
|
||||||
|
this.chaos += n;
|
||||||
|
if (this.chaos < 0) {this.chaos = 0;}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiatizes a City object from a JSON save state.
|
||||||
|
*/
|
||||||
|
static fromJSON(value: any): City {
|
||||||
|
return Generic_fromJSON(City, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the current object to a JSON save state.
|
||||||
|
*/
|
||||||
|
toJSON(): any {
|
||||||
|
return Generic_toJSON("City", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.City = City;
|
||||||
24
src/Bladeburner/Contract.ts
Normal file
24
src/Bladeburner/Contract.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// import { BladeburnerConstants } from "./data/Constants";
|
||||||
|
import { Action, IActionParams } from "./Action";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
|
||||||
|
export class Contract extends Action {
|
||||||
|
|
||||||
|
constructor(params: IActionParams | null = null) {
|
||||||
|
super(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionTypeSkillSuccessBonus(inst: any): number {
|
||||||
|
return inst.skillMultipliers.successChanceContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(value: any): Contract {
|
||||||
|
return Generic_fromJSON(Contract, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): any {
|
||||||
|
return Generic_toJSON("Contract", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.Contract = Contract;
|
||||||
49
src/Bladeburner/GeneralActions.ts
Normal file
49
src/Bladeburner/GeneralActions.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { Action } from "./Action";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const GeneralActions: IMap<Action> = {};
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
// General Actions
|
||||||
|
let actionName;
|
||||||
|
actionName = "Training";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name:actionName,
|
||||||
|
desc:"Improve your abilities at the Bladeburner unit's specialized training " +
|
||||||
|
"center. Doing this gives experience for all combat stats and also " +
|
||||||
|
"increases your max stamina."
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Field Analysis";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name:actionName,
|
||||||
|
desc:"Mine and analyze Synthoid-related data. This improve the " +
|
||||||
|
"Bladeburner's unit intelligence on Synthoid locations and " +
|
||||||
|
"activities. Completing this action will improve the accuracy " +
|
||||||
|
"of your Synthoid population estimated in the current city.<br><br>" +
|
||||||
|
"Does NOT require stamina."
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Recruitment";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name:actionName,
|
||||||
|
desc:"Attempt to recruit members for your Bladeburner team. These members " +
|
||||||
|
"can help you conduct operations.<br><br>" +
|
||||||
|
"Does NOT require stamina."
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Diplomacy";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
desc: "Improve diplomatic relations with the Synthoid population. " +
|
||||||
|
"Completing this action will reduce the Chaos level in your current city.<br><br>" +
|
||||||
|
"Does NOT require stamina."
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Hyperbolic Regeneration Chamber";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
desc: "Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. " +
|
||||||
|
"This will slowly heal your wounds and slightly increase your stamina.<br><br>",
|
||||||
|
});
|
||||||
|
})()
|
||||||
55
src/Bladeburner/Operation.ts
Normal file
55
src/Bladeburner/Operation.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
|
import { Action, IActionParams } from "./Action";
|
||||||
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||||
|
|
||||||
|
export interface IOperationParams extends IActionParams {
|
||||||
|
reqdRank?: number;
|
||||||
|
teamCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Operation extends Action {
|
||||||
|
reqdRank: number = 100;
|
||||||
|
teamCount: number = 0;
|
||||||
|
|
||||||
|
constructor(params: IOperationParams | null = null) {
|
||||||
|
super(params);
|
||||||
|
if(params && params.reqdRank) this.reqdRank = params.reqdRank;
|
||||||
|
if(params && params.teamCount) this.teamCount = params.teamCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For actions that have teams. To be implemented by subtypes.
|
||||||
|
getTeamSuccessBonus(inst: any): number {
|
||||||
|
if (this.teamCount && this.teamCount > 0) {
|
||||||
|
this.teamCount = Math.min(this.teamCount, inst.teamSize);
|
||||||
|
let teamMultiplier = Math.pow(this.teamCount, 0.05);
|
||||||
|
return teamMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionTypeSkillSuccessBonus(inst: any): number {
|
||||||
|
return inst.skillMultipliers.successChanceOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
getChaosDifficultyBonus(inst: any, params: any): number {
|
||||||
|
const city = inst.getCurrentCity();
|
||||||
|
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||||
|
let diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||||
|
let mult = Math.pow(diff, 0.1);
|
||||||
|
return mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(value: any): Operation {
|
||||||
|
return Generic_fromJSON(Operation, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): any {
|
||||||
|
return Generic_toJSON("Operation", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.Operation = Operation;
|
||||||
107
src/Bladeburner/Skill.ts
Normal file
107
src/Bladeburner/Skill.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
|
|
||||||
|
interface ISkillParams {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
|
||||||
|
baseCost?: number;
|
||||||
|
costInc?: number;
|
||||||
|
maxLvl?: number;
|
||||||
|
|
||||||
|
successChanceAll?: number;
|
||||||
|
successChanceStealth?: number;
|
||||||
|
successChanceKill?: number;
|
||||||
|
successChanceContract?: number;
|
||||||
|
successChanceOperation?: number;
|
||||||
|
successChanceEstimate?: number;
|
||||||
|
|
||||||
|
actionTime?: number;
|
||||||
|
|
||||||
|
effHack?: number;
|
||||||
|
effStr?: number;
|
||||||
|
effDef?: number;
|
||||||
|
effDex?: number;
|
||||||
|
effAgi?: number;
|
||||||
|
effCha?: number;
|
||||||
|
|
||||||
|
stamina?: number;
|
||||||
|
money?: number;
|
||||||
|
expGain?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Skill {
|
||||||
|
name: string;
|
||||||
|
desc: string;
|
||||||
|
// Cost is in Skill Points
|
||||||
|
baseCost: number = 1;
|
||||||
|
// Additive cost increase per level
|
||||||
|
costInc: number = 1;
|
||||||
|
maxLvl: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These benefits are additive. So total multiplier will be level (handled externally) times the
|
||||||
|
* effects below
|
||||||
|
*/
|
||||||
|
successChanceAll: number = 0;
|
||||||
|
successChanceStealth: number = 0;
|
||||||
|
successChanceKill: number = 0;
|
||||||
|
successChanceContract: number = 0;
|
||||||
|
successChanceOperation: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This multiplier affects everything that increases synthoid population/community estimate
|
||||||
|
* e.g. Field analysis, Investigation Op, Undercover Op
|
||||||
|
*/
|
||||||
|
successChanceEstimate: number = 0;
|
||||||
|
actionTime: number = 0;
|
||||||
|
effHack: number = 0;
|
||||||
|
effStr: number = 0;
|
||||||
|
effDef: number = 0;
|
||||||
|
effDex: number = 0;
|
||||||
|
effAgi: number = 0;
|
||||||
|
effCha: number = 0;
|
||||||
|
stamina: number = 0;
|
||||||
|
money: number = 0;
|
||||||
|
expGain: number = 0;
|
||||||
|
|
||||||
|
constructor(params: ISkillParams={name:"foo", desc:"foo"}) {
|
||||||
|
if (!params.name) {
|
||||||
|
throw new Error("Failed to initialize Bladeburner Skill. No name was specified in ctor");
|
||||||
|
}
|
||||||
|
if (!params.desc) {
|
||||||
|
throw new Error("Failed to initialize Bladeburner Skills. No desc was specified in ctor");
|
||||||
|
}
|
||||||
|
this.name = params.name;
|
||||||
|
this.desc = params.desc;
|
||||||
|
this.baseCost = params.baseCost ? params.baseCost : 1;
|
||||||
|
this.costInc = params.costInc ? params.costInc : 1;
|
||||||
|
|
||||||
|
if (params.maxLvl) {this.maxLvl = params.maxLvl;}
|
||||||
|
|
||||||
|
if (params.successChanceAll) {this.successChanceAll = params.successChanceAll;}
|
||||||
|
if (params.successChanceStealth) {this.successChanceStealth = params.successChanceStealth;}
|
||||||
|
if (params.successChanceKill) {this.successChanceKill = params.successChanceKill;}
|
||||||
|
if (params.successChanceContract) {this.successChanceContract = params.successChanceContract;}
|
||||||
|
if (params.successChanceOperation) {this.successChanceOperation = params.successChanceOperation;}
|
||||||
|
|
||||||
|
|
||||||
|
if (params.successChanceEstimate) {this.successChanceEstimate = params.successChanceEstimate;}
|
||||||
|
|
||||||
|
if (params.actionTime) {this.actionTime = params.actionTime;}
|
||||||
|
if (params.effHack) {this.effHack = params.effHack;}
|
||||||
|
if (params.effStr) {this.effStr = params.effStr;}
|
||||||
|
if (params.effDef) {this.effDef = params.effDef;}
|
||||||
|
if (params.effDex) {this.effDex = params.effDex;}
|
||||||
|
if (params.effAgi) {this.effAgi = params.effAgi;}
|
||||||
|
if (params.effCha) {this.effCha = params.effCha;}
|
||||||
|
|
||||||
|
if (params.stamina) {this.stamina = params.stamina;}
|
||||||
|
if (params.money) {this.money = params.money;}
|
||||||
|
if (params.expGain) {this.expGain = params.expGain;}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateCost(currentLevel: number): number {
|
||||||
|
return Math.floor((this.baseCost + (currentLevel * this.costInc)) * BitNodeMultipliers.BladeburnerSkillCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
90
src/Bladeburner/Skills.ts
Normal file
90
src/Bladeburner/Skills.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Skill } from "./Skill";
|
||||||
|
import { SkillNames } from "./data/SkillNames";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const Skills: IMap<Skill> = {};
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
Skills[SkillNames.BladesIntuition] = new Skill({
|
||||||
|
name:SkillNames.BladesIntuition,
|
||||||
|
desc:"Each level of this skill increases your success chance " +
|
||||||
|
"for all Contracts, Operations, and BlackOps by 3%",
|
||||||
|
baseCost: 3, costInc: 2.1,
|
||||||
|
successChanceAll:3
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Cloak] = new Skill({
|
||||||
|
name:SkillNames.Cloak,
|
||||||
|
desc:"Each level of this skill increases your " +
|
||||||
|
"success chance in stealth-related Contracts, Operations, and BlackOps by 5.5%",
|
||||||
|
baseCost: 2, costInc: 1.1,
|
||||||
|
successChanceStealth:5.5
|
||||||
|
});
|
||||||
|
Skills[SkillNames.ShortCircuit] = new Skill({
|
||||||
|
name:SkillNames.ShortCircuit,
|
||||||
|
desc:"Each level of this skill increases your success chance " +
|
||||||
|
"in Contracts, Operations, and BlackOps that involve retirement by 5.5%",
|
||||||
|
baseCost: 2, costInc: 2.1,
|
||||||
|
successChanceKill:5.5
|
||||||
|
});
|
||||||
|
Skills[SkillNames.DigitalObserver] = new Skill({
|
||||||
|
name:SkillNames.DigitalObserver,
|
||||||
|
desc:"Each level of this skill increases your success chance in " +
|
||||||
|
"all Operations and BlackOps by 4%",
|
||||||
|
baseCost: 2, costInc: 2.1,
|
||||||
|
successChanceOperation:4
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Tracer] = new Skill({
|
||||||
|
name:SkillNames.Tracer,
|
||||||
|
desc:"Each level of this skill increases your success chance in " +
|
||||||
|
"all Contracts by 4%",
|
||||||
|
baseCost: 2, costInc: 2.1,
|
||||||
|
successChanceContract:4
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Overclock] = new Skill({
|
||||||
|
name:SkillNames.Overclock,
|
||||||
|
desc:"Each level of this skill decreases the time it takes " +
|
||||||
|
"to attempt a Contract, Operation, and BlackOp by 1% (Max Level: 90)",
|
||||||
|
baseCost: 3, costInc: 1.4, maxLvl: 90,
|
||||||
|
actionTime:1
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Reaper] = new Skill({
|
||||||
|
name: SkillNames.Reaper,
|
||||||
|
desc: "Each level of this skill increases your effective combat stats for Bladeburner actions by 2%",
|
||||||
|
baseCost: 2, costInc: 2.1,
|
||||||
|
effStr: 2, effDef: 2, effDex: 2, effAgi: 2
|
||||||
|
});
|
||||||
|
Skills[SkillNames.EvasiveSystem] = new Skill({
|
||||||
|
name:SkillNames.EvasiveSystem,
|
||||||
|
desc:"Each level of this skill increases your effective " +
|
||||||
|
"dexterity and agility for Bladeburner actions by 4%",
|
||||||
|
baseCost: 2, costInc: 2.1,
|
||||||
|
effDex: 4, effAgi: 4
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Datamancer] = new Skill({
|
||||||
|
name:SkillNames.Datamancer,
|
||||||
|
desc:"Each level of this skill increases your effectiveness in " +
|
||||||
|
"synthoid population analysis and investigation by 5%. " +
|
||||||
|
"This affects all actions that can potentially increase " +
|
||||||
|
"the accuracy of your synthoid population/community estimates.",
|
||||||
|
baseCost:3, costInc:1,
|
||||||
|
successChanceEstimate:5
|
||||||
|
});
|
||||||
|
Skills[SkillNames.CybersEdge] = new Skill({
|
||||||
|
name:SkillNames.CybersEdge,
|
||||||
|
desc:"Each level of this skill increases your max stamina by 2%",
|
||||||
|
baseCost:1, costInc:3,
|
||||||
|
stamina:2
|
||||||
|
});
|
||||||
|
Skills[SkillNames.HandsOfMidas] = new Skill({
|
||||||
|
name: SkillNames.HandsOfMidas,
|
||||||
|
desc: "Each level of this skill increases the amount of money you receive from Contracts by 10%",
|
||||||
|
baseCost: 2, costInc: 2.5,
|
||||||
|
money: 10,
|
||||||
|
});
|
||||||
|
Skills[SkillNames.Hyperdrive] = new Skill({
|
||||||
|
name: SkillNames.Hyperdrive,
|
||||||
|
desc: "Each level of this skill increases the experience earned from Contracts, Operations, and BlackOps by 10%",
|
||||||
|
baseCost: 1, costInc: 2.5,
|
||||||
|
expGain: 10,
|
||||||
|
});
|
||||||
|
})()
|
||||||
14
src/Bladeburner/data/ActionTypes.ts
Normal file
14
src/Bladeburner/data/ActionTypes.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Action Identifier enum
|
||||||
|
export const ActionTypes = Object.freeze({
|
||||||
|
"Idle": 1,
|
||||||
|
"Contract": 2,
|
||||||
|
"Operation": 3,
|
||||||
|
"BlackOp": 4,
|
||||||
|
"BlackOperation": 4,
|
||||||
|
"Training": 5,
|
||||||
|
"Recruitment": 6,
|
||||||
|
"FieldAnalysis": 7,
|
||||||
|
"Field Analysis": 7,
|
||||||
|
"Diplomacy": 8,
|
||||||
|
"Hyperbolic Regeneration Chamber": 9,
|
||||||
|
});
|
||||||
79
src/Bladeburner/data/Constants.ts
Normal file
79
src/Bladeburner/data/Constants.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
export const BladeburnerConstants: {
|
||||||
|
CityNames: string[];
|
||||||
|
CyclesPerSecond: number;
|
||||||
|
StaminaGainPerSecond: number;
|
||||||
|
BaseStaminaLoss: number;
|
||||||
|
MaxStaminaToGainFactor: number;
|
||||||
|
DifficultyToTimeFactor: number;
|
||||||
|
DiffMultExponentialFactor: number;
|
||||||
|
DiffMultLinearFactor: number;
|
||||||
|
EffAgiLinearFactor: number;
|
||||||
|
EffDexLinearFactor: number;
|
||||||
|
EffAgiExponentialFactor: number;
|
||||||
|
EffDexExponentialFactor: number;
|
||||||
|
BaseRecruitmentTimeNeeded: number;
|
||||||
|
PopulationThreshold: number;
|
||||||
|
PopulationExponent: number;
|
||||||
|
ChaosThreshold: number;
|
||||||
|
BaseStatGain: number;
|
||||||
|
BaseIntGain: number;
|
||||||
|
ActionCountGrowthPeriod: number;
|
||||||
|
RankToFactionRepFactor: number;
|
||||||
|
RankNeededForFaction: number;
|
||||||
|
ContractSuccessesPerLevel: number;
|
||||||
|
OperationSuccessesPerLevel: number;
|
||||||
|
RanksPerSkillPoint: number;
|
||||||
|
ContractBaseMoneyGain: number;
|
||||||
|
HrcHpGain: number;
|
||||||
|
HrcStaminaGain: number;
|
||||||
|
} = {
|
||||||
|
CityNames: ["Aevum", "Chongqing", "Sector-12", "New Tokyo", "Ishima", "Volhaven"],
|
||||||
|
CyclesPerSecond: 5, // Game cycle is 200 ms
|
||||||
|
|
||||||
|
StaminaGainPerSecond: 0.0085,
|
||||||
|
BaseStaminaLoss: 0.285, // Base stamina loss per action. Increased based on difficulty
|
||||||
|
MaxStaminaToGainFactor: 70000, // Max Stamina is divided by this to get bonus stamina gain
|
||||||
|
|
||||||
|
DifficultyToTimeFactor: 10, // Action Difficulty divided by this to get base action time
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The difficulty multiplier affects stamina loss and hp loss of an action. Also affects
|
||||||
|
* experience gain. Its formula is:
|
||||||
|
* difficulty ^ exponentialFactor + difficulty / linearFactor
|
||||||
|
*/
|
||||||
|
DiffMultExponentialFactor: 0.28,
|
||||||
|
DiffMultLinearFactor: 650,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These factors are used to calculate action time.
|
||||||
|
* They affect how much action time is reduced based on your agility and dexterity
|
||||||
|
*/
|
||||||
|
EffAgiLinearFactor: 10e3,
|
||||||
|
EffDexLinearFactor: 10e3,
|
||||||
|
EffAgiExponentialFactor: 0.04,
|
||||||
|
EffDexExponentialFactor: 0.035,
|
||||||
|
|
||||||
|
BaseRecruitmentTimeNeeded: 300, // Base time needed (s) to complete a Recruitment action
|
||||||
|
|
||||||
|
PopulationThreshold: 1e9, // Population which determines baseline success rate
|
||||||
|
PopulationExponent: 0.7, // Exponent that influences how different populations affect success rate
|
||||||
|
ChaosThreshold: 50, // City chaos level after which it starts making tasks harder
|
||||||
|
|
||||||
|
BaseStatGain: 1, // Base stat gain per second
|
||||||
|
BaseIntGain: 0.001, // Base intelligence stat gain
|
||||||
|
|
||||||
|
ActionCountGrowthPeriod: 480, // Time (s) it takes for action count to grow by its specified value
|
||||||
|
|
||||||
|
RankToFactionRepFactor: 2, // Delta Faction Rep = this * Delta Rank
|
||||||
|
RankNeededForFaction: 25,
|
||||||
|
|
||||||
|
ContractSuccessesPerLevel: 3, // How many successes you need to level up a contract
|
||||||
|
OperationSuccessesPerLevel: 2.5, // How many successes you need to level up an op
|
||||||
|
|
||||||
|
RanksPerSkillPoint: 3, // How many ranks needed to get 1 Skill Point
|
||||||
|
|
||||||
|
ContractBaseMoneyGain: 250e3, // Base Money Gained per contract
|
||||||
|
|
||||||
|
HrcHpGain: 2, // HP Gained from Hyperbolic Regeneration chamber
|
||||||
|
HrcStaminaGain: 1, // Percentage Stamina gained from Hyperbolic Regeneration Chamber
|
||||||
|
}
|
||||||
116
src/Bladeburner/data/Help.ts
Normal file
116
src/Bladeburner/data/Help.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
export const ConsoleHelpText: {} = {
|
||||||
|
helpList: [
|
||||||
|
"Use 'help [command]' to get more information about a particular Bladeburner console command.",
|
||||||
|
"",
|
||||||
|
" automate [var] [val] [hi/low] Configure simple automation for Bladeburner tasks",
|
||||||
|
" clear/cls Clear the console",
|
||||||
|
" help [cmd] Display this help text, or help text for a specific command",
|
||||||
|
" log [en/dis] [type] Enable or disable logging for events and actions",
|
||||||
|
" skill [action] [name] Level or display info about your Bladeburner skills",
|
||||||
|
" start [type] [name] Start a Bladeburner action/task" ,
|
||||||
|
" stop Stops your current Bladeburner action/task"
|
||||||
|
],
|
||||||
|
automate: [
|
||||||
|
"automate [var] [val] [hi/low]",
|
||||||
|
"",
|
||||||
|
"A simple way to automate your Bladeburner actions. This console command can be used " +
|
||||||
|
"to automatically start an action when your stamina rises above a certain threshold, and " +
|
||||||
|
"automatically switch to another action when your stamina drops below another threshold.",
|
||||||
|
" automate status - Check the current status of your automation and get a brief description of what it'll do",
|
||||||
|
" automate en - Enable the automation feature",
|
||||||
|
" automate dis - Disable the automation feature",
|
||||||
|
"",
|
||||||
|
"There are four properties that must be set for this automation to work properly. Here is how to set them:",
|
||||||
|
"",
|
||||||
|
" automate stamina 100 high",
|
||||||
|
" automate contract Tracking high",
|
||||||
|
" automate stamina 50 low",
|
||||||
|
" automate general 'Field Analysis' low",
|
||||||
|
"",
|
||||||
|
"Using the four console commands above will set the automation to perform Tracking contracts " +
|
||||||
|
"if your stamina is 100 or higher, and then switch to Field Analysis if your stamina drops below " +
|
||||||
|
"50. Note that when setting the action, the name of the action is CASE-SENSITIVE. It must " +
|
||||||
|
"exactly match whatever the name is in the UI."
|
||||||
|
],
|
||||||
|
clear: [
|
||||||
|
"clear",
|
||||||
|
"",
|
||||||
|
"Clears the console"
|
||||||
|
],
|
||||||
|
cls: [
|
||||||
|
"cls",
|
||||||
|
"",
|
||||||
|
"Clears the console"
|
||||||
|
],
|
||||||
|
help: [
|
||||||
|
"help [command]",
|
||||||
|
"",
|
||||||
|
"Running 'help' with no arguments displays the general help text, which lists all console commands " +
|
||||||
|
"and a brief description of what they do. A command can be specified to get more specific help text " +
|
||||||
|
"about that particular command. For example:",
|
||||||
|
"",
|
||||||
|
" help automate",
|
||||||
|
"",
|
||||||
|
"will display specific information about using the automate console command"
|
||||||
|
],
|
||||||
|
log: [
|
||||||
|
"log [en/dis] [type]",
|
||||||
|
"",
|
||||||
|
"Enable or disable logging. By default, the results of completing actions such as contracts/operations are logged " +
|
||||||
|
"in the console. There are also random events that are logged in the console as well. The five categories of " +
|
||||||
|
"things that get logged are:",
|
||||||
|
"",
|
||||||
|
"[general, contracts, ops, blackops, events]",
|
||||||
|
"",
|
||||||
|
"The logging for these categories can be enabled or disabled like so:",
|
||||||
|
"",
|
||||||
|
" log dis contracts - Disables logging that occurs when contracts are completed",
|
||||||
|
" log en contracts - Enables logging that occurs when contracts are completed",
|
||||||
|
" log dis events - Disables logging for Bladeburner random events",
|
||||||
|
"",
|
||||||
|
"Logging can be universally enabled/disabled using the 'all' keyword:",
|
||||||
|
"",
|
||||||
|
" log dis all",
|
||||||
|
" log en all"
|
||||||
|
],
|
||||||
|
skill: [
|
||||||
|
"skill [action] [name]",
|
||||||
|
"",
|
||||||
|
"Level or display information about your skills.",
|
||||||
|
"",
|
||||||
|
"To display information about all of your skills and your multipliers, use:",
|
||||||
|
"",
|
||||||
|
" skill list",
|
||||||
|
"",
|
||||||
|
"To display information about a specific skill, specify the name of the skill afterwards. " +
|
||||||
|
"Note that the name of the skill is case-sensitive. Enter it exactly as seen in the UI. If " +
|
||||||
|
"the name of the skill has whitespace, enclose the name of the skill in double quotation marks:",
|
||||||
|
"",
|
||||||
|
" skill list Reaper<br>" +
|
||||||
|
" skill list 'Digital Observer'",
|
||||||
|
"",
|
||||||
|
"This console command can also be used to level up skills:",
|
||||||
|
"",
|
||||||
|
" skill level [skill name]"
|
||||||
|
],
|
||||||
|
start: [
|
||||||
|
"start [type] [name]",
|
||||||
|
"",
|
||||||
|
"Start an action. An action is specified by its type and its name. The " +
|
||||||
|
"name is case-sensitive. It must appear exactly as it does in the UI. If " +
|
||||||
|
"the name of the action has whitespace, enclose it in double quotation marks. " +
|
||||||
|
"Valid action types include:",
|
||||||
|
"",
|
||||||
|
"[general, contract, op, blackop]",
|
||||||
|
"",
|
||||||
|
"Examples:",
|
||||||
|
"",
|
||||||
|
" start contract Tracking",
|
||||||
|
" start op 'Undercover Operation'"
|
||||||
|
],
|
||||||
|
stop:[
|
||||||
|
"stop",
|
||||||
|
"",
|
||||||
|
"Stop your current action and go idle."
|
||||||
|
],
|
||||||
|
}
|
||||||
31
src/Bladeburner/data/SkillNames.ts
Normal file
31
src/Bladeburner/data/SkillNames.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export const SkillNames: {
|
||||||
|
BladesIntuition: string;
|
||||||
|
Cloak: string;
|
||||||
|
Marksman: string;
|
||||||
|
WeaponProficiency: string;
|
||||||
|
ShortCircuit: string;
|
||||||
|
DigitalObserver: string;
|
||||||
|
Tracer: string;
|
||||||
|
Overclock: string;
|
||||||
|
Reaper: string;
|
||||||
|
EvasiveSystem: string;
|
||||||
|
Datamancer: string;
|
||||||
|
CybersEdge: string;
|
||||||
|
HandsOfMidas: string;
|
||||||
|
Hyperdrive: string;
|
||||||
|
} = {
|
||||||
|
BladesIntuition: "Blade's Intuition",
|
||||||
|
Cloak: "Cloak",
|
||||||
|
Marksman: "Marksman",
|
||||||
|
WeaponProficiency: "Weapon Proficiency",
|
||||||
|
ShortCircuit: "Short-Circuit",
|
||||||
|
DigitalObserver: "Digital Observer",
|
||||||
|
Tracer: "Tracer",
|
||||||
|
Overclock: "Overclock",
|
||||||
|
Reaper: "Reaper",
|
||||||
|
EvasiveSystem: "Evasive System",
|
||||||
|
Datamancer: "Datamancer",
|
||||||
|
CybersEdge: "Cyber's Edge",
|
||||||
|
HandsOfMidas: "Hands of Midas",
|
||||||
|
Hyperdrive: "Hyperdrive",
|
||||||
|
}
|
||||||
63
src/Bladeburner/ui/BlackOperationsPage.tsx
Normal file
63
src/Bladeburner/ui/BlackOperationsPage.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { BlackOperations } from "../BlackOperations";
|
||||||
|
/*
|
||||||
|
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
|
||||||
|
throw new Error("Bladeburner.createBlackOpsContent called with either " +
|
||||||
|
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
|
||||||
|
}
|
||||||
|
|
||||||
|
DomElems.actionsAndSkillsDesc.innerHTML =
|
||||||
|
"Black Operations (Black Ops) are special, one-time covert operations. " +
|
||||||
|
"Each Black Op must be unlocked successively by completing " +
|
||||||
|
"the one before it.<br><br>" +
|
||||||
|
"<b>Your ultimate goal to climb through the ranks of Bladeburners is to complete " +
|
||||||
|
"all of the Black Ops.</b><br><br>" +
|
||||||
|
"Like normal operations, you may use a team for Black Ops. Failing " +
|
||||||
|
"a black op will incur heavy HP and rank losses.";
|
||||||
|
|
||||||
|
// Put Black Operations in sequence of required rank
|
||||||
|
var blackops = [];
|
||||||
|
for (var blackopName in BlackOperations) {
|
||||||
|
if (BlackOperations.hasOwnProperty(blackopName)) {
|
||||||
|
blackops.push(BlackOperations[blackopName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blackops.sort(function(a, b) {
|
||||||
|
return (a.reqdRank - b.reqdRank);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = blackops.length-1; i >= 0 ; --i) {
|
||||||
|
if (this.blackops[[blackops[i].name]] == null && i !== 0 && this.blackops[[blackops[i-1].name]] == null) {continue;} // If this one nor the next are completed then this isn't unlocked yet.
|
||||||
|
DomElems.blackops[blackops[i].name] = createElement("div", {
|
||||||
|
class:"bladeburner-action", name:blackops[i].name
|
||||||
|
});
|
||||||
|
DomElems.actionsAndSkillsList.appendChild(DomElems.blackops[blackops[i].name]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export function BlackOperationsPage(inst: any): React.ReactElement {
|
||||||
|
// Put Black Operations in sequence of required rank
|
||||||
|
const blackops = [];
|
||||||
|
for (const name in BlackOperations) {
|
||||||
|
if (BlackOperations.hasOwnProperty(name)) {
|
||||||
|
blackops.push(BlackOperations[name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blackops.sort(function(a, b) {
|
||||||
|
return (a.reqdRank - b.reqdRank);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (<div>
|
||||||
|
<p>
|
||||||
|
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked successively by completing the one before it.<br /><br />
|
||||||
|
<b>Your ultimate goal to climb through the ranks of Bladeburners is to complete all of the Black Ops.</b><br /><br />
|
||||||
|
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank losses.</p>
|
||||||
|
{blackops.map( op =>
|
||||||
|
<div className="bladeburner-action">
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
@@ -242,6 +242,12 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
Gang
|
Gang
|
||||||
* style improvements
|
* style improvements
|
||||||
|
|
||||||
|
Bladeburner
|
||||||
|
* style improvements
|
||||||
|
|
||||||
|
Bladeburner
|
||||||
|
* fix bug where 'skill list SKILL' would crash if skill is level 0.
|
||||||
|
|
||||||
Sleeve
|
Sleeve
|
||||||
* karma gain now scales with sync.
|
* karma gain now scales with sync.
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -431,14 +431,18 @@ function NetscriptFunctions(workerScript) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkBladeburnerAccess = function(func) {
|
const checkBladeburnerAccess = function(func, skipjoined=false) {
|
||||||
const accessDenied = `You do not ` +
|
const apiAccess = (Player.bitNodeN === 7 || Player.sourceFiles.some(a=>{return a.n === 7}));
|
||||||
"currently have access to the Bladeburner API. To access the Bladeburner API " +
|
if (!apiAccess) {
|
||||||
"you must be employed at the Bladeburner division, AND you must either be in " +
|
const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`;
|
||||||
"BitNode-7 or have Source-File 7.";
|
throw makeRuntimeErrorMsg(`bladeburner.${func}`, apiDenied);
|
||||||
const hasAccess = Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || Player.sourceFiles.some(a=>{return a.n === 7}));
|
}
|
||||||
if(!hasAccess) {
|
if (!skipjoined) {
|
||||||
throw makeRuntimeErrorMsg(`bladeburner.${func}`, accessDenied);
|
const bladeburnerAccess = Player.bladeburner instanceof Bladeburner;
|
||||||
|
if(!bladeburnerAccess) {
|
||||||
|
const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`;
|
||||||
|
throw makeRuntimeErrorMsg(`bladeburner.${func}`, bladeburnerDenied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,17 +711,19 @@ function NetscriptFunctions(workerScript) {
|
|||||||
maxThreadNeeded = 1e6;
|
maxThreadNeeded = 1e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
let moneyGained = Math.floor(server.moneyAvailable * percentHacked) * threads;
|
let moneyDrained = Math.floor(server.moneyAvailable * percentHacked) * threads;
|
||||||
|
|
||||||
// Over-the-top safety checks
|
// Over-the-top safety checks
|
||||||
if (moneyGained <= 0) {
|
if (moneyDrained <= 0) {
|
||||||
moneyGained = 0;
|
moneyDrained = 0;
|
||||||
expGainedOnSuccess = expGainedOnFailure;
|
expGainedOnSuccess = expGainedOnFailure;
|
||||||
}
|
}
|
||||||
if (moneyGained > server.moneyAvailable) {moneyGained = server.moneyAvailable;}
|
if (moneyDrained > server.moneyAvailable) {moneyDrained = server.moneyAvailable;}
|
||||||
server.moneyAvailable -= moneyGained;
|
server.moneyAvailable -= moneyDrained;
|
||||||
if (server.moneyAvailable < 0) {server.moneyAvailable = 0;}
|
if (server.moneyAvailable < 0) {server.moneyAvailable = 0;}
|
||||||
|
|
||||||
|
const moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain;
|
||||||
|
|
||||||
Player.gainMoney(moneyGained);
|
Player.gainMoney(moneyGained);
|
||||||
workerScript.scriptRef.onlineMoneyMade += moneyGained;
|
workerScript.scriptRef.onlineMoneyMade += moneyGained;
|
||||||
Player.scriptProdSinceLastAug += moneyGained;
|
Player.scriptProdSinceLastAug += moneyGained;
|
||||||
@@ -1681,7 +1687,8 @@ function NetscriptFunctions(workerScript) {
|
|||||||
checkTixApiAccess("buyStock");
|
checkTixApiAccess("buyStock");
|
||||||
const stock = getStockFromSymbol(symbol, "buyStock");
|
const stock = getStockFromSymbol(symbol, "buyStock");
|
||||||
const res = buyStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent });
|
const res = buyStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent });
|
||||||
|
console.log(stock);
|
||||||
|
console.log(res);
|
||||||
return res ? stock.price : 0;
|
return res ? stock.price : 0;
|
||||||
},
|
},
|
||||||
sellStock: function(symbol, shares) {
|
sellStock: function(symbol, shares) {
|
||||||
@@ -3696,12 +3703,12 @@ function NetscriptFunctions(workerScript) {
|
|||||||
},
|
},
|
||||||
joinBladeburnerFaction: function() {
|
joinBladeburnerFaction: function() {
|
||||||
updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction"));
|
updateDynamicRam("joinBladeburnerFaction", getRamCost("bladeburner", "joinBladeburnerFaction"));
|
||||||
checkBladeburnerAccess("joinBladeburnerFaction");
|
checkBladeburnerAccess("joinBladeburnerFaction", true);
|
||||||
return Player.bladeburner.joinBladeburnerFactionNetscriptFn(workerScript);
|
return Player.bladeburner.joinBladeburnerFactionNetscriptFn(workerScript);
|
||||||
},
|
},
|
||||||
joinBladeburnerDivision: function() {
|
joinBladeburnerDivision: function() {
|
||||||
updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision"));
|
updateDynamicRam("joinBladeburnerDivision", getRamCost("bladeburner", "joinBladeburnerDivision"));
|
||||||
checkBladeburnerAccess("joinBladeburnerDivision");
|
checkBladeburnerAccess("joinBladeburnerDivision", true);
|
||||||
if ((Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) {
|
if ((Player.bitNodeN === 7 || SourceFileFlags[7] > 0)) {
|
||||||
if (Player.bitNodeN === 8) { return false; }
|
if (Player.bitNodeN === 8) { return false; }
|
||||||
if (Player.bladeburner instanceof Bladeburner) {
|
if (Player.bladeburner instanceof Bladeburner) {
|
||||||
|
|||||||
@@ -176,4 +176,5 @@ export interface IPlayer {
|
|||||||
startWorkPartTime(companyName: string): void;
|
startWorkPartTime(companyName: string): void;
|
||||||
travel(to: CityName): boolean;
|
travel(to: CityName): boolean;
|
||||||
giveExploit(exploit: Exploit): void;
|
giveExploit(exploit: Exploit): void;
|
||||||
|
queryStatFromString(str: string): number;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user