mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-24 15:12:27 +01:00
Merge pull request #3680 from danielyxie/sleeve-blade
SLEEVE: Can now perform bladeburner actions
This commit is contained in:
commit
8d2041389e
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "1.6.4",
|
||||
"version": "1.7.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "1.6.4",
|
||||
"version": "1.7.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"dependencies": {
|
||||
|
@ -5,6 +5,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { IBladeburner } from "./IBladeburner";
|
||||
import { IAction, ISuccessChanceParams } from "./IAction";
|
||||
import { IPerson } from "../PersonObjects/IPerson";
|
||||
|
||||
class StatsMultiplier {
|
||||
[key: string]: number;
|
||||
@ -152,8 +153,8 @@ export class Action implements IAction {
|
||||
* Tests for success. Should be called when an action has completed
|
||||
* @param inst {Bladeburner} - Bladeburner instance
|
||||
*/
|
||||
attempt(inst: IBladeburner): boolean {
|
||||
return Math.random() < this.getSuccessChance(inst);
|
||||
attempt(inst: IBladeburner, person: IPerson): boolean {
|
||||
return Math.random() < this.getSuccessChance(inst, person);
|
||||
}
|
||||
|
||||
// To be implemented by subtypes
|
||||
@ -161,13 +162,13 @@ export class Action implements IAction {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTime(inst: IBladeburner): number {
|
||||
getActionTime(inst: IBladeburner, person: IPerson): 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 effAgility = person.agility * inst.skillMultipliers.effAgi;
|
||||
const effDexterity = person.dexterity * inst.skillMultipliers.effDex;
|
||||
const statFac =
|
||||
0.5 *
|
||||
(Math.pow(effAgility, BladeburnerConstants.EffAgiExponentialFactor) +
|
||||
@ -211,12 +212,12 @@ export class Action implements IAction {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getEstSuccessChance(inst: IBladeburner): [number, number] {
|
||||
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number] {
|
||||
function clamp(x: number): number {
|
||||
return Math.max(0, Math.min(x, 1));
|
||||
}
|
||||
const est = this.getSuccessChance(inst, { est: true });
|
||||
const real = this.getSuccessChance(inst);
|
||||
const est = this.getSuccessChance(inst, person, { est: true });
|
||||
const real = this.getSuccessChance(inst, person);
|
||||
const diff = Math.abs(real - est);
|
||||
let low = real - diff;
|
||||
let high = real + diff;
|
||||
@ -232,7 +233,7 @@ export class Action implements IAction {
|
||||
* @params - options:
|
||||
* est (bool): Get success chance estimate instead of real success chance
|
||||
*/
|
||||
getSuccessChance(inst: IBladeburner, params: ISuccessChanceParams = { est: false }): number {
|
||||
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams = { est: false }): number {
|
||||
if (inst == null) {
|
||||
throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");
|
||||
}
|
||||
@ -240,7 +241,7 @@ export class Action implements IAction {
|
||||
let competence = 0;
|
||||
for (const stat of Object.keys(this.weights)) {
|
||||
if (this.weights.hasOwnProperty(stat)) {
|
||||
const playerStatLvl = Player.queryStatFromString(stat);
|
||||
const playerStatLvl = person.queryStatFromString(stat);
|
||||
const key = "eff" + stat.charAt(0).toUpperCase() + stat.slice(1);
|
||||
let effMultiplier = inst.skillMultipliers[key];
|
||||
if (effMultiplier == null) {
|
||||
|
@ -15,6 +15,8 @@ import { Skill } from "./Skill";
|
||||
import { City } from "./City";
|
||||
import { IAction } from "./IAction";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker";
|
||||
import { IPerson } from "../PersonObjects/IPerson";
|
||||
import { IRouter, Page } from "../ui/Router";
|
||||
import { ConsoleHelpText } from "./data/Help";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
@ -52,6 +54,7 @@ export class Bladeburner implements IBladeburner {
|
||||
totalSkillPoints = 0;
|
||||
|
||||
teamSize = 0;
|
||||
sleeveSize = 0;
|
||||
teamLost = 0;
|
||||
hpLost = 0;
|
||||
|
||||
@ -158,7 +161,7 @@ export class Bladeburner implements IBladeburner {
|
||||
return { isAvailable: true, action };
|
||||
}
|
||||
|
||||
startAction(player: IPlayer, actionId: IActionIdentifier): void {
|
||||
startAction(person: IPerson, actionId: IActionIdentifier): void {
|
||||
if (actionId == null) return;
|
||||
this.action = actionId;
|
||||
this.actionTimeCurrent = 0;
|
||||
@ -175,7 +178,7 @@ export class Bladeburner implements IBladeburner {
|
||||
if (action.count < 1) {
|
||||
return this.resetAction();
|
||||
}
|
||||
this.actionTimeToComplete = action.getActionTime(this);
|
||||
this.actionTimeToComplete = action.getActionTime(this, person);
|
||||
} catch (e: any) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
@ -192,7 +195,7 @@ export class Bladeburner implements IBladeburner {
|
||||
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
|
||||
return this.resetAction();
|
||||
}
|
||||
this.actionTimeToComplete = action.getActionTime(this);
|
||||
this.actionTimeToComplete = action.getActionTime(this, person);
|
||||
} catch (e: any) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
@ -210,14 +213,14 @@ export class Bladeburner implements IBladeburner {
|
||||
if (testBlackOp.action === undefined) {
|
||||
throw new Error("action should not be null");
|
||||
}
|
||||
this.actionTimeToComplete = testBlackOp.action.getActionTime(this);
|
||||
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person);
|
||||
} catch (e: any) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionTypes["Recruitment"]:
|
||||
this.actionTimeToComplete = this.getRecruitmentTime(player);
|
||||
this.actionTimeToComplete = this.getRecruitmentTime(person);
|
||||
break;
|
||||
case ActionTypes["Training"]:
|
||||
case ActionTypes["FieldAnalysis"]:
|
||||
@ -996,11 +999,11 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process stat gains from Contracts, Operations, and Black Operations
|
||||
* Return stat to be gained from Contracts, Operations, and Black Operations
|
||||
* @param action(Action obj) - Derived action class
|
||||
* @param success(bool) - Whether action was successful
|
||||
*/
|
||||
gainActionStats(player: IPlayer, action: IAction, success: boolean): void {
|
||||
getActionStats(action: IAction, success: boolean): ITaskTracker {
|
||||
const difficulty = action.getDifficulty();
|
||||
|
||||
/**
|
||||
@ -1017,34 +1020,48 @@ export class Bladeburner implements IBladeburner {
|
||||
const unweightedGain = time * BladeburnerConstants.BaseStatGain * successMult * difficultyMult;
|
||||
const unweightedIntGain = time * BladeburnerConstants.BaseIntGain * successMult * difficultyMult;
|
||||
const skillMult = this.skillMultipliers.expGain;
|
||||
player.gainHackingExp(unweightedGain * action.weights.hack * player.hacking_exp_mult * skillMult);
|
||||
player.gainStrengthExp(unweightedGain * action.weights.str * player.strength_exp_mult * skillMult);
|
||||
player.gainDefenseExp(unweightedGain * action.weights.def * player.defense_exp_mult * skillMult);
|
||||
player.gainDexterityExp(unweightedGain * action.weights.dex * player.dexterity_exp_mult * skillMult);
|
||||
player.gainAgilityExp(unweightedGain * action.weights.agi * player.agility_exp_mult * skillMult);
|
||||
player.gainCharismaExp(unweightedGain * action.weights.cha * player.charisma_exp_mult * skillMult);
|
||||
player.gainIntelligenceExp(unweightedIntGain * action.weights.int * skillMult);
|
||||
|
||||
return {
|
||||
hack: unweightedGain * action.weights.hack * skillMult,
|
||||
str: unweightedGain * action.weights.str * skillMult,
|
||||
def: unweightedGain * action.weights.def * skillMult,
|
||||
dex: unweightedGain * action.weights.dex * skillMult,
|
||||
agi: unweightedGain * action.weights.agi * skillMult,
|
||||
cha: unweightedGain * action.weights.cha * skillMult,
|
||||
int: unweightedIntGain * action.weights.int * skillMult,
|
||||
money: 0,
|
||||
};
|
||||
}
|
||||
|
||||
getDiplomacyEffectiveness(player: IPlayer): number {
|
||||
getDiplomacyEffectiveness(person: IPerson): number {
|
||||
// Returns a decimal by which the city's chaos level should be multiplied (e.g. 0.98)
|
||||
const CharismaLinearFactor = 1e3;
|
||||
const CharismaExponentialFactor = 0.045;
|
||||
|
||||
const charismaEff = Math.pow(player.charisma, CharismaExponentialFactor) + player.charisma / CharismaLinearFactor;
|
||||
const charismaEff = Math.pow(person.charisma, CharismaExponentialFactor) + person.charisma / CharismaLinearFactor;
|
||||
return (100 - charismaEff) / 100;
|
||||
}
|
||||
|
||||
getRecruitmentSuccessChance(player: IPlayer): number {
|
||||
return Math.pow(player.charisma, 0.45) / (this.teamSize + 1);
|
||||
getRecruitmentSuccessChance(person: IPerson): number {
|
||||
return Math.pow(person.charisma, 0.45) / (this.teamSize - this.sleeveSize + 1);
|
||||
}
|
||||
|
||||
getRecruitmentTime(player: IPlayer): number {
|
||||
const effCharisma = player.charisma * this.skillMultipliers.effCha;
|
||||
getRecruitmentTime(person: IPerson): number {
|
||||
const effCharisma = person.charisma * this.skillMultipliers.effCha;
|
||||
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
|
||||
return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor));
|
||||
}
|
||||
|
||||
sleeveSupport(joining: boolean): void {
|
||||
if (joining) {
|
||||
this.sleeveSize += 1;
|
||||
this.teamSize += 1;
|
||||
} else {
|
||||
this.sleeveSize -= 1;
|
||||
this.teamSize -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
resetSkillMultipliers(): void {
|
||||
this.skillMultipliers = {
|
||||
successChanceAll: 1,
|
||||
@ -1096,7 +1113,7 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
}
|
||||
|
||||
completeOperation(success: boolean): void {
|
||||
completeOperation(success: boolean, player: IPlayer): void {
|
||||
if (this.action.type !== ActionTypes.Operation) {
|
||||
throw new Error("completeOperation() called even though current action is not an Operation");
|
||||
}
|
||||
@ -1116,6 +1133,15 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
const losses = getRandomInt(0, max);
|
||||
this.teamSize -= losses;
|
||||
if (this.teamSize < this.sleeveSize) {
|
||||
const sup = player.sleeves.filter((x) => x.bbAction == "Support main sleeve");
|
||||
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
|
||||
const r = Math.floor(Math.random() * sup.length);
|
||||
sup[r].takeDamage(sup[r].max_hp);
|
||||
sup.splice(r, 1);
|
||||
}
|
||||
this.teamSize += this.sleeveSize;
|
||||
}
|
||||
this.teamLost += losses;
|
||||
if (this.logging.ops && losses > 0) {
|
||||
this.log("Lost " + formatNumber(losses, 0) + " team members during this " + action.name);
|
||||
@ -1213,13 +1239,13 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
}
|
||||
|
||||
completeContract(success: boolean): void {
|
||||
if (this.action.type !== ActionTypes.Contract) {
|
||||
completeContract(success: boolean, actionIdent: IActionIdentifier): void {
|
||||
if (actionIdent.type !== ActionTypes.Contract) {
|
||||
throw new Error("completeContract() called even though current action is not a Contract");
|
||||
}
|
||||
const city = this.getCurrentCity();
|
||||
if (success) {
|
||||
switch (this.action.name) {
|
||||
switch (actionIdent.name) {
|
||||
case "Tracking":
|
||||
// Increase estimate accuracy by a relatively small amount
|
||||
city.improvePopulationEstimateByCount(getRandomInt(100, 1e3));
|
||||
@ -1233,20 +1259,21 @@ export class Bladeburner implements IBladeburner {
|
||||
city.changeChaosByCount(0.04);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid Action name in completeContract: " + this.action.name);
|
||||
throw new Error("Invalid Action name in completeContract: " + actionIdent.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completeAction(router: IRouter, player: IPlayer): void {
|
||||
switch (this.action.type) {
|
||||
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer = true): ITaskTracker {
|
||||
let retValue = createTaskTracker();
|
||||
switch (actionIdent.type) {
|
||||
case ActionTypes["Contract"]:
|
||||
case ActionTypes["Operation"]: {
|
||||
try {
|
||||
const isOperation = this.action.type === ActionTypes["Operation"];
|
||||
const action = this.getActionObject(this.action);
|
||||
const isOperation = actionIdent.type === ActionTypes["Operation"];
|
||||
const action = this.getActionObject(actionIdent);
|
||||
if (action == null) {
|
||||
throw new Error("Failed to get Contract/Operation Object for: " + this.action.name);
|
||||
throw new Error("Failed to get Contract/Operation Object for: " + actionIdent.name);
|
||||
}
|
||||
const difficulty = action.getDifficulty();
|
||||
const difficultyMultiplier =
|
||||
@ -1254,15 +1281,17 @@ export class Bladeburner implements IBladeburner {
|
||||
difficulty / BladeburnerConstants.DiffMultLinearFactor;
|
||||
const rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
|
||||
|
||||
if (isPlayer) {
|
||||
// Stamina loss is based on difficulty
|
||||
this.stamina -= BladeburnerConstants.BaseStaminaLoss * difficultyMultiplier;
|
||||
if (this.stamina < 0) {
|
||||
this.stamina = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Process Contract/Operation success/failure
|
||||
if (action.attempt(this)) {
|
||||
this.gainActionStats(player, action, true);
|
||||
if (action.attempt(this, person)) {
|
||||
retValue = this.getActionStats(action, true);
|
||||
++action.successes;
|
||||
--action.count;
|
||||
|
||||
@ -1270,7 +1299,7 @@ export class Bladeburner implements IBladeburner {
|
||||
let moneyGain = 0;
|
||||
if (!isOperation) {
|
||||
moneyGain = BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * this.skillMultipliers.money;
|
||||
player.gainMoney(moneyGain, "bladeburner");
|
||||
retValue.money = moneyGain;
|
||||
}
|
||||
|
||||
if (isOperation) {
|
||||
@ -1280,11 +1309,18 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
if (action.rankGain) {
|
||||
const gain = addOffset(action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank, 10);
|
||||
this.changeRank(player, gain);
|
||||
this.changeRank(person, gain);
|
||||
if (isOperation && this.logging.ops) {
|
||||
this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
action.name +
|
||||
" successfully completed! Gained " +
|
||||
formatNumber(gain, 3) +
|
||||
" rank",
|
||||
);
|
||||
} else if (!isOperation && this.logging.contracts) {
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
action.name +
|
||||
" contract successfully completed! Gained " +
|
||||
formatNumber(gain, 3) +
|
||||
@ -1293,22 +1329,22 @@ export class Bladeburner implements IBladeburner {
|
||||
);
|
||||
}
|
||||
}
|
||||
isOperation ? this.completeOperation(true) : this.completeContract(true);
|
||||
isOperation ? this.completeOperation(true, player) : this.completeContract(true, actionIdent);
|
||||
} else {
|
||||
this.gainActionStats(player, action, false);
|
||||
retValue = this.getActionStats(action, false);
|
||||
++action.failures;
|
||||
let loss = 0,
|
||||
damage = 0;
|
||||
if (action.rankLoss) {
|
||||
loss = addOffset(action.rankLoss * rewardMultiplier, 10);
|
||||
this.changeRank(player, -1 * loss);
|
||||
this.changeRank(person, -1 * loss);
|
||||
}
|
||||
if (action.hpLoss) {
|
||||
damage = action.hpLoss * difficultyMultiplier;
|
||||
damage = Math.ceil(addOffset(damage, 10));
|
||||
this.hpLost += damage;
|
||||
const cost = calculateHospitalizationCost(player, damage);
|
||||
if (player.takeDamage(damage)) {
|
||||
if (person.takeDamage(damage)) {
|
||||
++this.numHosp;
|
||||
this.moneyLost += cost;
|
||||
}
|
||||
@ -1321,16 +1357,15 @@ export class Bladeburner implements IBladeburner {
|
||||
logLossText += "Took " + formatNumber(damage, 0) + " damage.";
|
||||
}
|
||||
if (isOperation && this.logging.ops) {
|
||||
this.log(action.name + " failed! " + logLossText);
|
||||
this.log(`${person.whoAmI()}: ` + action.name + " failed! " + logLossText);
|
||||
} else if (!isOperation && this.logging.contracts) {
|
||||
this.log(action.name + " contract failed! " + logLossText);
|
||||
this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText);
|
||||
}
|
||||
isOperation ? this.completeOperation(false) : this.completeContract(false);
|
||||
isOperation ? this.completeOperation(false, player) : this.completeContract(false, actionIdent);
|
||||
}
|
||||
if (action.autoLevel) {
|
||||
action.level = action.maxLevel;
|
||||
} // Autolevel
|
||||
this.startAction(player, this.action); // Repeat action
|
||||
} catch (e: any) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
@ -1339,9 +1374,9 @@ export class Bladeburner implements IBladeburner {
|
||||
case ActionTypes["BlackOp"]:
|
||||
case ActionTypes["BlackOperation"]: {
|
||||
try {
|
||||
const action = this.getActionObject(this.action);
|
||||
const action = this.getActionObject(actionIdent);
|
||||
if (action == null || !(action instanceof BlackOperation)) {
|
||||
throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
|
||||
throw new Error("Failed to get BlackOperation Object for: " + actionIdent.name);
|
||||
}
|
||||
const difficulty = action.getDifficulty();
|
||||
const difficultyMultiplier =
|
||||
@ -1358,39 +1393,35 @@ export class Bladeburner implements IBladeburner {
|
||||
const teamCount = action.teamCount;
|
||||
let teamLossMax;
|
||||
|
||||
if (action.attempt(this)) {
|
||||
this.gainActionStats(player, action, true);
|
||||
if (action.attempt(this, person)) {
|
||||
retValue = this.getActionStats(action, true);
|
||||
action.count = 0;
|
||||
this.blackops[action.name] = true;
|
||||
let rankGain = 0;
|
||||
if (action.rankGain) {
|
||||
rankGain = addOffset(action.rankGain * BitNodeMultipliers.BladeburnerRank, 10);
|
||||
this.changeRank(player, rankGain);
|
||||
this.changeRank(person, rankGain);
|
||||
}
|
||||
teamLossMax = Math.ceil(teamCount / 2);
|
||||
|
||||
// Operation Daedalus
|
||||
if (action.name === BlackOperationNames.OperationDaedalus) {
|
||||
this.resetAction();
|
||||
return router.toBitVerse(false, false);
|
||||
}
|
||||
|
||||
if (this.logging.blackops) {
|
||||
this.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank");
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` + action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.gainActionStats(player, action, false);
|
||||
retValue = this.getActionStats(action, false);
|
||||
let rankLoss = 0;
|
||||
let damage = 0;
|
||||
if (action.rankLoss) {
|
||||
rankLoss = addOffset(action.rankLoss, 10);
|
||||
this.changeRank(player, -1 * rankLoss);
|
||||
this.changeRank(person, -1 * rankLoss);
|
||||
}
|
||||
if (action.hpLoss) {
|
||||
damage = action.hpLoss * difficultyMultiplier;
|
||||
damage = Math.ceil(addOffset(damage, 10));
|
||||
const cost = calculateHospitalizationCost(player, damage);
|
||||
if (player.takeDamage(damage)) {
|
||||
if (person.takeDamage(damage)) {
|
||||
++this.numHosp;
|
||||
this.moneyLost += cost;
|
||||
}
|
||||
@ -1399,6 +1430,7 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
if (this.logging.blackops) {
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
action.name +
|
||||
" failed! Lost " +
|
||||
formatNumber(rankLoss, 1) +
|
||||
@ -1415,9 +1447,18 @@ export class Bladeburner implements IBladeburner {
|
||||
if (teamCount >= 1) {
|
||||
const losses = getRandomInt(1, teamLossMax);
|
||||
this.teamSize -= losses;
|
||||
if (this.teamSize < this.sleeveSize) {
|
||||
const sup = player.sleeves.filter((x) => x.bbAction == "Support main sleeve");
|
||||
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
|
||||
const r = Math.floor(Math.random() * sup.length);
|
||||
sup[r].takeDamage(sup[r].max_hp);
|
||||
sup.splice(r, 1);
|
||||
}
|
||||
this.teamSize += this.sleeveSize;
|
||||
}
|
||||
this.teamLost += losses;
|
||||
if (this.logging.blackops) {
|
||||
this.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
|
||||
this.log(`${person.whoAmI()}: You lost ${formatNumber(losses, 0)} team members during ${action.name}`);
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
@ -1427,18 +1468,19 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
case ActionTypes["Training"]: {
|
||||
this.stamina -= 0.5 * BladeburnerConstants.BaseStaminaLoss;
|
||||
const strExpGain = 30 * player.strength_exp_mult,
|
||||
defExpGain = 30 * player.defense_exp_mult,
|
||||
dexExpGain = 30 * player.dexterity_exp_mult,
|
||||
agiExpGain = 30 * player.agility_exp_mult,
|
||||
const strExpGain = 30 * person.strength_exp_mult,
|
||||
defExpGain = 30 * person.defense_exp_mult,
|
||||
dexExpGain = 30 * person.dexterity_exp_mult,
|
||||
agiExpGain = 30 * person.agility_exp_mult,
|
||||
staminaGain = 0.04 * this.skillMultipliers.stamina;
|
||||
player.gainStrengthExp(strExpGain);
|
||||
player.gainDefenseExp(defExpGain);
|
||||
player.gainDexterityExp(dexExpGain);
|
||||
player.gainAgilityExp(agiExpGain);
|
||||
retValue.str = strExpGain;
|
||||
retValue.def = defExpGain;
|
||||
retValue.dex = dexExpGain;
|
||||
retValue.agi = agiExpGain;
|
||||
this.staminaBonus += staminaGain;
|
||||
if (this.logging.general) {
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
"Training completed. Gained: " +
|
||||
formatNumber(strExpGain, 1) +
|
||||
" str exp, " +
|
||||
@ -1452,80 +1494,89 @@ export class Bladeburner implements IBladeburner {
|
||||
" max stamina",
|
||||
);
|
||||
}
|
||||
this.startAction(player, this.action); // Repeat action
|
||||
break;
|
||||
}
|
||||
case ActionTypes["FieldAnalysis"]:
|
||||
case ActionTypes["Field Analysis"]: {
|
||||
// Does not use stamina. Effectiveness depends on hacking, int, and cha
|
||||
let eff =
|
||||
0.04 * Math.pow(player.hacking, 0.3) +
|
||||
0.04 * Math.pow(player.intelligence, 0.9) +
|
||||
0.02 * Math.pow(player.charisma, 0.3);
|
||||
eff *= player.bladeburner_analysis_mult;
|
||||
0.04 * Math.pow(person.hacking, 0.3) +
|
||||
0.04 * Math.pow(person.intelligence, 0.9) +
|
||||
0.02 * Math.pow(person.charisma, 0.3);
|
||||
eff *= person.bladeburner_analysis_mult;
|
||||
if (isNaN(eff) || eff < 0) {
|
||||
throw new Error("Field Analysis Effectiveness calculated to be NaN or negative");
|
||||
}
|
||||
const hackingExpGain = 20 * player.hacking_exp_mult;
|
||||
const charismaExpGain = 20 * player.charisma_exp_mult;
|
||||
const hackingExpGain = 20 * person.hacking_exp_mult;
|
||||
const charismaExpGain = 20 * person.charisma_exp_mult;
|
||||
const rankGain = 0.1 * BitNodeMultipliers.BladeburnerRank;
|
||||
player.gainHackingExp(hackingExpGain);
|
||||
player.gainIntelligenceExp(BladeburnerConstants.BaseIntGain);
|
||||
player.gainCharismaExp(charismaExpGain);
|
||||
this.changeRank(player, rankGain);
|
||||
retValue.hack = hackingExpGain;
|
||||
retValue.cha = charismaExpGain;
|
||||
retValue.int = BladeburnerConstants.BaseIntGain;
|
||||
this.changeRank(person, rankGain);
|
||||
this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate);
|
||||
if (this.logging.general) {
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
`Field analysis completed. Gained ${formatNumber(rankGain, 2)} rank, ` +
|
||||
`${formatNumber(hackingExpGain, 1)} hacking exp, and ` +
|
||||
`${formatNumber(charismaExpGain, 1)} charisma exp`,
|
||||
);
|
||||
}
|
||||
this.startAction(player, this.action); // Repeat action
|
||||
break;
|
||||
}
|
||||
case ActionTypes["Recruitment"]: {
|
||||
const successChance = this.getRecruitmentSuccessChance(player);
|
||||
const successChance = this.getRecruitmentSuccessChance(person);
|
||||
const recruitTime = this.getRecruitmentTime(person) * 1000;
|
||||
if (Math.random() < successChance) {
|
||||
const expGain = 2 * BladeburnerConstants.BaseStatGain * this.actionTimeToComplete;
|
||||
player.gainCharismaExp(expGain);
|
||||
const expGain = 2 * BladeburnerConstants.BaseStatGain * recruitTime;
|
||||
retValue.cha = expGain;
|
||||
++this.teamSize;
|
||||
if (this.logging.general) {
|
||||
this.log("Successfully recruited a team member! Gained " + formatNumber(expGain, 1) + " charisma exp");
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
"Successfully recruited a team member! Gained " +
|
||||
formatNumber(expGain, 1) +
|
||||
" charisma exp",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const expGain = BladeburnerConstants.BaseStatGain * this.actionTimeToComplete;
|
||||
player.gainCharismaExp(expGain);
|
||||
const expGain = BladeburnerConstants.BaseStatGain * recruitTime;
|
||||
retValue.cha = expGain;
|
||||
if (this.logging.general) {
|
||||
this.log("Failed to recruit a team member. Gained " + formatNumber(expGain, 1) + " charisma exp");
|
||||
this.log(
|
||||
`${person.whoAmI()}: ` +
|
||||
"Failed to recruit a team member. Gained " +
|
||||
formatNumber(expGain, 1) +
|
||||
" charisma exp",
|
||||
);
|
||||
}
|
||||
}
|
||||
this.startAction(player, this.action); // Repeat action
|
||||
break;
|
||||
}
|
||||
case ActionTypes["Diplomacy"]: {
|
||||
const eff = this.getDiplomacyEffectiveness(player);
|
||||
const eff = this.getDiplomacyEffectiveness(person);
|
||||
this.getCurrentCity().chaos *= eff;
|
||||
if (this.getCurrentCity().chaos < 0) {
|
||||
this.getCurrentCity().chaos = 0;
|
||||
}
|
||||
if (this.logging.general) {
|
||||
this.log(
|
||||
`Diplomacy completed. Chaos levels in the current city fell by ${numeralWrapper.formatPercentage(1 - eff)}`,
|
||||
`${person.whoAmI()}: Diplomacy completed. Chaos levels in the current city fell by ${numeralWrapper.formatPercentage(
|
||||
1 - eff,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
this.startAction(player, this.action); // Repeat Action
|
||||
break;
|
||||
}
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]: {
|
||||
player.regenerateHp(BladeburnerConstants.HrcHpGain);
|
||||
person.regenerateHp(BladeburnerConstants.HrcHpGain);
|
||||
|
||||
const staminaGain = this.maxStamina * (BladeburnerConstants.HrcStaminaGain / 100);
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
|
||||
this.startAction(player, this.action);
|
||||
if (this.logging.general) {
|
||||
this.log(
|
||||
`Rested in Hyperbolic Regeneration Chamber. Restored ${
|
||||
`${person.whoAmI()}: Rested in Hyperbolic Regeneration Chamber. Restored ${
|
||||
BladeburnerConstants.HrcHpGain
|
||||
} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`,
|
||||
);
|
||||
@ -1544,24 +1595,37 @@ export class Bladeburner implements IBladeburner {
|
||||
this.operations[operation].count += (60 * 3 * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod;
|
||||
}
|
||||
if (this.logging.general) {
|
||||
this.log(`Incited violence in the synthoid communities.`);
|
||||
this.log(`${person.whoAmI()}: Incited violence in the synthoid communities.`);
|
||||
}
|
||||
for (const cityName of Object.keys(this.cities)) {
|
||||
const city = this.cities[cityName];
|
||||
city.chaos += 10;
|
||||
city.chaos += city.chaos / (Math.log(city.chaos) / Math.log(10));
|
||||
}
|
||||
|
||||
this.startAction(player, this.action);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
|
||||
console.error(`Bladeburner.completeAction() called for invalid action: ${actionIdent.type}`);
|
||||
break;
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
infiltrateSynthoidCommunities(p: IPlayer): void {
|
||||
const infilSleeves = p.sleeves.filter((s) => s.bbAction === "Infiltrate synthoids").length;
|
||||
const amt = Math.pow(infilSleeves, -0.5) / 2;
|
||||
for (const contract of Object.keys(this.contracts)) {
|
||||
this.contracts[contract].count += amt;
|
||||
}
|
||||
for (const operation of Object.keys(this.operations)) {
|
||||
this.operations[operation].count += amt;
|
||||
}
|
||||
if (this.logging.general) {
|
||||
this.log(`Sleeve: Infiltrate the synthoid communities.`);
|
||||
}
|
||||
}
|
||||
|
||||
changeRank(player: IPlayer, change: number): void {
|
||||
changeRank(person: IPerson, change: number): void {
|
||||
if (isNaN(change)) {
|
||||
throw new Error("NaN passed into Bladeburner.changeRank()");
|
||||
}
|
||||
@ -1582,7 +1646,7 @@ export class Bladeburner implements IBladeburner {
|
||||
if (bladeburnerFac.isMember) {
|
||||
const favorBonus = 1 + bladeburnerFac.favor / 100;
|
||||
bladeburnerFac.playerReputation +=
|
||||
BladeburnerConstants.RankToFactionRepFactor * change * player.faction_rep_mult * favorBonus;
|
||||
BladeburnerConstants.RankToFactionRepFactor * change * person.faction_rep_mult * favorBonus;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1613,7 +1677,19 @@ export class Bladeburner implements IBladeburner {
|
||||
this.actionTimeOverflow = 0;
|
||||
if (this.actionTimeCurrent >= this.actionTimeToComplete) {
|
||||
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
|
||||
return this.completeAction(router, player);
|
||||
const retValue = this.completeAction(player, player, this.action);
|
||||
player.gainMoney(retValue.money, "bladeburner");
|
||||
player.gainStats(retValue);
|
||||
// Operation Daedalus
|
||||
const action = this.getActionObject(this.action);
|
||||
if (action == null) {
|
||||
throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
|
||||
} else if (action.name === BlackOperationNames.OperationDaedalus && this.blackops[action.name]) {
|
||||
this.resetAction();
|
||||
router.toBitVerse(false, false);
|
||||
} else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) {
|
||||
this.startAction(player, this.action); // Repeat action
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2092,67 +2168,53 @@ export class Bladeburner implements IBladeburner {
|
||||
}
|
||||
}
|
||||
|
||||
getActionTimeNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string {
|
||||
const actionId = this.getActionIdFromTypeAndName(type, name);
|
||||
if (actionId == null) {
|
||||
workerScript.log("bladeburner.getActionTime", () => errorLogText);
|
||||
return -1;
|
||||
return "bladeburner.getActionTime";
|
||||
}
|
||||
|
||||
const actionObj = this.getActionObject(actionId);
|
||||
if (actionObj == null) {
|
||||
workerScript.log("bladeburner.getActionTime", () => errorLogText);
|
||||
return -1;
|
||||
return "bladeburner.getActionTime";
|
||||
}
|
||||
|
||||
switch (actionId.type) {
|
||||
case ActionTypes["Contract"]:
|
||||
case ActionTypes["Operation"]:
|
||||
case ActionTypes["BlackOp"]:
|
||||
case ActionTypes["BlackOperation"]:
|
||||
return actionObj.getActionTime(this) * 1000;
|
||||
return actionObj.getActionTime(this, person) * 1000;
|
||||
case ActionTypes["Training"]:
|
||||
case ActionTypes["Field Analysis"]:
|
||||
case ActionTypes["FieldAnalysis"]:
|
||||
return 30000;
|
||||
case ActionTypes["Recruitment"]:
|
||||
return this.getRecruitmentTime(player) * 1000;
|
||||
return this.getRecruitmentTime(person) * 1000;
|
||||
case ActionTypes["Diplomacy"]:
|
||||
case ActionTypes["Hyperbolic Regeneration Chamber"]:
|
||||
case ActionTypes["Incite Violence"]:
|
||||
return 60000;
|
||||
default:
|
||||
workerScript.log("bladeburner.getActionTime", () => errorLogText);
|
||||
return -1;
|
||||
return "bladeburner.getActionTime";
|
||||
}
|
||||
}
|
||||
|
||||
getActionEstimatedSuccessChanceNetscriptFn(
|
||||
player: IPlayer,
|
||||
type: string,
|
||||
name: string,
|
||||
workerScript: WorkerScript,
|
||||
): [number, number] {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string {
|
||||
const actionId = this.getActionIdFromTypeAndName(type, name);
|
||||
if (actionId == null) {
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", () => errorLogText);
|
||||
return [-1, -1];
|
||||
return "bladeburner.getActionEstimatedSuccessChance";
|
||||
}
|
||||
|
||||
const actionObj = this.getActionObject(actionId);
|
||||
if (actionObj == null) {
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", () => errorLogText);
|
||||
return [-1, -1];
|
||||
return "bladeburner.getActionEstimatedSuccessChance";
|
||||
}
|
||||
|
||||
switch (actionId.type) {
|
||||
case ActionTypes["Contract"]:
|
||||
case ActionTypes["Operation"]:
|
||||
case ActionTypes["BlackOp"]:
|
||||
case ActionTypes["BlackOperation"]:
|
||||
return actionObj.getEstSuccessChance(this);
|
||||
return actionObj.getEstSuccessChance(this, person);
|
||||
case ActionTypes["Training"]:
|
||||
case ActionTypes["Field Analysis"]:
|
||||
case ActionTypes["FieldAnalysis"]:
|
||||
@ -2161,12 +2223,11 @@ export class Bladeburner implements IBladeburner {
|
||||
case ActionTypes["Incite Violence"]:
|
||||
return [1, 1];
|
||||
case ActionTypes["Recruitment"]: {
|
||||
const recChance = this.getRecruitmentSuccessChance(player);
|
||||
const recChance = this.getRecruitmentSuccessChance(person);
|
||||
return [recChance, recChance];
|
||||
}
|
||||
default:
|
||||
workerScript.log("bladeburner.getActionEstimatedSuccessChance", () => errorLogText);
|
||||
return [-1, -1];
|
||||
return "bladeburner.getActionEstimatedSuccessChance";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { IPerson } from "../PersonObjects/IPerson";
|
||||
import { IBladeburner } from "./IBladeburner";
|
||||
|
||||
interface IStatsMultiplier {
|
||||
@ -55,15 +56,15 @@ export interface IAction {
|
||||
teamCount: number;
|
||||
|
||||
getDifficulty(): number;
|
||||
attempt(inst: IBladeburner): boolean;
|
||||
attempt(inst: IBladeburner, person: IPerson): boolean;
|
||||
getActionTimePenalty(): number;
|
||||
getActionTime(inst: IBladeburner): number;
|
||||
getActionTime(inst: IBladeburner, person: IPerson): number;
|
||||
getTeamSuccessBonus(inst: IBladeburner): number;
|
||||
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
|
||||
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||
getChaosDifficultyBonus(inst: IBladeburner): number;
|
||||
getEstSuccessChance(inst: IBladeburner): [number, number];
|
||||
getSuccessChance(inst: IBladeburner, params: ISuccessChanceParams): number;
|
||||
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number];
|
||||
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams): number;
|
||||
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
|
||||
setMaxLevel(baseSuccessesPerLevel: number): void;
|
||||
toJSON(): any;
|
||||
|
@ -3,6 +3,8 @@ import { City } from "./City";
|
||||
import { Skill } from "./Skill";
|
||||
import { IAction } from "./IAction";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IPerson } from "../PersonObjects/IPerson";
|
||||
import { ITaskTracker } from "../PersonObjects/ITaskTracker";
|
||||
import { IRouter } from "../ui/Router";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
|
||||
@ -70,13 +72,8 @@ export interface IBladeburner {
|
||||
getGeneralActionNamesNetscriptFn(): string[];
|
||||
getSkillNamesNetscriptFn(): string[];
|
||||
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
|
||||
getActionTimeNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): number;
|
||||
getActionEstimatedSuccessChanceNetscriptFn(
|
||||
player: IPlayer,
|
||||
type: string,
|
||||
name: string,
|
||||
workerScript: WorkerScript,
|
||||
): [number, number];
|
||||
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string;
|
||||
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string;
|
||||
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
|
||||
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
||||
getSkillUpgradeCostNetscriptFn(skillName: string, workerScript: WorkerScript): number;
|
||||
@ -95,20 +92,22 @@ export interface IBladeburner {
|
||||
triggerMigration(sourceCityName: string): void;
|
||||
triggerPotentialMigration(sourceCityName: string, chance: number): void;
|
||||
randomEvent(): void;
|
||||
gainActionStats(player: IPlayer, action: IAction, success: boolean): void;
|
||||
getDiplomacyEffectiveness(player: IPlayer): number;
|
||||
getRecruitmentSuccessChance(player: IPlayer): number;
|
||||
getRecruitmentTime(player: IPlayer): number;
|
||||
getRecruitmentSuccessChance(player: IPerson): number;
|
||||
getRecruitmentTime(player: IPerson): number;
|
||||
resetSkillMultipliers(): void;
|
||||
updateSkillMultipliers(): void;
|
||||
completeOperation(success: boolean): void;
|
||||
completeOperation(success: boolean, player: IPlayer): void;
|
||||
getActionObject(actionId: IActionIdentifier): IAction | null;
|
||||
completeContract(success: boolean): void;
|
||||
completeAction(router: IRouter, player: IPlayer): void;
|
||||
completeContract(success: boolean, actionIdent: IActionIdentifier): void;
|
||||
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer?: boolean): ITaskTracker;
|
||||
infiltrateSynthoidCommunities(p: IPlayer): void;
|
||||
changeRank(player: IPlayer, change: number): void;
|
||||
processAction(router: IRouter, player: IPlayer, seconds: number): void;
|
||||
calculateStaminaGainPerSecond(player: IPlayer): number;
|
||||
calculateMaxStamina(player: IPlayer): void;
|
||||
create(): void;
|
||||
process(router: IRouter, player: IPlayer): void;
|
||||
getActionStats(action: IAction, success: boolean): ITaskTracker;
|
||||
sleeveSupport(joining: boolean): void;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
||||
props.action.name === props.bladeburner.action.name;
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner, props.player);
|
||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
|
@ -32,7 +32,7 @@ export function ContractElem(props: IProps): React.ReactElement {
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner, props.player);
|
||||
|
||||
const actionData = Contracts[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
|
@ -33,7 +33,7 @@ export function OperationElem(props: IProps): React.ReactElement {
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner, props.player);
|
||||
|
||||
const actionData = Operations[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
|
@ -4,6 +4,7 @@ import { StealthIcon } from "./StealthIcon";
|
||||
import { KillIcon } from "./KillIcon";
|
||||
import { IAction } from "../IAction";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { Player } from "../../Player";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,7 +12,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function SuccessChance(props: IProps): React.ReactElement {
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner, Player);
|
||||
|
||||
let chance = <></>;
|
||||
if (estimatedSuccessChance[0] === estimatedSuccessChance[1]) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
|
||||
import { IPerson } from "../PersonObjects/IPerson";
|
||||
import { IRouter } from "../ui/Router";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { CrimeType } from "../utils/WorkType";
|
||||
@ -117,7 +117,7 @@ export class Crime {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
successRate(p: IPlayerOrSleeve): number {
|
||||
successRate(p: IPerson): number {
|
||||
let chance: number =
|
||||
this.hacking_success_weight * p.hacking +
|
||||
this.strength_success_weight * p.strength +
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
export function getHospitalizationCost(p: IPlayer): number {
|
||||
if (p.money < 0) {
|
||||
|
@ -284,6 +284,7 @@ const sleeve: IMap<any> = {
|
||||
getSleeveAugmentations: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleevePurchasableAugs: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
purchaseSleeveAug: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToBladeburnerAction: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
};
|
||||
|
||||
// Stanek API
|
||||
|
@ -125,7 +125,14 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionTimeNetscriptFn(player, type, name, workerScript);
|
||||
const time = bladeburner.getActionTimeNetscriptFn(player, type, name);
|
||||
if (typeof time === "string") {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
ctx.log(() => errorLogText);
|
||||
return -1;
|
||||
} else {
|
||||
return time;
|
||||
}
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
@ -139,7 +146,14 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
|
||||
try {
|
||||
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name, workerScript);
|
||||
const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name);
|
||||
if (typeof chance === "string") {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
ctx.log(() => errorLogText);
|
||||
return [-1, -1];
|
||||
} else {
|
||||
return chance;
|
||||
}
|
||||
} catch (e: any) {
|
||||
throw ctx.makeRuntimeErrorMsg(e);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import { Router } from "../ui/GameRoot";
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
import { Page } from "../ui/Router";
|
||||
import { Locations } from "../Locations/Locations";
|
||||
import { GetServer, AddToAllServers, createUniqueRandomIp } from "../Server/AllServers";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
@ -39,7 +39,7 @@ import { Factions, factionExists } from "../Faction/Factions";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { netscriptDelay } from "../NetscriptEvaluator";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { getServerOnNetwork, safetlyCreateUniqueServer } from "../Server/ServerHelpers";
|
||||
import { getServerOnNetwork } from "../Server/ServerHelpers";
|
||||
import { Terminal } from "../Terminal";
|
||||
import { calculateHackingTime } from "../Hacking";
|
||||
import { Server } from "../Server/Server";
|
||||
|
@ -310,5 +310,36 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI<ISleeve> {
|
||||
|
||||
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug);
|
||||
},
|
||||
setToBladeburnerAction:
|
||||
(ctx: NetscriptContext) =>
|
||||
(_sleeveNumber: unknown, _action: unknown, _contract?: unknown): boolean => {
|
||||
const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber);
|
||||
const action = ctx.helper.string("action", _action);
|
||||
let contract: string;
|
||||
if (typeof _contract === "undefined") {
|
||||
contract = "------";
|
||||
} else {
|
||||
contract = ctx.helper.string("contract", _contract);
|
||||
}
|
||||
checkSleeveAPIAccess(ctx);
|
||||
checkSleeveNumber(ctx, sleeveNumber);
|
||||
|
||||
// Cannot Take on Contracts if another sleeve is performing that action
|
||||
if (action === "Take on contracts") {
|
||||
for (let i = 0; i < player.sleeves.length; ++i) {
|
||||
if (i === sleeveNumber) {
|
||||
continue;
|
||||
}
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Bladeburner && other.bbAction === action) {
|
||||
throw ctx.helper.makeRuntimeErrorMsg(
|
||||
`Sleeve ${sleeveNumber} cannot take of contracts because Sleeve ${i} is already performing that action.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return player.sleeves[sleeveNumber].bladeburner(player, action, contract);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
66
src/PersonObjects/IPerson.ts
Normal file
66
src/PersonObjects/IPerson.ts
Normal file
@ -0,0 +1,66 @@
|
||||
// Interface that represents either the player (PlayerObject) or
|
||||
// a Sleeve. Used for functions that need to take in both.
|
||||
|
||||
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { ITaskTracker } from "./ITaskTracker";
|
||||
|
||||
export interface IPerson {
|
||||
// Stats
|
||||
hacking: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
hp: number;
|
||||
max_hp: number;
|
||||
|
||||
// Experience
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
intelligence_exp: number;
|
||||
|
||||
// Multipliers
|
||||
hacking_exp_mult: number;
|
||||
strength_exp_mult: number;
|
||||
defense_exp_mult: number;
|
||||
dexterity_exp_mult: number;
|
||||
agility_exp_mult: number;
|
||||
charisma_exp_mult: number;
|
||||
hacking_mult: number;
|
||||
strength_mult: number;
|
||||
defense_mult: number;
|
||||
dexterity_mult: number;
|
||||
agility_mult: number;
|
||||
charisma_mult: number;
|
||||
|
||||
company_rep_mult: number;
|
||||
faction_rep_mult: number;
|
||||
|
||||
crime_money_mult: number;
|
||||
crime_success_mult: number;
|
||||
|
||||
bladeburner_analysis_mult: number;
|
||||
|
||||
augmentations: IPlayerOwnedAugmentation[];
|
||||
|
||||
getIntelligenceBonus(weight: number): number;
|
||||
gainHackingExp(exp: number): void;
|
||||
gainStrengthExp(exp: number): void;
|
||||
gainDefenseExp(exp: number): void;
|
||||
gainDexterityExp(exp: number): void;
|
||||
gainAgilityExp(exp: number): void;
|
||||
gainCharismaExp(exp: number): void;
|
||||
gainIntelligenceExp(exp: number): void;
|
||||
gainStats(retValue: ITaskTracker): void;
|
||||
calculateSkill(exp: number, mult?: number): number;
|
||||
takeDamage(amt: number): boolean;
|
||||
regenerateHp: (amt: number) => void;
|
||||
queryStatFromString: (str: string) => number;
|
||||
whoAmI: () => string;
|
||||
}
|
@ -30,11 +30,10 @@ import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { ISkillProgress } from "./formulas/skill";
|
||||
import { PlayerAchievement } from "../Achievements/Achievements";
|
||||
import { IPerson } from "./IPerson";
|
||||
import { WorkType, ClassType, CrimeType } from "../utils/WorkType";
|
||||
|
||||
export interface IPlayer {
|
||||
// Class members
|
||||
augmentations: IPlayerOwnedAugmentation[];
|
||||
export interface IPlayer extends IPerson {
|
||||
bitNodeN: number;
|
||||
city: CityName;
|
||||
companyName: string;
|
||||
@ -186,13 +185,6 @@ export interface IPlayer {
|
||||
canAccessGang(): boolean;
|
||||
canAccessGrafting(): boolean;
|
||||
canAfford(cost: number): boolean;
|
||||
gainHackingExp(exp: number): void;
|
||||
gainStrengthExp(exp: number): void;
|
||||
gainDefenseExp(exp: number): void;
|
||||
gainDexterityExp(exp: number): void;
|
||||
gainAgilityExp(exp: number): void;
|
||||
gainCharismaExp(exp: number): void;
|
||||
gainIntelligenceExp(exp: number): void;
|
||||
gainMoney(money: number, source: string): void;
|
||||
getCurrentServer(): BaseServer;
|
||||
getGangFaction(): Faction;
|
||||
@ -215,7 +207,6 @@ export interface IPlayer {
|
||||
process(router: IRouter, numCycles?: number): void;
|
||||
reapplyAllAugmentations(resetMultipliers?: boolean): void;
|
||||
reapplyAllSourceFiles(): void;
|
||||
regenerateHp(amt: number): void;
|
||||
setMoney(amt: number): void;
|
||||
singularityStopWork(): string;
|
||||
startBladeburner(p: any): void;
|
||||
@ -242,12 +233,9 @@ export interface IPlayer {
|
||||
startGang(facName: string, isHacking: boolean): void;
|
||||
startWork(companyName: string): void;
|
||||
startWorkPartTime(companyName: string): void;
|
||||
takeDamage(amt: number): boolean;
|
||||
travel(to: CityName): boolean;
|
||||
giveExploit(exploit: Exploit): void;
|
||||
giveAchievement(achievementId: string): void;
|
||||
queryStatFromString(str: string): number;
|
||||
getIntelligenceBonus(weight: number): number;
|
||||
getCasinoWinnings(): number;
|
||||
quitJob(company: string, sing?: boolean): void;
|
||||
hasJob(): boolean;
|
||||
@ -268,7 +256,6 @@ export interface IPlayer {
|
||||
resetMultipliers(): void;
|
||||
prestigeAugmentation(): void;
|
||||
prestigeSourceFile(): void;
|
||||
calculateSkill(exp: number, mult?: number): number;
|
||||
calculateSkillProgress(exp: number, mult?: number): ISkillProgress;
|
||||
resetWorkStatus(generalType?: WorkType, group?: string, workType?: string): void;
|
||||
getWorkHackExpGain(): number;
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Interface that represents either the player (PlayerObject) or
|
||||
// a Sleeve. Used for functions that need to take in both.
|
||||
|
||||
export interface IPlayerOrSleeve {
|
||||
// Stats
|
||||
hacking: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
|
||||
// Experience
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
|
||||
// Multipliers
|
||||
crime_success_mult: number;
|
||||
|
||||
getIntelligenceBonus(weight: number): number;
|
||||
}
|
25
src/PersonObjects/ITaskTracker.ts
Normal file
25
src/PersonObjects/ITaskTracker.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// Interface that defines a generic object used to track experience/money
|
||||
// earnings for tasks
|
||||
export interface ITaskTracker {
|
||||
hack: number;
|
||||
str: number;
|
||||
def: number;
|
||||
dex: number;
|
||||
agi: number;
|
||||
cha: number;
|
||||
int: number;
|
||||
money: number;
|
||||
}
|
||||
|
||||
export function createTaskTracker(): ITaskTracker {
|
||||
return {
|
||||
hack: 0,
|
||||
str: 0,
|
||||
def: 0,
|
||||
dex: 0,
|
||||
agi: 0,
|
||||
cha: 0,
|
||||
int: 0,
|
||||
money: 0,
|
||||
};
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Base class representing a person-like object
|
||||
import * as generalMethods from "./Player/PlayerObjectGeneralMethods";
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
@ -6,32 +6,10 @@ import { CityName } from "../Locations/data/CityNames";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { calculateSkill } from "./formulas/skill";
|
||||
import { calculateIntelligenceBonus } from "./formulas/intelligence";
|
||||
import { IPerson } from "./IPerson";
|
||||
|
||||
// Interface that defines a generic object used to track experience/money
|
||||
// earnings for tasks
|
||||
export interface ITaskTracker {
|
||||
hack: number;
|
||||
str: number;
|
||||
def: number;
|
||||
dex: number;
|
||||
agi: number;
|
||||
cha: number;
|
||||
money: number;
|
||||
}
|
||||
|
||||
export function createTaskTracker(): ITaskTracker {
|
||||
return {
|
||||
hack: 0,
|
||||
str: 0,
|
||||
def: 0,
|
||||
dex: 0,
|
||||
agi: 0,
|
||||
cha: 0,
|
||||
money: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class Person {
|
||||
// Base class representing a person-like object
|
||||
export abstract class Person implements IPerson {
|
||||
/**
|
||||
* Stats
|
||||
*/
|
||||
@ -41,7 +19,7 @@ export abstract class Person {
|
||||
dexterity = 1;
|
||||
agility = 1;
|
||||
charisma = 1;
|
||||
intelligence = 1;
|
||||
intelligence = 0;
|
||||
hp = 10;
|
||||
max_hp = 10;
|
||||
|
||||
@ -97,24 +75,28 @@ export abstract class Person {
|
||||
bladeburner_analysis_mult = 1;
|
||||
bladeburner_success_chance_mult = 1;
|
||||
|
||||
infiltration_base_rep_increase = 0;
|
||||
infiltration_rep_mult = 1;
|
||||
infiltration_trade_mult = 1;
|
||||
infiltration_sell_mult = 1;
|
||||
infiltration_timer_mult = 1;
|
||||
infiltration_damage_reduction_mult = 1;
|
||||
|
||||
/**
|
||||
* Augmentations
|
||||
*/
|
||||
augmentations: IPlayerOwnedAugmentation[] = [];
|
||||
queuedAugmentations: IPlayerOwnedAugmentation[] = [];
|
||||
|
||||
/**
|
||||
* City that the person is in
|
||||
*/
|
||||
city: CityName = CityName.Sector12;
|
||||
|
||||
gainHackingExp = generalMethods.gainHackingExp;
|
||||
gainStrengthExp = generalMethods.gainStrengthExp;
|
||||
gainDefenseExp = generalMethods.gainDefenseExp;
|
||||
gainDexterityExp = generalMethods.gainDexterityExp;
|
||||
gainAgilityExp = generalMethods.gainAgilityExp;
|
||||
gainCharismaExp = generalMethods.gainCharismaExp;
|
||||
gainIntelligenceExp = generalMethods.gainIntelligenceExp;
|
||||
gainStats = generalMethods.gainStats;
|
||||
calculateSkill = generalMethods.calculateSkill;
|
||||
regenerateHp = generalMethods.regenerateHp;
|
||||
queryStatFromString = generalMethods.queryStatFromString;
|
||||
|
||||
/**
|
||||
* Updates this object's multipliers for the given augmentation
|
||||
*/
|
||||
@ -213,13 +195,6 @@ export abstract class Person {
|
||||
this.bladeburner_stamina_gain_mult = 1;
|
||||
this.bladeburner_analysis_mult = 1;
|
||||
this.bladeburner_success_chance_mult = 1;
|
||||
|
||||
this.infiltration_base_rep_increase = 0;
|
||||
this.infiltration_rep_mult = 1;
|
||||
this.infiltration_trade_mult = 1;
|
||||
this.infiltration_sell_mult = 1;
|
||||
this.infiltration_timer_mult = 1;
|
||||
this.infiltration_damage_reduction_mult = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,4 +240,8 @@ export abstract class Person {
|
||||
getIntelligenceBonus(weight: number): number {
|
||||
return calculateIntelligenceBonus(this.intelligence, weight);
|
||||
}
|
||||
|
||||
abstract takeDamage(amt: number): boolean;
|
||||
|
||||
abstract whoAmI(): string;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import { ISkillProgress } from "../formulas/skill";
|
||||
import { PlayerAchievement } from "../../Achievements/Achievements";
|
||||
import { cyrb53 } from "../../utils/StringHelperFunctions";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { ITaskTracker } from "../ITaskTracker";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { WorkType, ClassType, CrimeType, PlayerFactionWorkType } from "../../utils/WorkType";
|
||||
|
||||
@ -203,6 +204,7 @@ export class PlayerObject implements IPlayer {
|
||||
gainAgilityExp: (exp: number) => void;
|
||||
gainCharismaExp: (exp: number) => void;
|
||||
gainIntelligenceExp: (exp: number) => void;
|
||||
gainStats: (retValue: ITaskTracker) => void;
|
||||
gainMoney: (money: number, source: string) => void;
|
||||
getCurrentServer: () => BaseServer;
|
||||
getGangFaction: () => Faction;
|
||||
@ -524,6 +526,7 @@ export class PlayerObject implements IPlayer {
|
||||
this.gainAgilityExp = generalMethods.gainAgilityExp;
|
||||
this.gainCharismaExp = generalMethods.gainCharismaExp;
|
||||
this.gainIntelligenceExp = generalMethods.gainIntelligenceExp;
|
||||
this.gainStats = generalMethods.gainStats;
|
||||
this.queryStatFromString = generalMethods.queryStatFromString;
|
||||
this.resetWorkStatus = generalMethods.resetWorkStatus;
|
||||
this.processWorkEarnings = generalMethods.processWorkEarnings;
|
||||
@ -632,6 +635,10 @@ export class PlayerObject implements IPlayer {
|
||||
this.applyEntropy = augmentationMethods.applyEntropy;
|
||||
}
|
||||
|
||||
whoAmI(): string {
|
||||
return "Player";
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
|
@ -64,6 +64,9 @@ import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
import { calculateClassEarnings } from "../formulas/work";
|
||||
import { achievements } from "../../Achievements/Achievements";
|
||||
import { FactionNames } from "../../Faction/data/FactionNames";
|
||||
import { ITaskTracker } from "../ITaskTracker";
|
||||
import { IPerson } from "../IPerson";
|
||||
import { Player } from "../../Player";
|
||||
import { graftingIntBonus } from "../Grafting/GraftingHelpers";
|
||||
|
||||
import { WorkType, PlayerFactionWorkType, ClassType, CrimeType } from "../../utils/WorkType";
|
||||
@ -228,7 +231,7 @@ export function receiveInvite(this: IPlayer, factionName: string): void {
|
||||
}
|
||||
|
||||
//Calculates skill level based on experience. The same formula will be used for every skill
|
||||
export function calculateSkill(this: IPlayer, exp: number, mult = 1): number {
|
||||
export function calculateSkill(this: IPerson, exp: number, mult = 1): number {
|
||||
return calculateSkillF(exp, mult);
|
||||
}
|
||||
|
||||
@ -379,7 +382,7 @@ export function recordMoneySource(this: PlayerObject, amt: number, source: strin
|
||||
this.moneySourceB.record(amt, source);
|
||||
}
|
||||
|
||||
export function gainHackingExp(this: IPlayer, exp: number): void {
|
||||
export function gainHackingExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into Player.gainHackingExp()");
|
||||
return;
|
||||
@ -392,7 +395,7 @@ export function gainHackingExp(this: IPlayer, exp: number): void {
|
||||
this.hacking = calculateSkillF(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier);
|
||||
}
|
||||
|
||||
export function gainStrengthExp(this: IPlayer, exp: number): void {
|
||||
export function gainStrengthExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into Player.gainStrengthExp()");
|
||||
return;
|
||||
@ -405,7 +408,7 @@ export function gainStrengthExp(this: IPlayer, exp: number): void {
|
||||
this.strength = calculateSkillF(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier);
|
||||
}
|
||||
|
||||
export function gainDefenseExp(this: IPlayer, exp: number): void {
|
||||
export function gainDefenseExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into player.gainDefenseExp()");
|
||||
return;
|
||||
@ -421,7 +424,7 @@ export function gainDefenseExp(this: IPlayer, exp: number): void {
|
||||
this.hp = Math.round(this.max_hp * ratio);
|
||||
}
|
||||
|
||||
export function gainDexterityExp(this: IPlayer, exp: number): void {
|
||||
export function gainDexterityExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into Player.gainDexterityExp()");
|
||||
return;
|
||||
@ -437,7 +440,7 @@ export function gainDexterityExp(this: IPlayer, exp: number): void {
|
||||
);
|
||||
}
|
||||
|
||||
export function gainAgilityExp(this: IPlayer, exp: number): void {
|
||||
export function gainAgilityExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into Player.gainAgilityExp()");
|
||||
return;
|
||||
@ -450,7 +453,7 @@ export function gainAgilityExp(this: IPlayer, exp: number): void {
|
||||
this.agility = calculateSkillF(this.agility_exp, this.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier);
|
||||
}
|
||||
|
||||
export function gainCharismaExp(this: IPlayer, exp: number): void {
|
||||
export function gainCharismaExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERR: NaN passed into Player.gainCharismaExp()");
|
||||
return;
|
||||
@ -463,17 +466,27 @@ export function gainCharismaExp(this: IPlayer, exp: number): void {
|
||||
this.charisma = calculateSkillF(this.charisma_exp, this.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier);
|
||||
}
|
||||
|
||||
export function gainIntelligenceExp(this: IPlayer, exp: number): void {
|
||||
export function gainIntelligenceExp(this: IPerson, exp: number): void {
|
||||
if (isNaN(exp)) {
|
||||
console.error("ERROR: NaN passed into Player.gainIntelligenceExp()");
|
||||
return;
|
||||
}
|
||||
if (this.sourceFileLvl(5) > 0 || this.intelligence > 0) {
|
||||
if (Player.sourceFileLvl(5) > 0 || this.intelligence > 0) {
|
||||
this.intelligence_exp += exp;
|
||||
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
|
||||
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp, 1));
|
||||
}
|
||||
}
|
||||
|
||||
export function gainStats(this: IPerson, retValue: ITaskTracker): void {
|
||||
this.gainHackingExp(retValue.hack * this.hacking_exp_mult);
|
||||
this.gainStrengthExp(retValue.str * this.strength_exp_mult);
|
||||
this.gainDefenseExp(retValue.def * this.defense_exp_mult);
|
||||
this.gainDexterityExp(retValue.dex * this.dexterity_exp_mult);
|
||||
this.gainAgilityExp(retValue.agi * this.agility_exp_mult);
|
||||
this.gainCharismaExp(retValue.cha * this.charisma_exp_mult);
|
||||
this.gainIntelligenceExp(retValue.int);
|
||||
}
|
||||
|
||||
//Given a string expression like "str" or "strength", returns the given stat
|
||||
export function queryStatFromString(this: IPlayer, str: string): number {
|
||||
const tempStr = str.toLowerCase();
|
||||
@ -1726,7 +1739,7 @@ export function takeDamage(this: IPlayer, amt: number): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function regenerateHp(this: IPlayer, amt: number): void {
|
||||
export function regenerateHp(this: IPerson, amt: number): void {
|
||||
if (typeof amt !== "number") {
|
||||
console.warn(`Player.regenerateHp() called without a numeric argument: ${amt}`);
|
||||
return;
|
||||
|
@ -9,7 +9,8 @@
|
||||
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
import { Person, ITaskTracker, createTaskTracker } from "../Person";
|
||||
import { Person } from "../Person";
|
||||
import { ITaskTracker, createTaskTracker } from "../ITaskTracker";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
|
||||
@ -33,6 +34,9 @@ import { CityName } from "../../Locations/data/CityNames";
|
||||
import { LocationName } from "../../Locations/data/LocationNames";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { BladeburnerConstants } from "../../Bladeburner/data/Constants";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { capitalizeFirstLetter, capitalizeEachWord } from "../../utils/StringHelperFunctions";
|
||||
|
||||
export class Sleeve extends Person {
|
||||
/**
|
||||
@ -58,6 +62,7 @@ export class Sleeve extends Person {
|
||||
* Faction/Company Work: Name of Faction/Company
|
||||
* Crime: Money earned if successful
|
||||
* Class/Gym: Name of university/gym
|
||||
* Bladeburner: success chance
|
||||
*/
|
||||
currentTaskLocation = "";
|
||||
|
||||
@ -101,6 +106,16 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
gymStatType = "";
|
||||
|
||||
/**
|
||||
* String that stores what stat the sleeve is training at the gym
|
||||
*/
|
||||
bbAction = "";
|
||||
|
||||
/**
|
||||
* String that stores what stat the sleeve is training at the gym
|
||||
*/
|
||||
bbContract = "";
|
||||
|
||||
/**
|
||||
* Keeps track of events/notifications for this sleeve
|
||||
*/
|
||||
@ -151,7 +166,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.gainRatesForTask.hack = crime.hacking_exp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
@ -160,6 +175,7 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.dex = crime.dexterity_exp * this.dexterity_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.agi = crime.agility_exp * this.agility_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.cha = crime.charisma_exp * this.charisma_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.int = crime.intelligence_exp;
|
||||
this.gainRatesForTask.money = crime.money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney;
|
||||
|
||||
this.currentTaskLocation = String(this.gainRatesForTask.money);
|
||||
@ -182,7 +198,7 @@ export class Sleeve extends Person {
|
||||
const crime: Crime | undefined = Object.values(Crimes).find((crime) => crime.name === this.crimeType);
|
||||
if (!crime) {
|
||||
console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`);
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
}
|
||||
if (Math.random() < crime.successRate(this)) {
|
||||
@ -206,11 +222,60 @@ export class Sleeve extends Person {
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
} else {
|
||||
// For other crimes... I dont think anything else needs to be done
|
||||
} else if (this.currentTask === SleeveTaskType.Bladeburner) {
|
||||
if (this.currentTaskMaxTime === 0) {
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
// For bladeburner, all experience and money is gained at the end
|
||||
const bb = p.bladeburner;
|
||||
if (bb === null) {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
}
|
||||
|
||||
this.resetTaskStatus();
|
||||
if (this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
if (this.bbAction === "Infiltrate synthoids") {
|
||||
bb.infiltrateSynthoidCommunities(p);
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
let type: string;
|
||||
let name: string;
|
||||
if (this.bbAction === "Take on contracts") {
|
||||
type = "Contracts";
|
||||
name = this.bbContract;
|
||||
} else {
|
||||
type = "General";
|
||||
name = this.bbAction;
|
||||
}
|
||||
|
||||
const actionIdent = bb.getActionIdFromTypeAndName(type, name);
|
||||
if (actionIdent === null) {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return retValue;
|
||||
}
|
||||
|
||||
const action = bb.getActionObject(actionIdent);
|
||||
if ((action?.count ?? 0) > 0) {
|
||||
const bbRetValue = bb.completeAction(p, this, actionIdent, false);
|
||||
if (bbRetValue) {
|
||||
retValue = this.gainExperience(p, bbRetValue);
|
||||
this.gainMoney(p, bbRetValue);
|
||||
|
||||
// Do not reset task to IDLE
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.resetTaskStatus(p);
|
||||
|
||||
return retValue;
|
||||
}
|
||||
@ -260,50 +325,56 @@ export class Sleeve extends Person {
|
||||
const pDexExp = exp.dex * multFac;
|
||||
const pAgiExp = exp.agi * multFac;
|
||||
const pChaExp = exp.cha * multFac;
|
||||
const pIntExp = exp.int * multFac;
|
||||
|
||||
// Experience is gained by both this sleeve and player
|
||||
if (pHackExp > 0) {
|
||||
this.hacking_exp += pHackExp;
|
||||
this.gainHackingExp(pHackExp);
|
||||
p.gainHackingExp(pHackExp);
|
||||
this.earningsForPlayer.hack += pHackExp;
|
||||
this.earningsForTask.hack += pHackExp;
|
||||
}
|
||||
|
||||
if (pStrExp > 0) {
|
||||
this.strength_exp += pStrExp;
|
||||
this.gainStrengthExp(pStrExp);
|
||||
p.gainStrengthExp(pStrExp);
|
||||
this.earningsForPlayer.str += pStrExp;
|
||||
this.earningsForTask.str += pStrExp;
|
||||
}
|
||||
|
||||
if (pDefExp > 0) {
|
||||
this.defense_exp += pDefExp;
|
||||
this.gainDefenseExp(pDefExp);
|
||||
p.gainDefenseExp(pDefExp);
|
||||
this.earningsForPlayer.def += pDefExp;
|
||||
this.earningsForTask.def += pDefExp;
|
||||
}
|
||||
|
||||
if (pDexExp > 0) {
|
||||
this.dexterity_exp += pDexExp;
|
||||
this.gainDexterityExp(pDexExp);
|
||||
p.gainDexterityExp(pDexExp);
|
||||
this.earningsForPlayer.dex += pDexExp;
|
||||
this.earningsForTask.dex += pDexExp;
|
||||
}
|
||||
|
||||
if (pAgiExp > 0) {
|
||||
this.agility_exp += pAgiExp;
|
||||
this.gainAgilityExp(pAgiExp);
|
||||
p.gainAgilityExp(pAgiExp);
|
||||
this.earningsForPlayer.agi += pAgiExp;
|
||||
this.earningsForTask.agi += pAgiExp;
|
||||
}
|
||||
|
||||
if (pChaExp > 0) {
|
||||
this.charisma_exp += pChaExp;
|
||||
this.gainCharismaExp(pChaExp);
|
||||
p.gainCharismaExp(pChaExp);
|
||||
this.earningsForPlayer.cha += pChaExp;
|
||||
this.earningsForTask.cha += pChaExp;
|
||||
}
|
||||
|
||||
if (pIntExp > 0) {
|
||||
this.gainIntelligenceExp(pIntExp);
|
||||
p.gainIntelligenceExp(pIntExp);
|
||||
}
|
||||
|
||||
// Record earnings for other sleeves
|
||||
this.earningsForSleeves.hack += pHackExp * (this.sync / 100);
|
||||
this.earningsForSleeves.str += pStrExp * (this.sync / 100);
|
||||
@ -320,7 +391,8 @@ export class Sleeve extends Person {
|
||||
dex: pDexExp * (this.sync / 100),
|
||||
agi: pAgiExp * (this.sync / 100),
|
||||
cha: pChaExp * (this.sync / 100),
|
||||
money: 0,
|
||||
int: pIntExp * (this.sync / 100),
|
||||
money: exp.money,
|
||||
};
|
||||
}
|
||||
|
||||
@ -445,7 +517,7 @@ export class Sleeve extends Person {
|
||||
this.charisma_exp = 0;
|
||||
|
||||
// Reset task-related stuff
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
this.earningsForSleeves = createTaskTracker();
|
||||
this.earningsForPlayer = createTaskTracker();
|
||||
this.shockRecovery(p);
|
||||
@ -523,7 +595,7 @@ export class Sleeve extends Person {
|
||||
// for, we need to reset the sleeve's task
|
||||
if (p.gang) {
|
||||
if (fac.name === p.gang.facName) {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,18 +617,18 @@ export class Sleeve extends Person {
|
||||
}
|
||||
case SleeveTaskType.Recovery:
|
||||
this.shock = Math.min(100, this.shock + 0.0002 * cyclesUsed);
|
||||
if (this.shock >= 100) this.resetTaskStatus();
|
||||
if (this.shock >= 100) this.resetTaskStatus(p);
|
||||
break;
|
||||
case SleeveTaskType.Synchro:
|
||||
this.sync = Math.min(100, this.sync + p.getIntelligenceBonus(0.5) * 0.0002 * cyclesUsed);
|
||||
if (this.sync >= 100) this.resetTaskStatus();
|
||||
if (this.sync >= 100) this.resetTaskStatus(p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
if (this.currentTask === SleeveTaskType.Crime || this.currentTask === SleeveTaskType.Bladeburner) {
|
||||
retValue = this.finishTask(p);
|
||||
} else {
|
||||
this.finishTask(p);
|
||||
@ -573,7 +645,16 @@ export class Sleeve extends Person {
|
||||
/**
|
||||
* Resets all parameters used to keep information about the current task
|
||||
*/
|
||||
resetTaskStatus(): void {
|
||||
resetTaskStatus(p: IPlayer): void {
|
||||
if (this.bbAction == "Support main sleeve") {
|
||||
p.bladeburner?.sleeveSupport(false);
|
||||
}
|
||||
if (this.currentTask == SleeveTaskType.Class) {
|
||||
const retVal = createTaskTracker();
|
||||
retVal.int = CONSTANTS.IntelligenceClassBaseExpGain * Math.round(this.currentTaskTime / 1000);
|
||||
const r = this.gainExperience(p, retVal);
|
||||
p.sleeves.filter((s) => s != this).forEach((s) => s.gainExperience(p, r, 1, true));
|
||||
}
|
||||
this.earningsForTask = createTaskTracker();
|
||||
this.gainRatesForTask = createTaskTracker();
|
||||
this.currentTask = SleeveTaskType.Idle;
|
||||
@ -584,13 +665,15 @@ export class Sleeve extends Person {
|
||||
this.currentTaskLocation = "";
|
||||
this.gymStatType = "";
|
||||
this.className = "";
|
||||
this.bbAction = "";
|
||||
this.bbContract = "------";
|
||||
}
|
||||
|
||||
shockRecovery(p: IPlayer): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Recovery;
|
||||
@ -601,7 +684,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Synchro;
|
||||
@ -615,7 +698,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
// Set exp/money multipliers based on which university.
|
||||
@ -809,7 +892,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
const company: Company | null = Companies[companyName];
|
||||
@ -875,7 +958,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
const factionInfo = faction.getInfo();
|
||||
@ -926,7 +1009,7 @@ export class Sleeve extends Person {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
// Set exp/money multipliers based on which university.
|
||||
@ -994,6 +1077,162 @@ export class Sleeve extends Person {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a bladeburner task
|
||||
*/
|
||||
bladeburner(p: IPlayer, action: string, contract: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus(p);
|
||||
}
|
||||
|
||||
this.gainRatesForTask.hack = 0;
|
||||
this.gainRatesForTask.str = 0;
|
||||
this.gainRatesForTask.def = 0;
|
||||
this.gainRatesForTask.dex = 0;
|
||||
this.gainRatesForTask.agi = 0;
|
||||
this.gainRatesForTask.cha = 0;
|
||||
this.gainRatesForTask.money = 0;
|
||||
this.currentTaskLocation = "";
|
||||
|
||||
let time = 0;
|
||||
|
||||
this.bbContract = "------";
|
||||
switch (action) {
|
||||
case "Field analysis":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
this.gainRatesForTask.hack = 20 * this.hacking_exp_mult;
|
||||
this.gainRatesForTask.cha = 20 * this.charisma_exp_mult;
|
||||
break;
|
||||
case "Recruitment":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
this.gainRatesForTask.cha =
|
||||
2 * BladeburnerConstants.BaseStatGain * (p.bladeburner?.getRecruitmentTime(this) ?? 0) * 1000;
|
||||
this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage(
|
||||
this.recruitmentSuccessChance(p),
|
||||
)})`;
|
||||
break;
|
||||
case "Diplomacy":
|
||||
time = this.getBladeburnerActionTime(p, "General", action);
|
||||
break;
|
||||
case "Infiltrate synthoids":
|
||||
time = 60000;
|
||||
this.currentTaskLocation = "This will generate additional contracts and operations";
|
||||
break;
|
||||
case "Support main sleeve":
|
||||
p.bladeburner?.sleeveSupport(true);
|
||||
time = 0;
|
||||
break;
|
||||
case "Take on contracts":
|
||||
time = this.getBladeburnerActionTime(p, "Contracts", contract);
|
||||
this.contractGainRates(p, "Contracts", contract);
|
||||
this.currentTaskLocation = this.contractSuccessChance(p, "Contracts", contract);
|
||||
this.bbContract = capitalizeEachWord(contract.toLowerCase());
|
||||
break;
|
||||
}
|
||||
|
||||
this.bbAction = capitalizeFirstLetter(action.toLowerCase());
|
||||
this.currentTaskMaxTime = time;
|
||||
this.currentTask = SleeveTaskType.Bladeburner;
|
||||
return true;
|
||||
}
|
||||
|
||||
recruitmentSuccessChance(p: IPlayer): number {
|
||||
return Math.max(0, Math.min(1, p.bladeburner?.getRecruitmentSuccessChance(this) ?? 0));
|
||||
}
|
||||
|
||||
contractSuccessChance(p: IPlayer, type: string, name: string): string {
|
||||
const bb = p.bladeburner;
|
||||
if (bb === null) {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.contractSuccessChance; Message: '${errorLogText}'`);
|
||||
return "0%";
|
||||
}
|
||||
const chances = bb.getActionEstimatedSuccessChanceNetscriptFn(this, type, name);
|
||||
if (typeof chances === "string") {
|
||||
console.error(`Function: sleeves.contractSuccessChance; Message: '${chances}'`);
|
||||
return "0%";
|
||||
}
|
||||
if (chances[0] >= 1) {
|
||||
return "100%";
|
||||
} else {
|
||||
return `${numeralWrapper.formatPercentage(chances[0])} - ${numeralWrapper.formatPercentage(chances[1])}`;
|
||||
}
|
||||
}
|
||||
|
||||
contractGainRates(p: IPlayer, type: string, name: string): void {
|
||||
const bb = p.bladeburner;
|
||||
if (bb === null) {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.contractGainRates; Message: '${errorLogText}'`);
|
||||
return;
|
||||
}
|
||||
const actionIdent = bb.getActionIdFromTypeAndName(type, name);
|
||||
if (actionIdent === null) {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
console.error(`Function: sleeves.contractGainRates; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return;
|
||||
}
|
||||
const action = bb.getActionObject(actionIdent);
|
||||
if (action === null) {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
console.error(`Function: sleeves.contractGainRates; Message: '${errorLogText}'`);
|
||||
this.resetTaskStatus(p);
|
||||
return;
|
||||
}
|
||||
const retValue = bb.getActionStats(action, true);
|
||||
this.gainRatesForTask.hack = retValue.hack;
|
||||
this.gainRatesForTask.str = retValue.str;
|
||||
this.gainRatesForTask.def = retValue.def;
|
||||
this.gainRatesForTask.dex = retValue.dex;
|
||||
this.gainRatesForTask.agi = retValue.agi;
|
||||
this.gainRatesForTask.cha = retValue.cha;
|
||||
const rewardMultiplier = Math.pow(action.rewardFac, action.level - 1);
|
||||
this.gainRatesForTask.money =
|
||||
BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * bb.skillMultipliers.money;
|
||||
}
|
||||
|
||||
getBladeburnerActionTime(p: IPlayer, type: string, name: string): number {
|
||||
//Maybe find workerscript and use original
|
||||
const bb = p.bladeburner;
|
||||
if (bb === null) {
|
||||
const errorLogText = `bladeburner is null`;
|
||||
console.error(`Function: sleeves.getBladeburnerActionTime; Message: '${errorLogText}'`);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const time = bb.getActionTimeNetscriptFn(this, type, name);
|
||||
if (typeof time === "string") {
|
||||
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
|
||||
console.error(`Function: sleeves.getBladeburnerActionTime; Message: '${errorLogText}'`);
|
||||
return -1;
|
||||
} else {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
takeDamage(amt: number): boolean {
|
||||
if (typeof amt !== "number") {
|
||||
console.warn(`Player.takeDamage() called without a numeric argument: ${amt}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hp -= amt;
|
||||
if (this.hp <= 0) {
|
||||
this.shock += 0.5;
|
||||
this.hp = this.max_hp;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
whoAmI(): string {
|
||||
return "Sleeve";
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
|
@ -9,6 +9,7 @@ export enum SleeveTaskType {
|
||||
Crime,
|
||||
Class,
|
||||
Gym,
|
||||
Bladeburner,
|
||||
Recovery,
|
||||
Synchro,
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
const [abc, setABC] = useState(["------", "------", "------"]);
|
||||
|
||||
function setTask(): void {
|
||||
props.sleeve.resetTaskStatus(); // sets to idle
|
||||
props.sleeve.resetTaskStatus(player); // sets to idle
|
||||
switch (abc[0]) {
|
||||
case "------":
|
||||
break;
|
||||
@ -49,6 +49,9 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
case "Workout at Gym":
|
||||
props.sleeve.workoutAtGym(player, abc[2], abc[1]);
|
||||
break;
|
||||
case "Perform Bladeburner Actions":
|
||||
props.sleeve.bladeburner(player, abc[1], abc[2]);
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
props.sleeve.shockRecovery(player);
|
||||
break;
|
||||
@ -106,6 +109,20 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
case SleeveTaskType.Gym:
|
||||
desc = <>This sleeve is currently working out at {props.sleeve.currentTaskLocation}.</>;
|
||||
break;
|
||||
case SleeveTaskType.Bladeburner: {
|
||||
let message = "";
|
||||
if (props.sleeve.bbContract !== "------") {
|
||||
message = ` - ${props.sleeve.bbContract} (Success Rate: ${props.sleeve.currentTaskLocation})`;
|
||||
} else if (props.sleeve.currentTaskLocation !== "") {
|
||||
message = props.sleeve.currentTaskLocation;
|
||||
}
|
||||
desc = (
|
||||
<>
|
||||
This sleeve is currently attempting to {props.sleeve.bbAction}. {message}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case SleeveTaskType.Recovery:
|
||||
desc = (
|
||||
<>
|
||||
@ -168,7 +185,9 @@ export function SleeveElem(props: IProps): React.ReactElement {
|
||||
</Button>
|
||||
<Typography>{desc}</Typography>
|
||||
<Typography>
|
||||
{props.sleeve.currentTask === SleeveTaskType.Crime && (
|
||||
{(props.sleeve.currentTask === SleeveTaskType.Crime ||
|
||||
props.sleeve.currentTask === SleeveTaskType.Bladeburner) &&
|
||||
props.sleeve.currentTaskMaxTime > 0 && (
|
||||
<ProgressBar
|
||||
variant="determinate"
|
||||
value={(props.sleeve.currentTaskTime / props.sleeve.currentTaskMaxTime) * 100}
|
||||
|
@ -22,6 +22,15 @@ const universitySelectorOptions: string[] = [
|
||||
|
||||
const gymSelectorOptions: string[] = ["Train Strength", "Train Defense", "Train Dexterity", "Train Agility"];
|
||||
|
||||
const bladeburnerSelectorOptions: string[] = [
|
||||
"Field analysis",
|
||||
"Recruitment",
|
||||
"Diplomacy",
|
||||
"Infiltrate synthoids",
|
||||
"Support main sleeve",
|
||||
"Take on contracts",
|
||||
];
|
||||
|
||||
interface IProps {
|
||||
sleeve: Sleeve;
|
||||
player: IPlayer;
|
||||
@ -84,6 +93,26 @@ function possibleFactions(player: IPlayer, sleeve: Sleeve): string[] {
|
||||
});
|
||||
}
|
||||
|
||||
function possibleContracts(player: IPlayer, sleeve: Sleeve): string[] {
|
||||
const bb = player.bladeburner;
|
||||
if (bb === null) {
|
||||
return ["------"];
|
||||
}
|
||||
let contracts = bb.getContractNamesNetscriptFn();
|
||||
for (const otherSleeve of player.sleeves) {
|
||||
if (sleeve === otherSleeve) {
|
||||
continue;
|
||||
}
|
||||
if (otherSleeve.currentTask === SleeveTaskType.Bladeburner && otherSleeve.bbAction == "Take on contracts") {
|
||||
contracts = contracts.filter((x) => x != otherSleeve.bbContract);
|
||||
}
|
||||
}
|
||||
if (contracts.length === 0) {
|
||||
return ["------"];
|
||||
}
|
||||
return contracts;
|
||||
}
|
||||
|
||||
const tasks: {
|
||||
[key: string]: undefined | ((player: IPlayer, sleeve: Sleeve) => ITaskDetails);
|
||||
["------"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
@ -92,6 +121,7 @@ const tasks: {
|
||||
["Commit Crime"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Take University Course"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Workout at Gym"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Perform Bladeburner Actions"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Shock Recovery"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
["Synchronize"]: (player: IPlayer, sleeve: Sleeve) => ITaskDetails;
|
||||
} = {
|
||||
@ -170,6 +200,18 @@ const tasks: {
|
||||
|
||||
return { first: gymSelectorOptions, second: () => gyms };
|
||||
},
|
||||
"Perform Bladeburner Actions": (player: IPlayer, sleeve: Sleeve): ITaskDetails => {
|
||||
return {
|
||||
first: bladeburnerSelectorOptions,
|
||||
second: (s1: string) => {
|
||||
if (s1 === "Take on contracts") {
|
||||
return possibleContracts(player, sleeve);
|
||||
} else {
|
||||
return ["------"];
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
"Shock Recovery": (): ITaskDetails => {
|
||||
return { first: ["------"], second: () => ["------"] };
|
||||
},
|
||||
@ -186,6 +228,7 @@ const canDo: {
|
||||
["Commit Crime"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Take University Course"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Workout at Gym"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Perform Bladeburner Actions"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Shock Recovery"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
["Synchronize"]: (player: IPlayer, sleeve: Sleeve) => boolean;
|
||||
} = {
|
||||
@ -197,6 +240,7 @@ const canDo: {
|
||||
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
|
||||
"Workout at Gym": (player: IPlayer, sleeve: Sleeve) =>
|
||||
[CityName.Aevum, CityName.Sector12, CityName.Volhaven].includes(sleeve.city),
|
||||
"Perform Bladeburner Actions": (player: IPlayer, _: Sleeve) => player.inBladeburner(),
|
||||
"Shock Recovery": (player: IPlayer, sleeve: Sleeve) => sleeve.shock < 100,
|
||||
Synchronize: (player: IPlayer, sleeve: Sleeve) => sleeve.sync < 100,
|
||||
};
|
||||
@ -228,6 +272,8 @@ function getABC(sleeve: Sleeve): [string, string, string] {
|
||||
return ["Take University Course", sleeve.className, sleeve.currentTaskLocation];
|
||||
case SleeveTaskType.Gym:
|
||||
return ["Workout at Gym", sleeve.gymStatType, sleeve.currentTaskLocation];
|
||||
case SleeveTaskType.Bladeburner:
|
||||
return ["Perform Bladeburner Actions", sleeve.bbAction, sleeve.bbContract];
|
||||
case SleeveTaskType.Recovery:
|
||||
return ["Shock Recovery", "------", "------"];
|
||||
case SleeveTaskType.Synchro:
|
||||
|
@ -26,7 +26,7 @@ export function TravelModal(props: IProps): React.ReactElement {
|
||||
}
|
||||
props.sleeve.city = city as CityName;
|
||||
player.loseMoney(CONSTANTS.TravelCost, "sleeve");
|
||||
props.sleeve.resetTaskStatus();
|
||||
props.sleeve.resetTaskStatus(player);
|
||||
props.rerender();
|
||||
props.onClose();
|
||||
}
|
||||
|
14
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
14
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -3789,6 +3789,20 @@ export interface Sleeve {
|
||||
* @returns True if the aug was purchased and installed on the sleeve, false otherwise.
|
||||
*/
|
||||
purchaseSleeveAug(sleeveNumber: number, augName: string): boolean;
|
||||
|
||||
/**
|
||||
* Set a sleeve to perform bladeburner actions.
|
||||
* @remarks
|
||||
* RAM cost: 4 GB
|
||||
*
|
||||
* Return a boolean indicating whether or not the sleeve started working out.
|
||||
*
|
||||
* @param sleeveNumber - Index of the sleeve to workout at the gym.
|
||||
* @param action - Name of the action to be performed.
|
||||
* @param contract - Name of the contract if applicable.
|
||||
* @returns True if the sleeve started working out, false otherwise.
|
||||
*/
|
||||
setToBladeburnerAction(sleeveNumber: number, action: string, contract?: string): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,6 +117,17 @@ function cyrb53(str: string, seed = 0): string {
|
||||
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16);
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(s: string): string {
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
|
||||
function capitalizeEachWord(s: string): string {
|
||||
return s
|
||||
.split(" ")
|
||||
.map((word) => capitalizeFirstLetter(word))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export {
|
||||
convertTimeMsToTimeElapsedString,
|
||||
longestCommonStart,
|
||||
@ -124,4 +135,6 @@ export {
|
||||
formatNumber,
|
||||
generateRandomString,
|
||||
cyrb53,
|
||||
capitalizeFirstLetter,
|
||||
capitalizeEachWord,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user