More converting blade to react.

This commit is contained in:
Olivier Gagnon 2021-08-16 01:35:05 -04:00
parent 58ada6d128
commit cc8de58cff
21 changed files with 694 additions and 603 deletions

@ -80,6 +80,15 @@ import {
getDiplomacyEffectiveness, getDiplomacyEffectiveness,
getRecruitmentSuccessChance, getRecruitmentSuccessChance,
getRecruitmentTime, getRecruitmentTime,
resetSkillMultipliers,
updateSkillMultipliers,
resetAction,
getActionObject,
completeOperation,
completeContract,
completeAction,
processAction,
startAction,
} from "./Bladeburner/Bladeburner"; } from "./Bladeburner/Bladeburner";
function Bladeburner(params={}) { function Bladeburner(params={}) {
@ -116,7 +125,7 @@ function Bladeburner(params={}) {
// Map of SkillNames -> level // Map of SkillNames -> level
this.skills = {}; this.skills = {};
this.skillMultipliers = {}; this.skillMultipliers = {};
this.updateSkillMultipliers(); // Calls resetSkillMultipliers() updateSkillMultipliers(this); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses // Max Stamina is based on stats and Bladeburner-specific bonuses
this.staminaBonus = 0; // Gained from training this.staminaBonus = 0; // Gained from training
@ -163,7 +172,7 @@ function Bladeburner(params={}) {
} }
Bladeburner.prototype.prestige = function() { Bladeburner.prototype.prestige = function() {
this.resetAction(); resetAction(this);
var bladeburnerFac = Factions["Bladeburners"]; var bladeburnerFac = Factions["Bladeburners"];
if (this.rank >= BladeburnerConstants.RankNeededForFaction) { if (this.rank >= BladeburnerConstants.RankNeededForFaction) {
joinFaction(bladeburnerFac); joinFaction(bladeburnerFac);
@ -289,7 +298,6 @@ Bladeburner.prototype.storeCycles = function(numCycles=1) {
this.storedCycles += numCycles; this.storedCycles += numCycles;
} }
Bladeburner.prototype.process = function() { Bladeburner.prototype.process = function() {
// Edge case condition...if Operation Daedalus is complete trigger the BitNode // Edge case condition...if Operation Daedalus is complete trigger the BitNode
if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) { if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) {
@ -308,13 +316,13 @@ Bladeburner.prototype.process = function() {
dialogBoxCreate(msg); dialogBoxCreate(msg);
} }
} }
this.resetAction(); resetAction(this);
} }
// If the Player has no Stamina, set action to idle // If the Player has no Stamina, set action to idle
if (this.stamina <= 0) { if (this.stamina <= 0) {
this.log("Your Bladeburner action was cancelled because your stamina hit 0"); this.log("Your Bladeburner action was cancelled because your stamina hit 0");
this.resetAction(); resetAction(this);
} }
// A 'tick' for this mechanic is one second (= 5 game cycles) // A 'tick' for this mechanic is one second (= 5 game cycles)
@ -352,7 +360,7 @@ Bladeburner.prototype.process = function() {
this.randomEventCounter += getRandomInt(240, 600); this.randomEventCounter += getRandomInt(240, 600);
} }
this.processAction(seconds); processAction(this, Player, seconds);
// Automation // Automation
if (this.automateEnabled) { if (this.automateEnabled) {
@ -360,12 +368,12 @@ Bladeburner.prototype.process = function() {
if (this.stamina <= this.automateThreshLow) { if (this.stamina <= this.automateThreshLow) {
if (this.action.name !== this.automateActionLow.name || this.action.type !== this.automateActionLow.type) { if (this.action.name !== this.automateActionLow.name || this.action.type !== this.automateActionLow.type) {
this.action = new ActionIdentifier({type: this.automateActionLow.type, name: this.automateActionLow.name}); this.action = new ActionIdentifier({type: this.automateActionLow.type, name: this.automateActionLow.name});
this.startAction(this.action); startAction(this, Player, this.action);
} }
} else if (this.stamina >= this.automateThreshHigh) { } else if (this.stamina >= this.automateThreshHigh) {
if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) { if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) {
this.action = new ActionIdentifier({type: this.automateActionHigh.type, name: this.automateActionHigh.name}); this.action = new ActionIdentifier({type: this.automateActionHigh.type, name: this.automateActionHigh.name});
this.startAction(this.action); startAction(this, Player, this.action);
} }
} }
} }
@ -397,34 +405,6 @@ Bladeburner.prototype.calculateStaminaPenalty = function() {
return Math.min(1, this.stamina / (0.5 * this.maxStamina)); return Math.min(1, this.stamina / (0.5 * this.maxStamina));
} }
Bladeburner.prototype.changeRank = function(change) {
if (isNaN(change)) {throw new Error("NaN passed into Bladeburner.changeRank()");}
this.rank += change;
if (this.rank < 0) {this.rank = 0;}
this.maxRank = Math.max(this.rank, this.maxRank);
var bladeburnersFactionName = "Bladeburners";
if (factionExists(bladeburnersFactionName)) {
var bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) {
throw new Error("Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button");
}
if (bladeburnerFac.isMember) {
var favorBonus = 1 + (bladeburnerFac.favor / 100);
bladeburnerFac.playerReputation += (BladeburnerConstants.RankToFactionRepFactor * change * Player.faction_rep_mult * favorBonus);
}
}
// Gain skill points
var rankNeededForSp = (this.totalSkillPoints+1) * BladeburnerConstants.RanksPerSkillPoint;
if (this.maxRank >= rankNeededForSp) {
// Calculate how many skill points to gain
var gainedSkillPoints = Math.floor((this.maxRank - rankNeededForSp) / BladeburnerConstants.RanksPerSkillPoint + 1);
this.skillPoints += gainedSkillPoints;
this.totalSkillPoints += gainedSkillPoints;
}
}
Bladeburner.prototype.getCurrentCity = function() { Bladeburner.prototype.getCurrentCity = function() {
var city = this.cities[this.city]; var city = this.cities[this.city];
if (!(city instanceof City)) { if (!(city instanceof City)) {
@ -433,55 +413,6 @@ Bladeburner.prototype.getCurrentCity = function() {
return city; return city;
} }
Bladeburner.prototype.resetSkillMultipliers = function() {
this.skillMultipliers = {
successChanceAll: 1,
successChanceStealth: 1,
successChanceKill: 1,
successChanceContract: 1,
successChanceOperation: 1,
successChanceEstimate: 1,
actionTime: 1,
effHack: 1,
effStr: 1,
effDef: 1,
effDex: 1,
effAgi: 1,
effCha: 1,
effInt: 1,
stamina: 1,
money: 1,
expGain: 1,
};
}
Bladeburner.prototype.updateSkillMultipliers = function() {
this.resetSkillMultipliers();
for (var skillName in this.skills) {
if (this.skills.hasOwnProperty(skillName)) {
var skill = Skills[skillName];
if (skill == null) {
throw new Error("Could not find Skill Object for: " + skillName);
}
var level = this.skills[skillName];
if (level == null || level <= 0) {continue;} //Not upgraded
var multiplierNames = Object.keys(this.skillMultipliers);
for (var i = 0; i < multiplierNames.length; ++i) {
var multiplierName = multiplierNames[i];
if (skill[multiplierName] != null && !isNaN(skill[multiplierName])) {
var value = skill[multiplierName] * level;
var multiplierValue = 1 + (value / 100);
if (multiplierName === "actionTime") {
multiplierValue = 1 - (value / 100);
}
this.skillMultipliers[multiplierName] *= multiplierValue;
}
}
}
}
}
Bladeburner.prototype.upgradeSkill = function(skill) { Bladeburner.prototype.upgradeSkill = function(skill) {
// This does NOT handle deduction of skill points // This does NOT handle deduction of skill points
var skillName = skill.name; var skillName = skill.name;
@ -493,35 +424,7 @@ Bladeburner.prototype.upgradeSkill = function(skill) {
if (isNaN(this.skills[skillName]) || this.skills[skillName] < 0) { if (isNaN(this.skills[skillName]) || this.skills[skillName] < 0) {
throw new Error("Level of Skill " + skillName + " is invalid: " + this.skills[skillName]); throw new Error("Level of Skill " + skillName + " is invalid: " + this.skills[skillName]);
} }
this.updateSkillMultipliers(); updateSkillMultipliers(this);
}
Bladeburner.prototype.getActionObject = function(actionId) {
/**
* Given an ActionIdentifier object, returns the corresponding
* GeneralAction, Contract, Operation, or BlackOperation object
*/
switch (actionId.type) {
case ActionTypes["Contract"]:
return this.contracts[actionId.name];
case ActionTypes["Operation"]:
return this.operations[actionId.name];
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
return BlackOperations[actionId.name];
case ActionTypes["Training"]:
return GeneralActions["Training"];
case ActionTypes["Field Analysis"]:
return GeneralActions["Field Analysis"];
case ActionTypes["Recruitment"]:
return GeneralActions["Recruitment"];
case ActionTypes["Diplomacy"]:
return GeneralActions["Diplomacy"];
case ActionTypes["Hyperbolic Regeneration Chamber"]:
return GeneralActions["Hyperbolic Regeneration Chamber"];
default:
return null;
}
} }
// Sets the player to the "IDLE" action // Sets the player to the "IDLE" action
@ -529,448 +432,8 @@ Bladeburner.prototype.resetAction = function() {
this.action = new ActionIdentifier({type:ActionTypes.Idle}); this.action = new ActionIdentifier({type:ActionTypes.Idle});
} }
Bladeburner.prototype.startAction = function(actionId) {
if (actionId == null) {return;}
this.action = actionId;
this.actionTimeCurrent = 0;
switch (actionId.type) {
case ActionTypes["Idle"]:
this.actionTimeToComplete = 0;
break;
case ActionTypes["Contract"]:
try {
var action = this.getActionObject(actionId);
if (action == null) {
throw new Error("Failed to get Contract Object for: " + actionId.name);
}
if (action.count < 1) {return this.resetAction();}
this.actionTimeToComplete = action.getActionTime(this);
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["Operation"]:
try {
var action = this.getActionObject(actionId);
if (action == null) {
throw new Error ("Failed to get Operation Object for: " + actionId.name);
}
if (action.count < 1) {return this.resetAction();}
if (actionId.name === "Raid" && this.getCurrentCity().commsEst === 0) {return this.resetAction();}
this.actionTimeToComplete = action.getActionTime(this);
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
try {
// Safety measure - don't repeat BlackOps that are already done
if (this.blackops[actionId.name] != null) {
this.resetAction();
this.log("Error: Tried to start a Black Operation that had already been completed");
break;
}
var action = this.getActionObject(actionId);
if (action == null) {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
this.actionTimeToComplete = action.getActionTime(this);
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["Recruitment"]:
this.actionTimeToComplete = getRecruitmentTime(this, Player);
break;
case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]:
case ActionTypes["Field Analysis"]:
this.actionTimeToComplete = 30;
break;
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
this.actionTimeToComplete = 60;
break;
default:
throw new Error("Invalid Action Type in Bladeburner.startAction(): " + actionId.type);
break;
}
}
Bladeburner.prototype.processAction = function(seconds) {
if (this.action.type === ActionTypes["Idle"]) {return;}
if (this.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
}
if (!(this.action instanceof ActionIdentifier)) {
throw new Error("Bladeburner.action is not an ActionIdentifier Object");
}
// If the previous action went past its completion time, add to the next action
// This is not added inmediatly in case the automation changes the action
this.actionTimeCurrent += seconds + this.actionTimeOverflow;
this.actionTimeOverflow = 0;
if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
return this.completeAction();
}
}
Bladeburner.prototype.completeAction = function() {
switch (this.action.type) {
case ActionTypes["Contract"]:
case ActionTypes["Operation"]:
try {
var isOperation = (this.action.type === ActionTypes["Operation"]);
var action = this.getActionObject(this.action);
if (action == null) {
throw new Error("Failed to get Contract/Operation Object for: " + this.action.name);
}
var difficulty = action.getDifficulty();
var difficultyMultiplier = Math.pow(difficulty, BladeburnerConstants.DiffMultExponentialFactor) + difficulty / BladeburnerConstants.DiffMultLinearFactor;
var rewardMultiplier = Math.pow(action.rewardFac, action.level-1);
// 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)) {
gainActionStats(this, Player, action, true);
++action.successes;
--action.count;
// Earn money for contracts
var moneyGain = 0;
if (!isOperation) {
moneyGain = BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * this.skillMultipliers.money;
Player.gainMoney(moneyGain);
Player.recordMoneySource(moneyGain, "bladeburner");
}
if (isOperation) {
action.setMaxLevel(BladeburnerConstants.OperationSuccessesPerLevel);
} else {
action.setMaxLevel(BladeburnerConstants.ContractSuccessesPerLevel);
}
if (action.rankGain) {
var gain = addOffset(action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank, 10);
this.changeRank(gain);
if (isOperation && this.logging.ops) {
this.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
} else if (!isOperation && this.logging.contracts) {
this.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeralWrapper.formatMoney(moneyGain));
}
}
isOperation ? this.completeOperation(true) : this.completeContract(true);
} else {
gainActionStats(this, Player, action, false);
++action.failures;
var loss = 0, damage = 0;
if (action.rankLoss) {
loss = addOffset(action.rankLoss * rewardMultiplier, 10);
this.changeRank(-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)) {
++this.numHosp;
this.moneyLost += cost;
}
}
var logLossText = "";
if (loss > 0) {logLossText += "Lost " + formatNumber(loss, 3) + " rank. ";}
if (damage > 0) {logLossText += "Took " + formatNumber(damage, 0) + " damage.";}
if (isOperation && this.logging.ops) {
this.log(action.name + " failed! " + logLossText);
} else if (!isOperation && this.logging.contracts) {
this.log(action.name + " contract failed! " + logLossText);
}
isOperation ? this.completeOperation(false) : this.completeContract(false);
}
if (action.autoLevel) {action.level = action.maxLevel;} // Autolevel
this.startAction(this.action); // Repeat action
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
try {
var action = this.getActionObject(this.action);
if (action == null || !(action instanceof BlackOperation)) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
}
var difficulty = action.getDifficulty();
var difficultyMultiplier = Math.pow(difficulty, BladeburnerConstants.DiffMultExponentialFactor) + difficulty / BladeburnerConstants.DiffMultLinearFactor;
// Stamina loss is based on difficulty
this.stamina -= (BladeburnerConstants.BaseStaminaLoss * difficultyMultiplier);
if (this.stamina < 0) {this.stamina = 0;}
// Team loss variables
var teamCount = action.teamCount, teamLossMax;
if (action.attempt(this)) {
gainActionStats(this, Player, action, true);
action.count = 0;
this.blackops[action.name] = true;
var rankGain = 0;
if (action.rankGain) {
rankGain = addOffset(action.rankGain * BitNodeMultipliers.BladeburnerRank, 10);
this.changeRank(rankGain);
}
teamLossMax = Math.ceil(teamCount/2);
// Operation Daedalus
if (action.name === "Operation Daedalus") {
this.resetAction();
return hackWorldDaemon(Player.bitNodeN);
}
if (this.logging.blackops) {
this.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank");
}
} else {
gainActionStats(this, Player, action, false);
var rankLoss = 0, damage = 0;
if (action.rankLoss) {
rankLoss = addOffset(action.rankLoss, 10);
this.changeRank(-1 * rankLoss);
}
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(Player, damage);
if (Player.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += cost;
}
}
teamLossMax = Math.floor(teamCount);
if (this.logging.blackops) {
this.log(action.name + " failed! Lost " + formatNumber(rankLoss, 1) + " rank and took " + formatNumber(damage, 0) + " damage");
}
}
this.resetAction(); // Stop regardless of success or fail
// Calculate team lossses
if (teamCount >= 1) {
var losses = getRandomInt(1, teamLossMax);
this.teamSize -= losses;
this.teamLost += losses;
if (this.logging.blackops) {
this.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
}
}
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["Training"]:
this.stamina -= (0.5 * BladeburnerConstants.BaseStaminaLoss);
var strExpGain = 30 * Player.strength_exp_mult,
defExpGain = 30 * Player.defense_exp_mult,
dexExpGain = 30 * Player.dexterity_exp_mult,
agiExpGain = 30 * Player.agility_exp_mult,
staminaGain = 0.04 * this.skillMultipliers.stamina;
Player.gainStrengthExp(strExpGain);
Player.gainDefenseExp(defExpGain);
Player.gainDexterityExp(dexExpGain);
Player.gainAgilityExp(agiExpGain);
this.staminaBonus += (staminaGain);
if (this.logging.general) {
this.log("Training completed. Gained: " +
formatNumber(strExpGain, 1) + " str exp, " +
formatNumber(defExpGain, 1) + " def exp, " +
formatNumber(dexExpGain, 1) + " dex exp, " +
formatNumber(agiExpGain, 1) + " agi exp, " +
formatNumber(staminaGain, 3) + " max stamina");
}
this.startAction(this.action); // Repeat action
break;
case ActionTypes["FieldAnalysis"]:
case ActionTypes["Field Analysis"]:
// Does not use stamina. Effectiveness depends on hacking, int, and cha
var eff = 0.04 * Math.pow(Player.hacking_skill, 0.3) +
0.04 * Math.pow(Player.intelligence, 0.9) +
0.02 * Math.pow(Player.charisma, 0.3);
eff *= Player.bladeburner_analysis_mult;
if (isNaN(eff) || eff < 0) {
throw new Error("Field Analysis Effectiveness calculated to be NaN or negative");
}
var hackingExpGain = 20 * Player.hacking_exp_mult,
charismaExpGain = 20 * Player.charisma_exp_mult;
Player.gainHackingExp(hackingExpGain);
Player.gainIntelligenceExp(BladeburnerConstants.BaseIntGain);
Player.gainCharismaExp(charismaExpGain);
this.changeRank(0.1 * BitNodeMultipliers.BladeburnerRank);
this.getCurrentCity().improvePopulationEstimateByPercentage(eff * this.skillMultipliers.successChanceEstimate);
if (this.logging.general) {
this.log("Field analysis completed. Gained 0.1 rank, " + formatNumber(hackingExpGain, 1) + " hacking exp, and " + formatNumber(charismaExpGain, 1) + " charisma exp");
}
this.startAction(this.action); // Repeat action
break;
case ActionTypes["Recruitment"]:
var successChance = getRecruitmentSuccessChance(this, Player);
if (Math.random() < successChance) {
var expGain = 2 * BladeburnerConstants.BaseStatGain * this.actionTimeToComplete;
Player.gainCharismaExp(expGain);
++this.teamSize;
if (this.logging.general) {
this.log("Successfully recruited a team member! Gained " + formatNumber(expGain, 1) + " charisma exp");
}
} else {
var expGain = BladeburnerConstants.BaseStatGain * this.actionTimeToComplete;
Player.gainCharismaExp(expGain);
if (this.logging.general) {
this.log("Failed to recruit a team member. Gained " + formatNumber(expGain, 1) + " charisma exp");
}
}
this.startAction(this.action); // Repeat action
break;
case ActionTypes["Diplomacy"]:
var eff = getDiplomacyEffectiveness(this, Player);
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)}`);
}
this.startAction(this.action); // Repeat Action
break;
case ActionTypes["Hyperbolic Regeneration Chamber"]: {
Player.regenerateHp(BladeburnerConstants.HrcHpGain);
const staminaGain = this.maxStamina * (BladeburnerConstants.HrcStaminaGain / 100);
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
this.startAction(this.action);
if (this.logging.general) {
this.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${BladeburnerConstants.HrcHpGain} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`);
}
break;
}
default:
console.error(`Bladeburner.completeAction() called for invalid action: ${this.action.type}`);
break;
}
}
Bladeburner.prototype.completeContract = function(success) {
if (this.action.type !== ActionTypes.Contract) {
throw new Error("completeContract() called even though current action is not a Contract");
}
var city = this.getCurrentCity();
if (success) {
switch (this.action.name) {
case "Tracking":
// Increase estimate accuracy by a relatively small amount
city.improvePopulationEstimateByCount(getRandomInt(100, 1e3));
break;
case "Bounty Hunter":
city.changePopulationByCount(-1, {estChange:-1});
city.changeChaosByCount(0.02);
break;
case "Retirement":
city.changePopulationByCount(-1, {estChange:-1});
city.changeChaosByCount(0.04);
break;
default:
throw new Error("Invalid Action name in completeContract: " + this.action.name);
}
}
}
Bladeburner.prototype.completeOperation = function(success) {
if (this.action.type !== ActionTypes.Operation) {
throw new Error("completeOperation() called even though current action is not an Operation");
}
var action = this.getActionObject(this.action);
if (action == null) {
throw new Error("Failed to get Contract/Operation Object for: " + this.action.name);
}
// Calculate team losses
var teamCount = action.teamCount, max;
if (teamCount >= 1) {
if (success) {
max = Math.ceil(teamCount/2);
} else {
max = Math.floor(teamCount)
}
var losses = getRandomInt(0, max);
this.teamSize -= losses;
this.teamLost += losses;
if (this.logging.ops && losses > 0) {
this.log("Lost " + formatNumber(losses, 0) + " team members during this " + action.name);
}
}
var city = this.getCurrentCity();
switch (action.name) {
case "Investigation":
if (success) {
city.improvePopulationEstimateByPercentage(0.4 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < (0.02 * this.skillMultipliers.successChanceEstimate)) {
city.improveCommunityEstimate(1);
}
} else {
triggerPotentialMigration(this, this.city, 0.1);
}
break;
case "Undercover Operation":
if (success) {
city.improvePopulationEstimateByPercentage(0.8 * this.skillMultipliers.successChanceEstimate);
if (Math.random() < (0.02 * this.skillMultipliers.successChanceEstimate)) {
city.improveCommunityEstimate(1);
}
} else {
triggerPotentialMigration(this, this.city, 0.15);
}
break;
case "Sting Operation":
if (success) {
city.changePopulationByPercentage(-0.1, {changeEstEqually:true, nonZero:true});
}
city.changeChaosByCount(0.1);
break;
case "Raid":
if (success) {
city.changePopulationByPercentage(-1, {changeEstEqually:true, nonZero:true});
--city.comms;
--city.commsEst;
} else {
var change = getRandomInt(-10, -5) / 10;
city.changePopulationByPercentage(change, {nonZero:true});
}
city.changeChaosByPercentage(getRandomInt(1, 5));
break;
case "Stealth Retirement Operation":
if (success) {
city.changePopulationByPercentage(-0.5, {changeEstEqually:true,nonZero:true});
}
city.changeChaosByPercentage(getRandomInt(-3, -1));
break;
case "Assassination":
if (success) {
city.changePopulationByCount(-1, {estChange:-1});
}
city.changeChaosByPercentage(getRandomInt(-5, 5));
break;
default:
throw new Error("Invalid Action name in completeOperation: " + this.action.name);
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/////////////////////////////Unconvertable for now////////////////////////////// //////////////////////////// Unconvertable for now /////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Bladeburner Console Window // Bladeburner Console Window
@ -995,7 +458,7 @@ Bladeburner.prototype.executeConsoleCommands = function(commands) {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////Netscript Fns////////////////////////////////// //////////////////////////////// Netscript Fns /////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Bladeburner.prototype.getTypeAndNameFromActionId = function(actionId) { Bladeburner.prototype.getTypeAndNameFromActionId = function(actionId) {
@ -1044,7 +507,7 @@ Bladeburner.prototype.startActionNetscriptFn = function(type, name, workerScript
// Special logic for Black Ops // Special logic for Black Ops
if (actionId.type === ActionTypes["BlackOp"]) { if (actionId.type === ActionTypes["BlackOp"]) {
// Can't start a BlackOp if you don't have the required rank // Can't start a BlackOp if you don't have the required rank
let action = this.getActionObject(actionId); let action = getActionObject(this, actionId);
if (action.reqdRank > this.rank) { if (action.reqdRank > this.rank) {
workerScript.log("bladeburner.startAction", `Insufficient rank to start Black Op '${actionId.name}'.`); workerScript.log("bladeburner.startAction", `Insufficient rank to start Black Op '${actionId.name}'.`);
return false; return false;
@ -1080,11 +543,11 @@ Bladeburner.prototype.startActionNetscriptFn = function(type, name, workerScript
} }
try { try {
this.startAction(actionId); startAction(this, Player, actionId);
workerScript.log("bladeburner.startAction", `Starting bladeburner action with type '${type}' and name ${name}"`); workerScript.log("bladeburner.startAction", `Starting bladeburner action with type '${type}' and name ${name}"`);
return true; return true;
} catch(e) { } catch(e) {
this.resetAction(); resetAction(this);
workerScript.log("bladeburner.startAction", errorLogText); workerScript.log("bladeburner.startAction", errorLogText);
return false; return false;
} }
@ -1098,7 +561,7 @@ Bladeburner.prototype.getActionTimeNetscriptFn = function(type, name, workerScri
return -1; return -1;
} }
const actionObj = this.getActionObject(actionId); const actionObj = getActionObject(this, actionId);
if (actionObj == null) { if (actionObj == null) {
workerScript.log("bladeburner.getActionTime", errorLogText); workerScript.log("bladeburner.getActionTime", errorLogText);
return -1; return -1;
@ -1133,7 +596,7 @@ Bladeburner.prototype.getActionEstimatedSuccessChanceNetscriptFn = function(type
return -1; return -1;
} }
const actionObj = this.getActionObject(actionId); const actionObj = getActionObject(this, actionId);
if (actionObj == null) { if (actionObj == null) {
workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText); workerScript.log("bladeburner.getActionEstimatedSuccessChance", errorLogText);
return -1; return -1;
@ -1165,7 +628,7 @@ Bladeburner.prototype.getActionCountRemainingNetscriptFn = function(type, name,
return -1; return -1;
} }
const actionObj = this.getActionObject(actionId); const actionObj = getActionObject(this, actionId);
if (actionObj == null) { if (actionObj == null) {
workerScript.log("bladeburner.getActionCountRemaining", errorLogText); workerScript.log("bladeburner.getActionCountRemaining", errorLogText);
return -1; return -1;
@ -1261,7 +724,7 @@ Bladeburner.prototype.getTeamSizeNetscriptFn = function(type, name, workerScript
return -1; return -1;
} }
const actionObj = this.getActionObject(actionId); const actionObj = getActionObject(this, actionId);
if (actionObj == null) { if (actionObj == null) {
workerScript.log("bladeburner.getTeamSize", errorLogText); workerScript.log("bladeburner.getTeamSize", errorLogText);
return -1; return -1;
@ -1291,7 +754,7 @@ Bladeburner.prototype.setTeamSizeNetscriptFn = function(type, name, size, worker
return -1; return -1;
} }
const actionObj = this.getActionObject(actionId); const actionObj = getActionObject(this, actionId);
if (actionObj == null) { if (actionObj == null) {
workerScript.log("bladeburner.setTeamSize", errorLogText); workerScript.log("bladeburner.setTeamSize", errorLogText);
return -1; return -1;

@ -7,6 +7,7 @@ import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier"; import { ActionIdentifier } from "./ActionIdentifier";
import { ActionTypes } from "./data/ActionTypes"; import { ActionTypes } from "./data/ActionTypes";
import { BlackOperations } from "./BlackOperations"; import { BlackOperations } from "./BlackOperations";
import { BlackOperation } from "./BlackOperation";
import { GeneralActions } from "./GeneralActions"; import { GeneralActions } from "./GeneralActions";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { Skills } from "./Skills"; import { Skills } from "./Skills";
@ -18,6 +19,13 @@ import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { hackWorldDaemon } from "../RedPill";
export function getActionIdFromTypeAndName(bladeburner: IBladeburner, type: string = "", name: string = ""): IActionIdentifier | null { export function getActionIdFromTypeAndName(bladeburner: IBladeburner, type: string = "", name: string = ""): IActionIdentifier | null {
if (type === "" || name === "") {return null;} if (type === "" || name === "") {return null;}
@ -104,7 +112,7 @@ export function getActionIdFromTypeAndName(bladeburner: IBladeburner, type: stri
return null; return null;
} }
export function executeStartConsoleCommand(bladeburner: IBladeburner, args: string[]): void { export function executeStartConsoleCommand(bladeburner: IBladeburner, player: IPlayer, args: string[]): void {
if (args.length !== 3) { if (args.length !== 3) {
bladeburner.postToConsole("Invalid usage of 'start' console command: start [type] [name]"); bladeburner.postToConsole("Invalid usage of 'start' console command: start [type] [name]");
bladeburner.postToConsole("Use 'help start' for more info"); bladeburner.postToConsole("Use 'help start' for more info");
@ -117,7 +125,7 @@ export function executeStartConsoleCommand(bladeburner: IBladeburner, args: stri
if (GeneralActions[name] != null) { if (GeneralActions[name] != null) {
bladeburner.action.type = ActionTypes[name]; bladeburner.action.type = ActionTypes[name];
bladeburner.action.name = name; bladeburner.action.name = name;
bladeburner.startAction(bladeburner.action); startAction(bladeburner,player, bladeburner.action);
} else { } else {
bladeburner.postToConsole("Invalid action name specified: " + args[2]); bladeburner.postToConsole("Invalid action name specified: " + args[2]);
} }
@ -127,7 +135,7 @@ export function executeStartConsoleCommand(bladeburner: IBladeburner, args: stri
if (bladeburner.contracts[name] != null) { if (bladeburner.contracts[name] != null) {
bladeburner.action.type = ActionTypes.Contract; bladeburner.action.type = ActionTypes.Contract;
bladeburner.action.name = name; bladeburner.action.name = name;
bladeburner.startAction(bladeburner.action); startAction(bladeburner,player, bladeburner.action);
} else { } else {
bladeburner.postToConsole("Invalid contract name specified: " + args[2]); bladeburner.postToConsole("Invalid contract name specified: " + args[2]);
} }
@ -139,7 +147,7 @@ export function executeStartConsoleCommand(bladeburner: IBladeburner, args: stri
if (bladeburner.operations[name] != null) { if (bladeburner.operations[name] != null) {
bladeburner.action.type = ActionTypes.Operation; bladeburner.action.type = ActionTypes.Operation;
bladeburner.action.name = name; bladeburner.action.name = name;
bladeburner.startAction(bladeburner.action); startAction(bladeburner,player, bladeburner.action);
} else { } else {
bladeburner.postToConsole("Invalid Operation name specified: " + args[2]); bladeburner.postToConsole("Invalid Operation name specified: " + args[2]);
} }
@ -151,7 +159,7 @@ export function executeStartConsoleCommand(bladeburner: IBladeburner, args: stri
if (BlackOperations[name] != null) { if (BlackOperations[name] != null) {
bladeburner.action.type = ActionTypes.BlackOperation; bladeburner.action.type = ActionTypes.BlackOperation;
bladeburner.action.name = name; bladeburner.action.name = name;
bladeburner.startAction(bladeburner.action); startAction(bladeburner,player, bladeburner.action);
} else { } else {
bladeburner.postToConsole("Invalid BlackOp name specified: " + args[2]); bladeburner.postToConsole("Invalid BlackOp name specified: " + args[2]);
} }
@ -512,7 +520,7 @@ export function parseCommandArguments(command: string): string[] {
return args; return args;
} }
export function executeConsoleCommand(bladeburner: IBladeburner, command: string) { export function executeConsoleCommand(bladeburner: IBladeburner, player: IPlayer, command: string) {
command = command.trim(); command = command.trim();
command = command.replace(/\s\s+/g, ' '); // Replace all whitespace w/ a single space command = command.replace(/\s\s+/g, ' '); // Replace all whitespace w/ a single space
@ -537,7 +545,7 @@ export function executeConsoleCommand(bladeburner: IBladeburner, command: string
executeSkillConsoleCommand(bladeburner, args); executeSkillConsoleCommand(bladeburner, args);
break; break;
case "start": case "start":
executeStartConsoleCommand(bladeburner, args); executeStartConsoleCommand(bladeburner, player, args);
break; break;
case "stop": case "stop":
bladeburner.resetAction(); bladeburner.resetAction();
@ -549,7 +557,7 @@ export function executeConsoleCommand(bladeburner: IBladeburner, command: string
} }
// Handles a potential series of commands (comm1; comm2; comm3;) // Handles a potential series of commands (comm1; comm2; comm3;)
export function executeConsoleCommands(bladeburner: IBladeburner, commands: string): void { export function executeConsoleCommands(bladeburner: IBladeburner, player: IPlayer, commands: string): void {
try { try {
// Console History // Console History
if (bladeburner.consoleHistory[bladeburner.consoleHistory.length-1] != commands) { if (bladeburner.consoleHistory[bladeburner.consoleHistory.length-1] != commands) {
@ -561,7 +569,7 @@ export function executeConsoleCommands(bladeburner: IBladeburner, commands: stri
const arrayOfCommands = commands.split(";"); const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) { for (let i = 0; i < arrayOfCommands.length; ++i) {
executeConsoleCommand(bladeburner, arrayOfCommands[i]); executeConsoleCommand(bladeburner, player, arrayOfCommands[i]);
} }
} catch(e) { } catch(e) {
exceptionAlert(e); exceptionAlert(e);
@ -742,3 +750,564 @@ export function getRecruitmentTime(bladeburner: IBladeburner, player: IPlayer):
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90; const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor)); return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor));
} }
export function resetSkillMultipliers(bladeburner: IBladeburner): void {
bladeburner.skillMultipliers = {
successChanceAll: 1,
successChanceStealth: 1,
successChanceKill: 1,
successChanceContract: 1,
successChanceOperation: 1,
successChanceEstimate: 1,
actionTime: 1,
effHack: 1,
effStr: 1,
effDef: 1,
effDex: 1,
effAgi: 1,
effCha: 1,
effInt: 1,
stamina: 1,
money: 1,
expGain: 1,
};
}
export function updateSkillMultipliers(bladeburner: IBladeburner): void {
resetSkillMultipliers(bladeburner);
for (const skillName in bladeburner.skills) {
if (bladeburner.skills.hasOwnProperty(skillName)) {
const skill = Skills[skillName];
if (skill == null) {
throw new Error("Could not find Skill Object for: " + skillName);
}
const level = bladeburner.skills[skillName];
if (level == null || level <= 0) {continue;} //Not upgraded
const multiplierNames = Object.keys(bladeburner.skillMultipliers);
for (let i = 0; i < multiplierNames.length; ++i) {
const multiplierName = multiplierNames[i];
if (skill.getMultiplier(multiplierName) != null && !isNaN(skill.getMultiplier(multiplierName))) {
const value = skill.getMultiplier(multiplierName) * level;
let multiplierValue = 1 + (value / 100);
if (multiplierName === "actionTime") {
multiplierValue = 1 - (value / 100);
}
bladeburner.skillMultipliers[multiplierName] *= multiplierValue;
}
}
}
}
}
// Sets the player to the "IDLE" action
export function resetAction(bladeburner: IBladeburner): void {
bladeburner.action = new ActionIdentifier({type:ActionTypes.Idle});
}
export function completeOperation(bladeburner: IBladeburner, success: boolean): void {
if (bladeburner.action.type !== ActionTypes.Operation) {
throw new Error("completeOperation() called even though current action is not an Operation");
}
const action = getActionObject(bladeburner, bladeburner.action);
if (action == null) {
throw new Error("Failed to get Contract/Operation Object for: " + bladeburner.action.name);
}
// Calculate team losses
const teamCount = action.teamCount;
if (teamCount >= 1) {
let max;
if (success) {
max = Math.ceil(teamCount/2);
} else {
max = Math.floor(teamCount)
}
const losses = getRandomInt(0, max);
bladeburner.teamSize -= losses;
bladeburner.teamLost += losses;
if (bladeburner.logging.ops && losses > 0) {
bladeburner.log("Lost " + formatNumber(losses, 0) + " team members during this " + action.name);
}
}
const city = bladeburner.getCurrentCity();
switch (action.name) {
case "Investigation":
if (success) {
city.improvePopulationEstimateByPercentage(0.4 * bladeburner.skillMultipliers.successChanceEstimate);
if (Math.random() < (0.02 * bladeburner.skillMultipliers.successChanceEstimate)) {
city.improveCommunityEstimate(1);
}
} else {
triggerPotentialMigration(bladeburner, bladeburner.city, 0.1);
}
break;
case "Undercover Operation":
if (success) {
city.improvePopulationEstimateByPercentage(0.8 * bladeburner.skillMultipliers.successChanceEstimate);
if (Math.random() < (0.02 * bladeburner.skillMultipliers.successChanceEstimate)) {
city.improveCommunityEstimate(1);
}
} else {
triggerPotentialMigration(bladeburner, bladeburner.city, 0.15);
}
break;
case "Sting Operation":
if (success) {
city.changePopulationByPercentage(-0.1, {changeEstEqually:true, nonZero:true});
}
city.changeChaosByCount(0.1);
break;
case "Raid":
if (success) {
city.changePopulationByPercentage(-1, {changeEstEqually:true, nonZero:true});
--city.comms;
--city.commsEst;
} else {
const change = getRandomInt(-10, -5) / 10;
city.changePopulationByPercentage(change, {nonZero:true, changeEstEqually:false});
}
city.changeChaosByPercentage(getRandomInt(1, 5));
break;
case "Stealth Retirement Operation":
if (success) {
city.changePopulationByPercentage(-0.5, {changeEstEqually:true,nonZero:true});
}
city.changeChaosByPercentage(getRandomInt(-3, -1));
break;
case "Assassination":
if (success) {
city.changePopulationByCount(-1, {estChange:-1, estOffset: 0});
}
city.changeChaosByPercentage(getRandomInt(-5, 5));
break;
default:
throw new Error("Invalid Action name in completeOperation: " + bladeburner.action.name);
}
}
export function getActionObject(bladeburner: IBladeburner, actionId: IActionIdentifier): IAction | null {
/**
* Given an ActionIdentifier object, returns the corresponding
* GeneralAction, Contract, Operation, or BlackOperation object
*/
switch (actionId.type) {
case ActionTypes["Contract"]:
return bladeburner.contracts[actionId.name];
case ActionTypes["Operation"]:
return bladeburner.operations[actionId.name];
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]:
return BlackOperations[actionId.name];
case ActionTypes["Training"]:
return GeneralActions["Training"];
case ActionTypes["Field Analysis"]:
return GeneralActions["Field Analysis"];
case ActionTypes["Recruitment"]:
return GeneralActions["Recruitment"];
case ActionTypes["Diplomacy"]:
return GeneralActions["Diplomacy"];
case ActionTypes["Hyperbolic Regeneration Chamber"]:
return GeneralActions["Hyperbolic Regeneration Chamber"];
default:
return null;
}
}
export function completeContract(bladeburner: IBladeburner, success: boolean): void {
if (bladeburner.action.type !== ActionTypes.Contract) {
throw new Error("completeContract() called even though current action is not a Contract");
}
var city = bladeburner.getCurrentCity();
if (success) {
switch (bladeburner.action.name) {
case "Tracking":
// Increase estimate accuracy by a relatively small amount
city.improvePopulationEstimateByCount(getRandomInt(100, 1e3));
break;
case "Bounty Hunter":
city.changePopulationByCount(-1, {estChange:-1, estOffset: 0});
city.changeChaosByCount(0.02);
break;
case "Retirement":
city.changePopulationByCount(-1, {estChange:-1, estOffset: 0});
city.changeChaosByCount(0.04);
break;
default:
throw new Error("Invalid Action name in completeContract: " + bladeburner.action.name);
}
}
}
export function completeAction(bladeburner: IBladeburner, player: IPlayer): void {
switch (bladeburner.action.type) {
case ActionTypes["Contract"]:
case ActionTypes["Operation"]: {
try {
const isOperation = (bladeburner.action.type === ActionTypes["Operation"]);
const action = getActionObject(bladeburner, bladeburner.action);
if (action == null) {
throw new Error("Failed to get Contract/Operation Object for: " + bladeburner.action.name);
}
const difficulty = action.getDifficulty();
const difficultyMultiplier = Math.pow(difficulty, BladeburnerConstants.DiffMultExponentialFactor) + difficulty / BladeburnerConstants.DiffMultLinearFactor;
const rewardMultiplier = Math.pow(action.rewardFac, action.level-1);
// Stamina loss is based on difficulty
bladeburner.stamina -= (BladeburnerConstants.BaseStaminaLoss * difficultyMultiplier);
if (bladeburner.stamina < 0) {bladeburner.stamina = 0;}
// Process Contract/Operation success/failure
if (action.attempt(bladeburner)) {
gainActionStats(bladeburner, player, action, true);
++action.successes;
--action.count;
// Earn money for contracts
let moneyGain = 0;
if (!isOperation) {
moneyGain = BladeburnerConstants.ContractBaseMoneyGain * rewardMultiplier * bladeburner.skillMultipliers.money;
player.gainMoney(moneyGain);
player.recordMoneySource(moneyGain, "bladeburner");
}
if (isOperation) {
action.setMaxLevel(BladeburnerConstants.OperationSuccessesPerLevel);
} else {
action.setMaxLevel(BladeburnerConstants.ContractSuccessesPerLevel);
}
if (action.rankGain) {
const gain = addOffset(action.rankGain * rewardMultiplier * BitNodeMultipliers.BladeburnerRank, 10);
changeRank(bladeburner, player, gain);
if (isOperation && bladeburner.logging.ops) {
bladeburner.log(action.name + " successfully completed! Gained " + formatNumber(gain, 3) + " rank");
} else if (!isOperation && bladeburner.logging.contracts) {
bladeburner.log(action.name + " contract successfully completed! Gained " + formatNumber(gain, 3) + " rank and " + numeralWrapper.formatMoney(moneyGain));
}
}
isOperation ? completeOperation(bladeburner, true) : completeContract(bladeburner, true);
} else {
gainActionStats(bladeburner, player, action, false);
++action.failures;
let loss = 0, damage = 0;
if (action.rankLoss) {
loss = addOffset(action.rankLoss * rewardMultiplier, 10);
changeRank(bladeburner, player, -1 * loss);
}
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
bladeburner.hpLost += damage;
const cost = calculateHospitalizationCost(player, damage);
if (player.takeDamage(damage)) {
++bladeburner.numHosp;
bladeburner.moneyLost += cost;
}
}
let logLossText = "";
if (loss > 0) {logLossText += "Lost " + formatNumber(loss, 3) + " rank. ";}
if (damage > 0) {logLossText += "Took " + formatNumber(damage, 0) + " damage.";}
if (isOperation && bladeburner.logging.ops) {
bladeburner.log(action.name + " failed! " + logLossText);
} else if (!isOperation && bladeburner.logging.contracts) {
bladeburner.log(action.name + " contract failed! " + logLossText);
}
isOperation ? completeOperation(bladeburner, false) : completeContract(bladeburner, false);
}
if (action.autoLevel) {action.level = action.maxLevel;} // Autolevel
startAction(bladeburner,player, bladeburner.action); // Repeat action
} catch(e) {
exceptionAlert(e);
}
break;
}
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]: {
try {
const action = getActionObject(bladeburner, bladeburner.action);
if (action == null || !(action instanceof BlackOperation)) {
throw new Error("Failed to get BlackOperation Object for: " + bladeburner.action.name);
}
const difficulty = action.getDifficulty();
const difficultyMultiplier = Math.pow(difficulty, BladeburnerConstants.DiffMultExponentialFactor) + difficulty / BladeburnerConstants.DiffMultLinearFactor;
// Stamina loss is based on difficulty
bladeburner.stamina -= (BladeburnerConstants.BaseStaminaLoss * difficultyMultiplier);
if (bladeburner.stamina < 0) {bladeburner.stamina = 0;}
// Team loss variables
const teamCount = action.teamCount;
let teamLossMax;
if (action.attempt(bladeburner)) {
gainActionStats(bladeburner, player, action, true);
action.count = 0;
bladeburner.blackops[action.name] = true;
let rankGain = 0;
if (action.rankGain) {
rankGain = addOffset(action.rankGain * BitNodeMultipliers.BladeburnerRank, 10);
changeRank(bladeburner, player, rankGain);
}
teamLossMax = Math.ceil(teamCount/2);
// Operation Daedalus
if (action.name === "Operation Daedalus") {
resetAction(bladeburner);
return hackWorldDaemon(player.bitNodeN);
}
if (bladeburner.logging.blackops) {
bladeburner.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank");
}
} else {
gainActionStats(bladeburner, player, action, false);
let rankLoss = 0;
let damage = 0;
if (action.rankLoss) {
rankLoss = addOffset(action.rankLoss, 10);
changeRank(bladeburner, player, -1 * rankLoss);
}
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(player, damage);
if (player.takeDamage(damage)) {
++bladeburner.numHosp;
bladeburner.moneyLost += cost;
}
}
teamLossMax = Math.floor(teamCount);
if (bladeburner.logging.blackops) {
bladeburner.log(action.name + " failed! Lost " + formatNumber(rankLoss, 1) + " rank and took " + formatNumber(damage, 0) + " damage");
}
}
resetAction(bladeburner); // Stop regardless of success or fail
// Calculate team lossses
if (teamCount >= 1) {
const losses = getRandomInt(1, teamLossMax);
bladeburner.teamSize -= losses;
bladeburner.teamLost += losses;
if (bladeburner.logging.blackops) {
bladeburner.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
}
}
} catch(e) {
exceptionAlert(e);
}
break;
}
case ActionTypes["Training"]: {
bladeburner.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,
staminaGain = 0.04 * bladeburner.skillMultipliers.stamina;
player.gainStrengthExp(strExpGain);
player.gainDefenseExp(defExpGain);
player.gainDexterityExp(dexExpGain);
player.gainAgilityExp(agiExpGain);
bladeburner.staminaBonus += (staminaGain);
if (bladeburner.logging.general) {
bladeburner.log("Training completed. Gained: " +
formatNumber(strExpGain, 1) + " str exp, " +
formatNumber(defExpGain, 1) + " def exp, " +
formatNumber(dexExpGain, 1) + " dex exp, " +
formatNumber(agiExpGain, 1) + " agi exp, " +
formatNumber(staminaGain, 3) + " max stamina");
}
startAction(bladeburner,player, bladeburner.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_skill, 0.3) +
0.04 * Math.pow(player.intelligence, 0.9) +
0.02 * Math.pow(player.charisma, 0.3);
eff *= player.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,
charismaExpGain = 20 * player.charisma_exp_mult;
player.gainHackingExp(hackingExpGain);
player.gainIntelligenceExp(BladeburnerConstants.BaseIntGain);
player.gainCharismaExp(charismaExpGain);
changeRank(bladeburner, player, 0.1 * BitNodeMultipliers.BladeburnerRank);
bladeburner.getCurrentCity().improvePopulationEstimateByPercentage(eff * bladeburner.skillMultipliers.successChanceEstimate);
if (bladeburner.logging.general) {
bladeburner.log("Field analysis completed. Gained 0.1 rank, " + formatNumber(hackingExpGain, 1) + " hacking exp, and " + formatNumber(charismaExpGain, 1) + " charisma exp");
}
startAction(bladeburner,player, bladeburner.action); // Repeat action
break;
}
case ActionTypes["Recruitment"]: {
const successChance = getRecruitmentSuccessChance(bladeburner, player);
if (Math.random() < successChance) {
const expGain = 2 * BladeburnerConstants.BaseStatGain * bladeburner.actionTimeToComplete;
player.gainCharismaExp(expGain);
++bladeburner.teamSize;
if (bladeburner.logging.general) {
bladeburner.log("Successfully recruited a team member! Gained " + formatNumber(expGain, 1) + " charisma exp");
}
} else {
const expGain = BladeburnerConstants.BaseStatGain * bladeburner.actionTimeToComplete;
player.gainCharismaExp(expGain);
if (bladeburner.logging.general) {
bladeburner.log("Failed to recruit a team member. Gained " + formatNumber(expGain, 1) + " charisma exp");
}
}
startAction(bladeburner,player, bladeburner.action); // Repeat action
break;
}
case ActionTypes["Diplomacy"]: {
let eff = getDiplomacyEffectiveness(bladeburner, player);
bladeburner.getCurrentCity().chaos *= eff;
if (bladeburner.getCurrentCity().chaos < 0) { bladeburner.getCurrentCity().chaos = 0; }
if (bladeburner.logging.general) {
bladeburner.log(`Diplomacy completed. Chaos levels in the current city fell by ${numeralWrapper.formatPercentage(1 - eff)}`);
}
startAction(bladeburner,player, bladeburner.action); // Repeat Action
break;
}
case ActionTypes["Hyperbolic Regeneration Chamber"]: {
player.regenerateHp(BladeburnerConstants.HrcHpGain);
const staminaGain = bladeburner.maxStamina * (BladeburnerConstants.HrcStaminaGain / 100);
bladeburner.stamina = Math.min(bladeburner.maxStamina, bladeburner.stamina + staminaGain);
startAction(bladeburner,player, bladeburner.action);
if (bladeburner.logging.general) {
bladeburner.log(`Rested in Hyperbolic Regeneration Chamber. Restored ${BladeburnerConstants.HrcHpGain} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`);
}
break;
}
default:
console.error(`Bladeburner.completeAction() called for invalid action: ${bladeburner.action.type}`);
break;
}
}
export function changeRank(bladeburner: IBladeburner, player: IPlayer, change: number): void {
if (isNaN(change)) {throw new Error("NaN passed into Bladeburner.changeRank()");}
bladeburner.rank += change;
if (bladeburner.rank < 0) {bladeburner.rank = 0;}
bladeburner.maxRank = Math.max(bladeburner.rank, bladeburner.maxRank);
var bladeburnersFactionName = "Bladeburners";
if (factionExists(bladeburnersFactionName)) {
var bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) {
throw new Error("Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button");
}
if (bladeburnerFac.isMember) {
var favorBonus = 1 + (bladeburnerFac.favor / 100);
bladeburnerFac.playerReputation += (BladeburnerConstants.RankToFactionRepFactor * change * player.faction_rep_mult * favorBonus);
}
}
// Gain skill points
var rankNeededForSp = (bladeburner.totalSkillPoints+1) * BladeburnerConstants.RanksPerSkillPoint;
if (bladeburner.maxRank >= rankNeededForSp) {
// Calculate how many skill points to gain
var gainedSkillPoints = Math.floor((bladeburner.maxRank - rankNeededForSp) / BladeburnerConstants.RanksPerSkillPoint + 1);
bladeburner.skillPoints += gainedSkillPoints;
bladeburner.totalSkillPoints += gainedSkillPoints;
}
}
export function processAction(bladeburner: IBladeburner, player: IPlayer, seconds: number): void {
if (bladeburner.action.type === ActionTypes["Idle"]) return;
if (bladeburner.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${bladeburner.actionTimeToComplete}, type; ${bladeburner.action.type}`);
}
if (!(bladeburner.action instanceof ActionIdentifier)) {
throw new Error("Bladeburner.action is not an ActionIdentifier Object");
}
// If the previous action went past its completion time, add to the next action
// This is not added inmediatly in case the automation changes the action
bladeburner.actionTimeCurrent += seconds + bladeburner.actionTimeOverflow;
bladeburner.actionTimeOverflow = 0;
if (bladeburner.actionTimeCurrent >= bladeburner.actionTimeToComplete) {
bladeburner.actionTimeOverflow = bladeburner.actionTimeCurrent - bladeburner.actionTimeToComplete;
return completeAction(bladeburner, player);
}
}
export function startAction(bladeburner: IBladeburner, player: IPlayer, actionId: IActionIdentifier): void {
if (actionId == null) return;
bladeburner.action = actionId;
bladeburner.actionTimeCurrent = 0;
switch (actionId.type) {
case ActionTypes["Idle"]:
bladeburner.actionTimeToComplete = 0;
break;
case ActionTypes["Contract"]:
try {
const action = getActionObject(bladeburner, actionId);
if (action == null) {
throw new Error("Failed to get Contract Object for: " + actionId.name);
}
if (action.count < 1) {return resetAction(bladeburner);}
bladeburner.actionTimeToComplete = action.getActionTime(bladeburner);
} catch(e) {
exceptionAlert(e);
}
break;
case ActionTypes["Operation"]: {
try {
const action = getActionObject(bladeburner, actionId);
if (action == null) {
throw new Error ("Failed to get Operation Object for: " + actionId.name);
}
if (action.count < 1) {return resetAction(bladeburner);}
if (actionId.name === "Raid" && bladeburner.getCurrentCity().commsEst === 0) {return resetAction(bladeburner);}
bladeburner.actionTimeToComplete = action.getActionTime(bladeburner);
} catch(e) {
exceptionAlert(e);
}
break;
}
case ActionTypes["BlackOp"]:
case ActionTypes["BlackOperation"]: {
try {
// Safety measure - don't repeat BlackOps that are already done
if (bladeburner.blackops[actionId.name] != null) {
resetAction(bladeburner);
bladeburner.log("Error: Tried to start a Black Operation that had already been completed");
break;
}
const action = getActionObject(bladeburner, actionId);
if (action == null) {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
bladeburner.actionTimeToComplete = action.getActionTime(bladeburner);
} catch(e) {
exceptionAlert(e);
}
break;
}
case ActionTypes["Recruitment"]:
bladeburner.actionTimeToComplete = getRecruitmentTime(bladeburner, player);
break;
case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]:
case ActionTypes["Field Analysis"]:
bladeburner.actionTimeToComplete = 30;
break;
case ActionTypes["Diplomacy"]:
case ActionTypes["Hyperbolic Regeneration Chamber"]:
bladeburner.actionTimeToComplete = 60;
break;
default:
throw new Error("Invalid Action Type in startAction(Bladeburner,player, ): " + actionId.type);
break;
}
}

@ -4,14 +4,14 @@ import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { addOffset } from "../../utils/helpers/addOffset"; import { addOffset } from "../../utils/helpers/addOffset";
export class ChangePopulationByCountParams { interface IChangePopulationByCountParams {
estChange = 0; estChange: number;
estOffset = 0; estOffset: number;
} }
export class ChangePopulationByPercentageParams { interface IChangePopulationByPercentageParams {
nonZero = false; nonZero: boolean;
changeEstEqually = false; changeEstEqually: boolean;
} }
export class City { export class City {
@ -113,7 +113,7 @@ export class City {
* estChange(int): How much the estimate should change by * estChange(int): How much the estimate should change by
* estOffset(int): Add offset to estimate (offset by percentage) * estOffset(int): Add offset to estimate (offset by percentage)
*/ */
changePopulationByCount(n: number, params: ChangePopulationByCountParams=new ChangePopulationByCountParams()): void { changePopulationByCount(n: number, params: IChangePopulationByCountParams = {estChange: 0, estOffset: 0}): void {
if (isNaN(n)) {throw new Error("NaN passed into City.changePopulationByCount()");} if (isNaN(n)) {throw new Error("NaN passed into City.changePopulationByCount()");}
this.pop += n; this.pop += n;
if (params.estChange && !isNaN(params.estChange)) {this.popEst += params.estChange;} if (params.estChange && !isNaN(params.estChange)) {this.popEst += params.estChange;}
@ -129,7 +129,7 @@ export class City {
* changeEstEqually(bool) - Change the population estimate by an equal amount * 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 * nonZero (bool) - Set to true to ensure that population always changes by at least 1
*/ */
changePopulationByPercentage(p: number, params: ChangePopulationByPercentageParams=new ChangePopulationByPercentageParams()): number { changePopulationByPercentage(p: number, params: IChangePopulationByPercentageParams={nonZero: false, changeEstEqually: false}): number {
if (isNaN(p)) {throw new Error("NaN passed into City.changePopulationByPercentage()");} if (isNaN(p)) {throw new Error("NaN passed into City.changePopulationByPercentage()");}
if (p === 0) {return 0;} if (p === 0) {return 0;}
let change = Math.round(this.pop * (p/100)); let change = Math.round(this.pop * (p/100));

@ -11,6 +11,7 @@ export interface IBladeburner {
totalSkillPoints: number; totalSkillPoints: number;
teamSize: number; teamSize: number;
teamLost: number; teamLost: number;
hpLost: number;
storedCycles: number; storedCycles: number;
randomEventCounter: number; randomEventCounter: number;
actionTimeToComplete: number; actionTimeToComplete: number;

@ -103,5 +103,28 @@ export class Skill {
calculateCost(currentLevel: number): number { calculateCost(currentLevel: number): number {
return Math.floor((this.baseCost + (currentLevel * this.costInc)) * BitNodeMultipliers.BladeburnerSkillCost); return Math.floor((this.baseCost + (currentLevel * this.costInc)) * BitNodeMultipliers.BladeburnerSkillCost);
} }
getMultiplier(name: string): number {
if(name === "successChanceAll") return this.successChanceAll;
if(name === "successChanceStealth") return this.successChanceStealth;
if(name === "successChanceKill") return this.successChanceKill;
if(name === "successChanceContract") return this.successChanceContract;
if(name === "successChanceOperation") return this.successChanceOperation;
if(name === "successChanceEstimate") return this.successChanceEstimate;
if(name === "actionTime") return this.actionTime;
if(name === "effHack") return this.effHack;
if(name === "effStr") return this.effStr;
if(name === "effDef") return this.effDef;
if(name === "effDex") return this.effDex;
if(name === "effAgi") return this.effAgi;
if(name === "effCha") return this.effCha;
if(name === "stamina") return this.stamina;
if(name === "money") return this.money;
if(name === "expGain") return this.expGain;
return 1;
}
} }

@ -6,9 +6,11 @@ import { BlackOpPage } from "./BlackOpPage";
import { SkillPage } from "./SkillPage"; import { SkillPage } from "./SkillPage";
import { stealthIcon, killIcon } from "../data/Icons"; import { stealthIcon, killIcon } from "../data/Icons";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function AllPages(props: IProps): React.ReactElement { export function AllPages(props: IProps): React.ReactElement {
@ -36,10 +38,10 @@ export function AllPages(props: IProps): React.ReactElement {
<Header name={'BlackOps'} /> <Header name={'BlackOps'} />
<Header name={'Skills'} /> <Header name={'Skills'} />
<div style={{display:"block", margin:"4px", padding:"4px"}}> <div style={{display:"block", margin:"4px", padding:"4px"}}>
{page === 'General' && <GeneralActionPage bladeburner={props.bladeburner} />} {page === 'General' && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
{page === 'Contracts' && <ContractPage bladeburner={props.bladeburner} />} {page === 'Contracts' && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
{page === 'Operations' && <OperationPage bladeburner={props.bladeburner} />} {page === 'Operations' && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
{page === 'BlackOps' && <BlackOpPage bladeburner={props.bladeburner} />} {page === 'BlackOps' && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
{page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />} {page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />}
</div> </div>
<span className="text">{stealthIcon}= This action requires stealth, {killIcon} = This action involves retirement</span> <span className="text">{stealthIcon}= This action requires stealth, {killIcon} = This action involves retirement</span>

@ -9,9 +9,12 @@ import { stealthIcon, killIcon } from "../data/Icons";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup"; import { TeamSizePopup } from "./TeamSizePopup";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { startAction } from "../Bladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: any; action: any;
} }
@ -32,7 +35,7 @@ export function BlackOpElem(props: IProps): React.ReactElement {
function onStart() { function onStart() {
props.bladeburner.action.type = ActionTypes.BlackOperation; props.bladeburner.action.type = ActionTypes.BlackOperation;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }

@ -10,9 +10,11 @@ import { BlackOperations } from "../BlackOperations";
import { BlackOperation } from "../BlackOperation"; import { BlackOperation } from "../BlackOperation";
import { BlackOpElem } from "./BlackOpElem"; import { BlackOpElem } from "./BlackOpElem";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function BlackOpList(props: IProps): React.ReactElement { export function BlackOpList(props: IProps): React.ReactElement {
@ -34,7 +36,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
return (<> return (<>
{blackops.map((blackop: BlackOperation) => <li key={blackop.name} className="bladeburner-action"> {blackops.map((blackop: BlackOperation) => <li key={blackop.name} className="bladeburner-action">
<BlackOpElem bladeburner={props.bladeburner} action={blackop} /> <BlackOpElem bladeburner={props.bladeburner} action={blackop} player={props.player} />
</li>, </li>,
)} )}
</>); </>);

@ -1,9 +1,11 @@
import * as React from "react"; import * as React from "react";
import { BlackOpList } from "./BlackOpList"; import { BlackOpList } from "./BlackOpList";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function BlackOpPage(props: IProps): React.ReactElement { export function BlackOpPage(props: IProps): React.ReactElement {
@ -21,6 +23,6 @@ export function BlackOpPage(props: IProps): React.ReactElement {
Like normal operations, you may use a team for Black Ops. Failing Like normal operations, you may use a team for Black Ops. Failing
a black op will incur heavy HP and rank losses. a black op will incur heavy HP and rank losses.
</p> </p>
<BlackOpList bladeburner={props.bladeburner} /> <BlackOpList bladeburner={props.bladeburner} player={props.player} />
</>); </>);
} }

@ -1,6 +1,8 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface ILineProps { interface ILineProps {
content: any; content: any;
} }
@ -13,6 +15,7 @@ function Line(props: ILineProps): React.ReactElement {
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function Console(props: IProps): React.ReactElement { export function Console(props: IProps): React.ReactElement {

@ -8,9 +8,12 @@ import {
import { stealthIcon, killIcon } from "../data/Icons"; import { stealthIcon, killIcon } from "../data/Icons";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { startAction } from "../Bladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: any; action: any;
} }
@ -26,19 +29,19 @@ export function ContractElem(props: IProps): React.ReactElement {
function onStart() { function onStart() {
props.bladeburner.action.type = ActionTypes.Contract; props.bladeburner.action.type = ActionTypes.Contract;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }
function increaseLevel() { function increaseLevel() {
++props.action.level; ++props.action.level;
if (isActive) props.bladeburner.startAction(props.bladeburner.action); if (isActive) startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }
function decreaseLevel() { function decreaseLevel() {
--props.action.level; --props.action.level;
if (isActive) props.bladeburner.startAction(props.bladeburner.action); if (isActive) startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }

@ -6,9 +6,11 @@ import {
import { ContractElem } from "./ContractElem"; import { ContractElem } from "./ContractElem";
import { Contract } from "../Contract"; import { Contract } from "../Contract";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function ContractList(props: IProps): React.ReactElement { export function ContractList(props: IProps): React.ReactElement {
@ -16,7 +18,7 @@ export function ContractList(props: IProps): React.ReactElement {
const contracts = props.bladeburner.contracts; const contracts = props.bladeburner.contracts;
return (<> return (<>
{names.map((name: string) => <li key={name} className="bladeburner-action"> {names.map((name: string) => <li key={name} className="bladeburner-action">
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} /> <ContractElem bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
</li>, </li>,
)} )}
</>); </>);

@ -1,9 +1,11 @@
import * as React from "react"; import * as React from "react";
import { ContractList } from "./ContractList"; import { ContractList } from "./ContractList";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function ContractPage(props: IProps): React.ReactElement { export function ContractPage(props: IProps): React.ReactElement {
@ -16,6 +18,6 @@ export function ContractPage(props: IProps): React.ReactElement {
You can unlock higher-level contracts by successfully completing them. You can unlock higher-level contracts by successfully completing them.
Higher-level contracts are more difficult, but grant more rank, experience, and money. Higher-level contracts are more difficult, but grant more rank, experience, and money.
</p> </p>
<ContractList bladeburner={props.bladeburner} /> <ContractList bladeburner={props.bladeburner} player={props.player} />
</>); </>);
} }

@ -8,9 +8,13 @@ import {
import { stealthIcon, killIcon } from "../data/Icons"; import { stealthIcon, killIcon } from "../data/Icons";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { startAction } from "../Bladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: any; action: any;
} }
@ -22,7 +26,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
function onStart() { function onStart() {
props.bladeburner.action.type = ActionTypes[(props.action.name as string)]; props.bladeburner.action.type = ActionTypes[(props.action.name as string)];
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }

@ -7,9 +7,11 @@ import { GeneralActionElem } from "./GeneralActionElem";
import { Action } from "../Action"; import { Action } from "../Action";
import { GeneralActions } from "../GeneralActions"; import { GeneralActions } from "../GeneralActions";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function GeneralActionList(props: IProps): React.ReactElement { export function GeneralActionList(props: IProps): React.ReactElement {
@ -21,7 +23,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
} }
return (<> return (<>
{actions.map((action: Action) => <li key={action.name} className="bladeburner-action"> {actions.map((action: Action) => <li key={action.name} className="bladeburner-action">
<GeneralActionElem bladeburner={props.bladeburner} action={action} /> <GeneralActionElem bladeburner={props.bladeburner} action={action} player={props.player} />
</li>, </li>,
)} )}
</>); </>);

@ -1,9 +1,11 @@
import * as React from "react"; import * as React from "react";
import { GeneralActionList } from "./GeneralActionList"; import { GeneralActionList } from "./GeneralActionList";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function GeneralActionPage(props: IProps): React.ReactElement { export function GeneralActionPage(props: IProps): React.ReactElement {
@ -12,6 +14,6 @@ export function GeneralActionPage(props: IProps): React.ReactElement {
These are generic actions that will assist you in your Bladeburner These are generic actions that will assist you in your Bladeburner
duties. They will not affect your Bladeburner rank in any way. duties. They will not affect your Bladeburner rank in any way.
</p> </p>
<GeneralActionList bladeburner={props.bladeburner} /> <GeneralActionList bladeburner={props.bladeburner} player={props.player} />
</>); </>);
} }

@ -10,9 +10,12 @@ import { BladeburnerConstants } from "../data/Constants";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup"; import { TeamSizePopup } from "./TeamSizePopup";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { startAction } from "../Bladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: any; action: any;
} }
@ -28,7 +31,7 @@ export function OperationElem(props: IProps): React.ReactElement {
function onStart() { function onStart() {
props.bladeburner.action.type = ActionTypes.Operation; props.bladeburner.action.type = ActionTypes.Operation;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }
@ -43,13 +46,13 @@ export function OperationElem(props: IProps): React.ReactElement {
function increaseLevel() { function increaseLevel() {
++props.action.level; ++props.action.level;
if (isActive) props.bladeburner.startAction(props.bladeburner.action); if (isActive) startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }
function decreaseLevel() { function decreaseLevel() {
--props.action.level; --props.action.level;
if (isActive) props.bladeburner.startAction(props.bladeburner.action); if (isActive) startAction(props.bladeburner, props.player, props.bladeburner.action);
setRerender(old => !old); setRerender(old => !old);
} }

@ -6,9 +6,11 @@ import {
import { OperationElem } from "./OperationElem"; import { OperationElem } from "./OperationElem";
import { Operation } from "../Operation"; import { Operation } from "../Operation";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function OperationList(props: IProps): React.ReactElement { export function OperationList(props: IProps): React.ReactElement {
@ -16,7 +18,7 @@ export function OperationList(props: IProps): React.ReactElement {
const operations = props.bladeburner.operations; const operations = props.bladeburner.operations;
return (<> return (<>
{names.map((name: string) => <li key={name} className="bladeburner-action"> {names.map((name: string) => <li key={name} className="bladeburner-action">
<OperationElem bladeburner={props.bladeburner} action={operations[name]} /> <OperationElem bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
</li>, </li>,
)} )}
</>); </>);

@ -1,9 +1,11 @@
import * as React from "react"; import * as React from "react";
import { OperationList } from "./OperationList"; import { OperationList } from "./OperationList";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function OperationPage(props: IProps): React.ReactElement { export function OperationPage(props: IProps): React.ReactElement {
@ -27,6 +29,6 @@ export function OperationPage(props: IProps): React.ReactElement {
You can unlock higher-level operations by successfully completing them. You can unlock higher-level operations by successfully completing them.
Higher-level operations are more difficult, but grant more rank and experience. Higher-level operations are more difficult, but grant more rank and experience.
</p> </p>
<OperationList bladeburner={props.bladeburner} /> <OperationList bladeburner={props.bladeburner} player={props.player} />
</>); </>);
} }

@ -19,10 +19,10 @@ export function Root(props: IProps): React.ReactElement {
<div style={{height: '100%', width:"30%", display:"inline-block", border:"1px solid white"}}> <div style={{height: '100%', width:"30%", display:"inline-block", border:"1px solid white"}}>
<Stats bladeburner={props.bladeburner} player={props.player} engine={props.engine} /> <Stats bladeburner={props.bladeburner} player={props.player} engine={props.engine} />
</div> </div>
<Console bladeburner={props.bladeburner} /> <Console bladeburner={props.bladeburner} player={props.player} />
</div> </div>
<div style={{width:"70%", display:"block", border:"1px solid white", marginTop:"6px", padding: "6px", position:"relative"}}> <div style={{width:"70%", display:"block", border:"1px solid white", marginTop:"6px", padding: "6px", position:"relative"}}>
<AllPages bladeburner={props.bladeburner} /> <AllPages bladeburner={props.bladeburner} player={props.player} />
</div> </div>
</div>); </div>);
} }

1
src/RedPill.d.ts vendored Normal file

@ -0,0 +1 @@
export declare function hackWorldDaemon(currentNodeNumber: number, flume: boolean = false, quick: boolean = false): void;