code:
window.LivingRule = Object.freeze({LUXURIOUS: 'luxurious', NORMAL: 'normal', SPARE: 'spare'});
window.Job = Object.freeze({
	DAIRY: 'work in the dairy', MILKMAID: 'be the Milkmaid', MASTER_SUITE: 'serve in the master suite', CONCUBINE: 'be your Concubine',
	BABY_FACTORY: 'labor in the production line', BROTHEL: 'work in the brothel', MADAM: 'be the Madam', ARCADE: 'be confined in the arcade',
	SERVANT: 'work as a servant', SERVER: 'be a servant', STEWARD: 'be the Stewardess', CLUB: 'serve in the club', DJ: 'be the DJ',
	JAIL: 'be confined in the cellblock', WARDEN: 'be the Wardeness', CLINIC: 'get treatment in the clinic', NURSE: 'be the Nurse',
	HGTOY: 'live with your Head Girl', SCHOOL: 'learn in the schoolroom', TEACHER: 'be the Schoolteacher', SPA: 'rest in the spa', ATTEND: 'be the Attendant',
	NANNY: 'work as a nanny', MATRON: 'be the Matron', FARMYARD: 'work as a farmhand', FARMER: 'be the Farmer', REST: 'rest'
	});
window.PersonalAttention = Object.freeze({TRADE: 'trading', WAR: 'warfare', SLAVING: 'slaving', ENGINEERING: 'engineering', MEDICINE: 'medicine', MAID: 'upkeep', HACKING: 'hacking'});

window.predictCost = function(array) {
	var array2 = array;
	var totalCosts = (
	getBrothelCosts() +
	getBrothelAdsCosts() +
	getArcadeCosts()  +
	getClubCosts() +
	getClubAdsCosts() +
	getDairyCosts() +
	getIncubatorCosts() +
	getServantsQuartersCosts() +
	getMasterSuiteCosts() +
	getNurseryCosts() +
	getFarmyardCosts() +
	getSecurityExpansionCost() +
	getLifestyleCosts() +
	getFSCosts() +
	getCitizenOrphanageCosts() +
	getPrivateOrphanageCosts() +
	getPeacekeeperCosts() +
	getMercenariesCosts() +
	getMenialRetirementCosts() +
	getRecruiterCosts() +
	getSchoolCosts() +
	getPolicyCosts() +
	getCyberModCosts() +
	getPCTrainingCosts() +
	getPCCosts() +
	predictTotalSlaveCosts(array2)
	);

	//these two apply a multiplicative effect to all costs so far.
	totalCosts = getEnvironmentCosts(totalCosts);
	totalCosts = getPCMultiplierCosts(totalCosts);

	//in the old order these were applied after multiplication.  Not sure if deliberate, but I'm leaving it for now.
	totalCosts += (
	getSFCosts() +
	getWeatherCosts()
	);
/*
	// clean up
	if(totalCosts > 0) {
		totalCosts = 0;
	} else {
		totalCosts = Math.ceil(totalCosts);
	}
*/
	return totalCosts;
};

window.getCost = function(array) {
	var array2 = array;
	var oldCash = State.variables.cash;
	var costSoFar = 0;
	cashX(forceNeg(getBrothelCosts()), "brothel");
	cashX(forceNeg(getBrothelAdsCosts()), "brothelAds");
	cashX(forceNeg(getArcadeCosts()), "arcade");
	cashX(forceNeg(getClubCosts()), "club");
	cashX(forceNeg(getClubAdsCosts()), "brothelAds");
	cashX(forceNeg(getDairyCosts()), "dairy");
	cashX(forceNeg(getIncubatorCosts()), "incubator");
	cashX(forceNeg(getServantsQuartersCosts()), "servantsQuarters");
	cashX(forceNeg(getMasterSuiteCosts()), "masterSuite");
	cashX(forceNeg(getNurseryCosts()), "nursery");
	cashX(forceNeg(getFarmyardCosts()), "farmyard");
	cashX(forceNeg(getSecurityExpansionCost()), "securityExpansion");
	cashX(forceNeg(getLifestyleCosts()), "personalLivingExpenses");
	cashX(forceNeg(getFSCosts()), "futureSocieties");
	cashX(forceNeg(getCitizenOrphanageCosts()), "citizenOrphanage");
	cashX(forceNeg(getPrivateOrphanageCosts()), "privateOrphanage");
	cashX(forceNeg(getPeacekeeperCosts()), "peacekeepers");
	cashX(forceNeg(getMercenariesCosts()), "mercenaries");
	cashX(forceNeg(getMenialRetirementCosts()), "menialRetirement");
	cashX(forceNeg(getRecruiterCosts()), "recruiter");
	cashX(forceNeg(getSchoolCosts()), "schoolBacking");
	cashX(forceNeg(getPolicyCosts()), "policies");
	cashX(forceNeg(getCyberModCosts()), "lab");
	cashX(forceNeg(getPCTrainingCosts()), "PCtraining");
	cashX(forceNeg(getPCCosts()), "PCmedical");
	getTotalSlaveCosts(array2);

	//these two apply a multiplicative effect to all costs so far.
	// Calculate what the deduced expenses would be, then subtract
	costSoFar = (oldCash - State.variables.cash); //How much we have spent by this point; expected to be positive.
	cashX(costSoFar - getEnvironmentCosts(costSoFar), "environment"); //getEnv takes total costs and makes it worse.  Figure out how much worse and record it

	costSoFar = (oldCash - State.variables.cash);
	cashX(costSoFar - getPCMultiplierCosts(costSoFar), "PCskills");

	//in the old order these were applied after multiplication.  Not sure if deliberate, but I'm leaving it for now.
	cashX(forceNeg(getSFCosts()), "specialForces");
	cashX(forceNeg(getWeatherCosts()), "weather");
	return (oldCash - State.variables.cash);
};

//slave expenses
window.predictTotalSlaveCosts = function(array3) {
	var loopCosts = 0;
	//slave expenses
	for (var slave of array3) {
		loopCosts += getSlaveCost(slave);
		loopCosts += getSlaveMinorCosts(slave);
	}
	return loopCosts;
};

window.getTotalSlaveCosts = function(array3) {
	var slaveCost = 0;
	var slaveCostMinor = 0;
	for (var slave of array3) {
		slaveCost = getSlaveCost(slave);
		cashX(forceNeg(slaveCost), "slaveUpkeep", slave);
		slaveCostMinor = getSlaveMinorCosts(slave);
		cashX(Math.abs(slaveCostMinor), "houseServant", slave);
	}
	//nothing to return, cashX already billed.
};

//facility expenses
window.getBrothelCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var brothel = State.variables.brothel;
	var costs = (brothel * facilityCost);
	costs += (0.1 * State.variables.brothelUpgradeDrugs * brothel * facilityCost);
	return costs;
};

window.getBrothelAdsCosts = function() {
	var brothel = State.variables.brothel;
	var costs = 0;
	if(brothel > 0) {
		costs += State.variables.brothelAdsSpending;
	}
	return costs;
};

window.getArcadeCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var arcade = State.variables.arcade;
	var costs = (arcade * facilityCost * 0.5);
	costs += (0.2 * State.variables.arcadeUpgradeInjectors * arcade * facilityCost)
	+ (0.2 * State.variables.arcadeUpgradeCollectors * arcade * facilityCost);
	return costs;
};

window.getClubCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var club = State.variables.club;
	var costs = (club * facilityCost);
	costs += (0.2 * State.variables.clubUpgradePDAs * club * facilityCost);
	if(club > 0) {
		costs += State.variables.clubAdsSpending;
	}
	return costs;
};

window.getClubAdsCosts = function() {
	var club = State.variables.club;
	var costs = 0;
	if(club > 0) {
		costs += State.variables.clubAdsSpending;
	}
	return costs;
};

window.getDairyCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var dairy = State.variables.dairy;
	var costs = (dairy * facilityCost) + (0.2 * State.variables.dairyFeedersUpgrade * dairy * facilityCost)
	+ (0.1 * State.variables.dairyPregUpgrade * dairy * facilityCost)
	+ (0.2 * State.variables.dairyStimulatorsUpgrade * facilityCost);
	if(dairy > 0) {
		costs += ((State.variables.bioreactorsXY + State.variables.bioreactorsXX + State.variables.bioreactorsHerm + State.variables.bioreactorsBarren) * 100);
	}
	return costs;
};

window.getIncubatorCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var incubator = State.variables.incubator;
	var costs = (State.variables.incubator * facilityCost * 10);
	costs += (0.2 * State.variables.incubatorUpgradeWeight * incubator * facilityCost)
	+ (0.2 * State.variables.incubatorUpgradeMuscles * incubator * facilityCost)
	+ (0.2 * State.variables.incubatorUpgradeReproduction * incubator * facilityCost)
	+ (0.2 * State.variables.incubatorUpgradeGrowthStims * incubator * facilityCost)
	+ (0.5 * State.variables.incubatorUpgradeSpeed * incubator * facilityCost);
	if(incubator > 0) {
		costs += ((State.variables.incubatorWeightSetting + State.variables.incubatorMusclesSetting + State.variables.incubatorReproductionSetting + State.variables.incubatorGrowthStimsSetting) * 500);
	}
	return costs;
};

window.getServantsQuartersCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var servantsQuarters = State.variables.servantsQuarters;
	var costs = (0.2 * State.variables.servantsQuartersUpgradeMonitoring * servantsQuarters * facilityCost);
	return costs;
};

window.getMasterSuiteCosts = function() {
	var costs = 0;
	if(State.variables.masterSuitePregnancySlaveLuxuries === 1) {
		costs += 500;
	}
	if(State.variables.masterSuitePregnancyFertilitySupplements === 1) {
		costs += 1000;
	}
	return costs;
};

window.getNurseryCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var nursery = State.variables.nursery;
	var costs = (nursery * facilityCost);
	return costs;
};

window.getFarmyardCosts = function() {
	var facilityCost = State.variables.facilityCost;
	var farmyard = State.variables.farmyard;
	var costs = (farmyard * facilityCost);
	return costs;
};

window.getSecurityExpansionCost = function() {
	//security expansion
	var secExpCost = 0;
	var soldierMod = 0;
	if(State.variables.secExp === 1) {
		if(State.variables.edictsUpkeep > 0) {
			secExpCost += State.variables.edictsUpkeep;
		}
		if(State.variables.SFSupportUpkeep > 0) {
			secExpCost += State.variables.SFSupportUpkeep;
		}
		if(State.variables.propHub > 0) {
			secExpCost += State.variables.propHubUpkeep;
		}
		if(State.variables.secHQ > 0) {
			secExpCost += State.variables.secHQUpkeep + 20 * State.variables.secMenials;
		}
		if(State.variables.secBarracks > 0) {
			secExpCost += State.variables.secBarracksUpkeep;
		}
		if(State.variables.riotCenter > 0) {
			secExpCost += State.variables.riotUpkeep;
		}
		if(State.variables.soldierWages === 0) {
			soldierMod = 1;
		}
		else if(State.variables.soldierWages === 1) {
			soldierMod = 1.5;
		}
		else {
			soldierMod = 2;
		}
		if (State.variables.militiaUnits != null) {
			for (var i = 0; i < State.variables.militiaUnits.length; i++) {
				if( !(State.variables.militiaUnits[i] === null) ){
					secExpCost += State.variables.militiaUnits[i].troops * State.variables.soldierUpkeep * soldierMod;
				}
			}
		}
		if (State.variables.slaveUnits != null) {
			for (var i = 0; i < State.variables.slaveUnits.length; i++) {
				if( !( State.variables.slaveUnits[i] === null) ){
					secExpCost += State.variables.slaveUnits[i].troops * State.variables.soldierUpkeep * 0.5 * soldierMod;
				}
			}
		}
		if (State.variables.mercUnits != null) {
			for (var i = 0; i < State.variables.mercUnits.length; i++) {
				if( !(State.variables.mercUnits[i] === null) ){
					secExpCost += State.variables.mercUnits[i].troops * State.variables.soldierUpkeep * 1.5 * soldierMod;
				}
			}
		}
	}
	return secExpCost;
};

	//general arcology costs

window.getLifestyleCosts = function() {
	var costs = 0;
	var localEcon = State.variables.localEcon;
	costs += (State.variables.girls * (250 + (50000 / localEcon)));
	return costs;
};

window.getFSCosts = function() {
	var costs = 0;
	costs += State.variables.FSSpending;
	if(State.variables.arcologies[0].FSRepopulationFocusLaw === 1 && State.variables.PC.pregKnown === 1) {
		costs -= 500;
	}
	return costs;
};

window.getCitizenOrphanageCosts = function() {
	var costs = 0;
	costs += State.variables.citizenOrphanageTotal * 100;
	return costs;
};

window.getPrivateOrphanageCosts = function() {
	var costs = 0;
	costs += State.variables.privateOrphanageTotal * 500;
	if(State.variables.breederOrphanageTotal > 0) {
		costs += 50;
	}
	return costs;
};

window.getPeacekeeperCosts = function() {
	var costs = 0;
	if(State.variables.peacekeepers !== 0 && State.variables.peacekeepers.undermining !== 0) {
		costs += State.variables.peacekeepers.undermining;
	}
	return costs;
};

window.getMercenariesCosts = function() {
	var costs = 0;
	var mercCosts = State.variables.mercenaries * 2000;
	if(State.variables.mercenaries > 0) {
		if(State.variables.barracks) {
			mercCosts *= 0.5;
		}
		if((State.variables.PC.warfare >= 100) || (State.variables.PC.career === 'arcology owner')) {
			mercCosts *= 0.5;
		}
		costs += mercCosts;
	}
	return costs;
};

window.getMenialRetirementCosts = function() {
	var costs = 0;
	if(State.variables.citizenRetirementMenials === 1) {
		costs += State.variables.menials * 2;
	}
	return costs;
};

// policy and other expenses
window.getRecruiterCosts = function() {
	var costs = 0;
	if(State.variables.Recruiter !== 0) {
		costs += 250;
	}
	return costs;
};

window.getSchoolCosts = function() {
	var costs = 0;
	if(State.variables.TSS.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.GRI.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.SCP.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.LDE.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.TGA.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.HA.schoolPresent === 1) {
		costs += 1000;
	}
	if(State.variables.TCR.schoolPresent === 1) {
		costs += 1000;
	}
	if((State.variables.TFS.schoolPresent === 1) && ((State.variables.PC.dick === 0) || (State.variables.PC.vagina === 0) || (State.variables.PC.boobs === 0))) {
		costs += 1000;
	}
	if(State.variables.TSS.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.GRI.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.SCP.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.LDE.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.TGA.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.HA.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.TCR.subsidize !== 0) {
		costs += 1000;
	}
	if(State.variables.TFS.subsidize !== 0) {
		costs += 1000;
	}
	return costs;
};

window.getPolicyCosts = function() {
	var costs = 0;
	var policyCost = State.variables.policyCost;
	if(State.variables.alwaysSubsidizeGrowth === 1) {
		costs += policyCost;
	}
	if(State.variables.alwaysSubsidizeRep === 1) {
		costs += policyCost;
	}
	if(State.variables.RegularParties === 1) {
		costs += policyCost;
	}

	if(State.variables.ProImmigrationCash === 1) {
		costs += policyCost;
	}
	if(State.variables.AntiEnslavementCash === 1) {
		costs += policyCost;
	}

	if(State.variables.CoursingAssociation === 1) {
		costs += 1000;
	}
	return costs;
};

window.getCyberModCosts = function() {
	var costs = 0;
	if(State.variables.cyberMod !== 0 && State.variables.researchLab.built === 'true') {
		costs += ((100 * State.variables.researchLab.maxSpace) + (300 * State.variables.researchLab.hired) + (100 * State.variables.researchLab.hired));
	}
	return costs;
};

//player expenses
window.getPCTrainingCosts = function() {
	var costs = 0;
	if(State.variables.PC.actualAge >= State.variables.IsInPrimePC && State.variables.PC.actualAge < State.variables.IsPastPrimePC) {
	if(State.variables.personalAttention === PersonalAttention.TRADE) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	} else if(State.variables.personalAttention === PersonalAttention.WAR) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	} else if(State.variables.personalAttention === PersonalAttention.SLAVING) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	} else if(State.variables.personalAttention === PersonalAttention.ENGINEERING) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	} else if(State.variables.personalAttention === PersonalAttention.MEDICINE) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	} else if(State.variables.personalAttention === PersonalAttention.HACKING) {
		costs += 10000*State.variables.AgeEffectOnTrainerPricingPC;
	}
	}
	return costs;
};
window.getPCCosts = function() {
	var costs = 0;
	if(State.variables.PC.preg === -1) {
		costs += 25;
	} else if(State.variables.PC.fertDrugs === 1) {
		costs += 50;
	} else if(State.variables.PC.preg >= 16) {
		costs += 100;
	}
	if(State.variables.PC.staminaPills === 1) {
		costs += 50;
	}
	return costs;
};

window.getPCMultiplierCosts = function(cost) {
	if(State.variables.PC.career === 'servant') {
		if(State.variables.personalAttention === PersonalAttention.MAID) {
			if(State.variables.PC.belly >= 5000) {
				cost *= 0.80;
			} else {
				cost *= 0.75;
			}
		} else {
			cost *= 0.9;
		}
	}

	return cost;
};

window.getEnvironmentCosts = function(cost) {
	if(State.variables.secExp === 1) {
		if(State.variables.terrain === 'oceanic' || State.variables.terrain === 'marine') {
			if(State.variables.docks > 0) {
				cost *= (1 - State.variables.docks * 0.05);
			}
		} else if(State.variables.railway > 0) {
			cost *= (1 - State.variables.railway * 0.05);
		}
	}
	return cost;
};

window.getSFCosts = function() {
	var costs = 0;
	if(State.variables.SF.Toggle && State.variables.SF.Active >= 1 && State.variables.SF.Subsidy > 0) {
		Count(); costs += Math.ceil(State.temporary.SFSubsidy);
	}
	return costs;
};

window.getWeatherCosts = function() {
	var costs = 0;
	if(State.variables.econWeatherDamage && State.variables.disasterResponse > 0) {
		costs += Math.trunc(State.variables.disasterResponse * 200000 / State.variables.localEcon);
	}
	if(State.variables.antiWeatherFreeze > 0) {
		costs += Math.trunc(State.variables.antiWeatherFreeze * 200000 / State.variables.localEcon);
	}
	return costs;
};

window.getSlaveMinorCosts = function(slave) {
	var costs = 0;
	var rulesCost = State.variables.rulesCost;
	if(slave.assignment === Job.SERVANT || slave.assignment === Job.SERVER) {
	if(slave.trust < -20) {
		costs -= rulesCost * 4;
	} else if(slave.devotion < -20) {
		if (slave.trust >= 20) {
			costs -= rulesCost / 2;
		} else {
			costs -= rulesCost * 2;
		}
	} else if(slave.devotion <= 20) {
		costs -= rulesCost * 3;
	} else if(slave.devotion <= 50) {
		costs -= rulesCost * 4;
	} else {
		costs -= rulesCost * 5;
	}
	if(slave.fetish === 'submissive') {
		costs -= rulesCost;
	} else if(slave.fetish === 'dom') {
		costs += rulesCost;
	}
	if(slave.relationship < -1) {
		costs -= rulesCost;
	}
	if(slave.energy < 20) {
		costs -= rulesCost;
	} else if(slave.energy < 40) {
		costs -= rulesCost / 2;
	}
	if(slave.lactation > 0) {
		costs -= 25;
	}
	if(slave.assignment === Job.SERVANT) {
		costs -= rulesCost;
	}
	if(setup.servantCareers.includes(slave.career) || slave.skillS >= State.variables.masteredXP) {
		costs -= rulesCost;
	}
	}
	return costs;
};

window.getSlaveCost = function(s) {
	if(!s) { return 0; }
	// Data duplicated from Cost Report
	var cost = 0;
	var rulesCost = State.variables.rulesCost;
	var foodCost = State.variables.foodCost;
	var drugsCost = State.variables.drugsCost;

	// Living expenses
	switch(s.assignment) {
	case Job.ARCADE:
		cost += rulesCost * 0.75;
		break;
	case Job.DAIRY:
		if(State.variables.dairyRestraintsSetting >= 2) {
			cost += rulesCost * 0.75;
		} else if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.5;
		} else if(State.variables.dairyDecoration === 'Degradationist') {
			cost += rulesCost * 0.90;
		} else {
			cost += rulesCost;
		}
		break;
	case Job.BROTHEL:
		if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.5;
		} else {
			cost += rulesCost;
		}
		break;
	case Job.SCHOOL: case Job.CLUB:
		cost += rulesCost * 1.5;
		break;
	case Job.CLINIC:
		if(s.livingRules === LivingRule.LUXURIOUS) {
			cost += rulesCost * 2;
		} else if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.5;
		} else {
			cost += rulesCost;
		}
		break;
	case Job.SPA: case Job.NANNY:
		if(s.livingRules === LivingRule.LUXURIOUS) {
			cost += rulesCost * 1.75;
		} else if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.5;
		} else {
			cost += rulesCost;
		}
		break;
	case Job.SERVANT:
		if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.5;
		} else {
			if(State.variables.servantsQuartersDecoration === 'Degradationist') {
				cost += rulesCost * 0.90;
			} else {
				cost += rulesCost;
			}
		}
		break;
	case Job.JAIL:
		if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 1.25;
		} else {
			cost += rulesCost * 0.90;
		}
		break;
	case Job.MADAM: case Job.DJ: case Job.NURSE: case Job.WARDEN:
	case Job.ATTEND: case Job.STEWARD: case Job.MILKMAID: case Job.TEACHER:
	case Job.MATRON:
		cost += rulesCost * 2;
		break;
	default:
		if(s.livingRules === LivingRule.LUXURIOUS) {
			cost += rulesCost * (s.relationship >= 4 ? 3 : 4);
		} else if(s.livingRules === LivingRule.NORMAL) {
			cost += rulesCost * 2;
		} else {
			cost += rulesCost;
		}
		break;
	}

	// Food
	cost += foodCost * 4;
	switch(s.diet) {
		case 'fattening': case 'muscle building':
			cost += foodCost;
			break;
		case 'restricted': case 'slimming':
			cost -= foodCost;
			break;
	}
	if(s.geneticQuirks.fertility === 2 && s.geneticQuirks.hyperFertility === 2 && s.preg === 0 && (s.ovaries === 1 || s.mpreg === 1)) {
		cost += foodCost * 0.5;
	}
	if(s.weight > 130) {
		cost += foodCost * 2;
	} else if(s.weight > 50) {
		cost += foodCost;
	} else if(s.weight < -50) {
		cost -= foodCost;
	}
	if(s.drugs === 'appetite suppressors') {
		cost -= foodCost;
	}
	if(s.lactation > 0) {
		cost += foodCost * s.lactation * (1 + Math.trunc(s.boobs/10000));
	}
	if(s.preg > s.pregData.normalBirth/8) {
		if(s.assignment === Job.DAIRY && State.variables.dairyFeedersSetting > 0) {
			// Extra feeding costs to support pregnancy are covered by dairy feeders.
			// TODO: Include them here anyway?
		} else if((s.assignment === Job.MASTER_SUITE || s.assignment === Job.CONCUBINE)
			&& State.variables.masterSuiteUpgradePregnancy === 1) {
			// Extra feeding costs to support pregnancy are covered by master suite luxuries.
			// TODO: Include them here anyway?
		} else {
			cost += foodCost * s.pregType * (s.pregControl === 'speed up' ? 3 : 1);
			if (s.pregType >= 100) {
				cost += foodCost * 5 * s.pregType * (s.pregControl === 'speed up' ? 3 : 1);
			} else if (s.pregType >= 50) {
				cost += foodCost * 3 * s.pregType * (s.pregControl === 'speed up' ? 3 : 1);
			} else if (s.pregType >= 30) {
				cost += foodCost * 2 * s.pregType * (s.pregControl === 'speed up' ? 3 : 1);
			} else if (s.pregType >= 10) {
				cost += foodCost * s.pregType * (s.pregControl === 'speed up' ? 3 : 1);
			}
		}
	}
	if(s.diet === 'XX' || s.diet === 'XY' || s.diet === 'fertility') {
		cost += 25;
	} else if(s.diet === 'cleansing') {
		cost += 50;
	} else if(s.diet === 'XXY') {
		cost += 75;
	}

	// Accessibility costs
	if(State.variables.boobAccessibility !== 1 && s.boobs > 20000
		&& (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 50;
	}
	if(State.variables.pregAccessibility !== 1
		&& (s.belly >= 60000) && s.assignment !== Job.BABY_FACTORY && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 100;
	}
	if(State.variables.dickAccessibility !== 1 && s.dick > 45 && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 50;
	}
	if(State.variables.ballsAccessibility !== 1 && s.balls > 90 && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 50;
	}
	if(State.variables.buttAccessibility !== 1 && s.butt > 15 && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 50;
	}
	if(!canSee(s) && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 50;
	} else if(s.eyes <= -1 && s.eyewear !== 'corrective glasses' && s.eyewear !== 'corrective contacts') {
		cost += 25;
	} else if(s.eyewear === 'blurring glasses' || s.eyewear === 'blurring contacts') {
		cost += 25;
	}
	if(!canHear(s) && (s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		cost += 40;
	} else if(s.hears <= -1 && s.earwear !== 'hearing aids') {
		cost += 15;
	} else if(s.earwear === 'muffling ear plugs') {
		cost += 15;
	}
	if((s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		if(s.amp !== 0) {
			if(s.amp === 1) {
				cost += rulesCost;
			} else {
				cost += rulesCost / 2;
			}
		} else if(!canWalk(s)) {
			cost += rulesCost;
		}
	}

	// Maintenance
	if(s.boobs > 10000 && s.boobsImplantType === 1) {
		cost += 50;
	}
	if(s.butt > 10 && s.buttImplantType === 1) {
		cost += 50;
	}
	if((s.assignment !== Job.DAIRY || State.variables.dairyRestraintsSetting < 2) && (s.assignment !== Job.ARCADE)) {
		if(s.preg > s.pregData.minLiveBirth && State.variables.universalRulesBirthing === 1) {
			cost += 50;
		}
	}

	// Retirement account
	if(State.variables.citizenRetirementMenials === 1 && State.variables.CitizenRetirement === 0) {
		cost += 2;
	}

	if(State.variables.CitizenRetirement === 1) {
		cost += 250;
	}

	// Enemas
	if(s.inflation === 3) {
		switch(s.inflationType) {
		case 'water':
			cost += 100;
			break;
		case 'food':
			cost += (foodCost * 4);
			break;
		case 'curative': case 'aphrodisiac': case 'tightener':
			cost += (100 + (drugsCost * 2));
			break;
		}
	} else if(s.inflation === 2) {
		switch(s.inflationType) {
		case 'water':
			cost += 50;
			break;
		case 'food':
			cost += (foodCost * 2);
			break;
		case 'curative': case 'aphrodisiac': case 'tightener':
			cost += (50 + (drugsCost * 2));
			break;
		}
	} else if(s.inflation === 1) {
		switch(s.inflationType) {
		case 'water':
			cost += 25;
			break;
		case 'food':
			cost += (foodCost);
			break;
		case 'curative': case 'aphrodisiac': case 'tightener':
			cost += (25 + (drugsCost * 2));
			break;
		}
	}

	// Drugs
	switch(s.drugs) {
	case 'anti-aging cream':
		cost += drugsCost * 10;
		break;
	case 'female hormone injections': case 'male hormone injections': case 'intensive breast injections':
	case 'intensive butt injections': case 'intensive penis enhancement': case 'intensive testicle enhancement':
	case 'intensive lip injections': case 'hyper breast injections': case 'hyper butt injections':
	case 'hyper penis enhancement': case 'hyper testicle enhancement': case 'hyper lip injections':
	case 'growth stimulants':
		cost += drugsCost * 5;
		break;
	case 'sag-B-gone':
		cost += Math.trunc(drugsCost * 0.1);
		break;
	case 'no drugs': case 'none':
		break;
	default:
		cost += drugsCost * 2;
		break;
	}
	if(s.curatives > 0 && s.assignmentVisible === 1) {
		cost += drugsCost * s.curatives;
	}
	if(s.aphrodisiacs !== 0) {
		cost += Math.trunc(drugsCost * Math.abs(s.aphrodisiacs));
	}
	if(s.hormones !== 0) {
		cost += Math.trunc((drugsCost * Math.abs(s.hormones) * 0.5));
	}
	if(s.bodySwap > 0) {
		cost += Math.trunc((drugsCost * s.bodySwap * 10));
	}
	if(s.preg === -1 && isFertile(s)) {
		cost += Math.trunc((drugsCost * 0.5));
	}

	// Promotion costs
	if(State.variables.studio === 1) {
		if(s.pornFameSpending > 0) {
			cost += (s.pornFameSpending/State.variables.PCSlutContacts);
		}
	}

	if(isNaN(cost)) {
		throw new Error('Cost calculation for slave ' + s.slaveName + ' (' + s.ID + ') failed.');
	}
	return cost;
};

// Supply and Demand for slaves (linear, simple)
// PC buying slaves reduces supply, selling slaves reduces demand.

window.menialSlaveCost = function(q) {
	if(!q) {
		q = 0;
	}
	var demand = State.variables.menialDemandFactor;
	var supply = State.variables.menialSupplyFactor;
	var baseCost = 1000;
	var random = State.variables.slaveCostRandom;
	return (Math.trunc(baseCost + demand / 400 - supply / 400 + q / 400) + random);
};

// Corporation Value

window.corpValue = function() {
	const V = State.variables;
	if(V.corpIncorporated === 0) {
		return 0;
	} else {
		var corpAssets = 0;
		if(V.corpDivExtra > 0) {
			corpAssets += V.corpDivExtraDev * 16000 + V.corpDivExtraSlaves * 10000;
		}
		if(V.corpDivLegal > 0) {
			corpAssets += V.corpDivLegalDev * 20000 + V.corpDivLegalSlaves * 15000;
		}
		if(V.corpDivBreak > 0) {
			corpAssets += V.corpDivBreakDev * 7200 + V.corpDivBreakSlaves * 10000 + V.corpDivBreakSlaves2 * 15000;
		}
		if(V.corpDivSurgery > 0) {
			corpAssets += V.corpDivSurgeryDev * 16000 + V.corpDivSurgerySlaves * 15000 + V.corpDivSurgerySlaves2 * 23000;
		}
		if(V.corpDivTrain > 0) {
			if(V.corpDivSurgery + V.corpDivTrain < 2 && V.corpDivTrainSurgerySwitch === 0) {
				corpAssets += V.corpDivTrainDev * 20000 + V.corpDivTrainSlaves * 15000 + V.corpDivTrainSlaves2 * 26000;
			} else if(V.corpDivTrainSurgerySwitch === 1 && V.corpDivTrainSurgeryTimer < 5) {
				corpAssets += V.corpDivTrainDev * 20000 + V.corpDivTrainSlaves * (15000 + 1600 * V.corpDivTrainSurgeryTimer) + V.corpDivTrainSlaves2 * (26000 + 1600 * V.corpDivTrainSurgeryTimer);
			} else {
				corpAssets += V.corpDivTrainDev * 20000 + V.corpDivTrainSlaves * 23000 + V.corpDivTrainSlaves2 * 34000;
			}
		}
		if(V.corpDivArcade > 0) {
			corpAssets += V.corpDivArcadeDev * 4000 + V.corpDivArcadeSlaves * 10000;
		}
		if(V.corpDivMenial > 0) {
			corpAssets += V.corpDivMenialDev * 5200 + V.corpDivMenialSlaves * 15000;
		}
		if(V.corpDivDairy > 0) {
			corpAssets += V.corpDivDairyDev * 12000 + V.corpDivDairySlaves * 23000;
		}
		if(V.corpDivWhore > 0) {
			if(V.corpDivSurgery + V.corpDivTrain < 2 && V.corpDivTrainSurgerySwitch === 0) {
				corpAssets += V.corpDivWhoreDev * 16000 + V.corpDivWhoreSlaves * 26000;
			} else if(V.corpDivTrainSurgerySwitch === 1 && V.corpDivTrainSurgeryTimer < 20) {
				corpAssets += V.corpDivWhoreDev * 16000 + V.corpDivWhoreSlaves * (26000 + 400 * V.corpDivTrainSurgeryTimer);
			} else {
				corpAssets += V.corpDivWhoreDev * 16000 + V.corpDivWhoreSlaves * 34000;
			}
		}
		return corpAssets + V.corpDividend + V.corpCash;
	}
};

// Corporation Share Price
// A positive q means adding shares to the market, negative means removing them

window.corpSharePrice = function(q) {
	const V = State.variables;
	if(V.corpIncorporated === 0) {
		return 0;
	} else {
		return Math.trunc(1000 * (corpValue() / (V.personalShares + V.publicShares + (q || 0))));
	}
};

// Corporation Division Slave room
// The amount of additional slaves you can fit in a division
// Can we condense this?

window.corpDivBreakSlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivBreak === 1 && V.corpDivBreakDev > V.corpDivBreakSlaves) {
		return V.corpDivBreakDev - V.corpDivBreakSlaves;
	} else {
		return 0;
	}
};

window.corpDivSurgerySlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivSurgery === 1 && V.corpDivSurgeryDev > V.corpDivSurgerySlaves) {
		return V.corpDivSurgeryDev - V.corpDivSurgerySlaves;
	} else {
		return 0;
	}
};

window.corpDivTrainSlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivTrain === 1 && V.corpDivTrainDev > V.corpDivTrainSlaves) {
		return V.corpDivTrainDev - V.corpDivTrainSlaves;
	} else {
		return 0;
	}
};

window.corpDivArcadeSlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivArcade === 1 && V.corpDivArcadeDev > V.corpDivArcadeSlaves) {
		return V.corpDivArcadeDev - V.corpDivArcadeSlaves;
	} else {
		return 0;
	}
};

window.corpDivMenialSlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivMenial === 1 && V.corpDivMenialDev > V.corpDivMenialSlaves) {
		return V.corpDivMenialDev - V.corpDivMenialSlaves;
	} else {
		return 0;
	}
};

window.corpDivDairySlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivDairy === 1 && V.corpDivDairyDev > V.corpDivDairySlaves) {
		return V.corpDivDairyDev - V.corpDivDairySlaves;
	} else {
		return 0;
	}
};

window.corpDivWhoreSlavesRoom = function() {
	const V = State.variables;
	if(V.corpDivWhore === 1 && V.corpDivWhoreDev > V.corpDivWhoreSlaves) {
		return V.corpDivWhoreDev - V.corpDivWhoreSlaves;
	} else {
		return 0;
	}
};

//Corporation race blacklisting/whitelisting
//race is the lowercase string representing the race, 'blacklist' is either 1 or 0. 1 means we are blacklisting and 0 means we are whitelisting said race
window.corpBlacklistRace = function(race, blacklist) {
	var raceArray = State.variables.corpSpecRaces;
	if(raceArray.length > 0 && blacklist === 1) {
		raceArray.delete(race);
	} else if(blacklist === 1) {
		raceArray = setup.filterRacesLowercase.filter(x => x !== race);
	} else {
		raceArray.push(race);
	}
	return raceArray;
};

window.getSlaveStatisticData = function(s, facility) {
	if(!s || !facility) {
		// Base data, even without facility
		return {
			ID: s.ID, slaveName: s.slaveName, customLabel: s.customLabel,
			income: 0, adsIncome: 0, rep: 0, cost: getSlaveCost(s),
			customers: 0 /* brothel, club, ... */
		};
	}

	if(!facility.income) {
		facility.income = new Map();
	}

	if(facility.income.has(s.ID)) {
		return facility.income.get(s.ID);
	} else {
		var data = {
			ID: s.ID, slaveName: s.slaveName, customLabel: s.customLabel,
			income: 0, adsIncome: 0, rep: 0, cost: getSlaveCost(s),
			customers: 0 /* brothel, club, ... */
		};
		facility.income.set(s.ID, data);
		return data;
	}
};

window.initFacilityStatistics = function(facility) {
	facility = facility || {};
	facility.adsIncome = 0;
	facility.maintenance = 0;
	facility.totalIncome = 0;
	facility.totalExpenses = 0;
	facility.profit = 0;
	facility.income = new Map();
	return facility;
};

/*

Welcome to the new way to spend and make money, all while having it recorded: cashX!  In the past, costs were directly deducted from $cash, with something like <<set $cash -= 100>>.

The new system will still happily spend your money, but it will also record it in the appropriate budget category and (optinally) the appropriate slave as well.

Let's say you were going to spend 100 on your favorite $activeSlave with cashX.  You might try:

<<run cashX(-100, "slaveMod", $activeSlave)>>

There we go!
1. -100 taken from your account
2. Recorded: -100 for the slaveMod category, to be displayed on the Budget screen
3. Recorded: -100 noted in your activeSlave's permanent record.  She better get busy paying that off!

cashX can be used in JS as well, and can be included in [[]] style links.

Make sure that expenses arrive in the COST slot as a negative, they are often positive in code.  Use the new function forceNeg or pass it along on a temporary variable if needed.

Costs don't have to be numbers either, you can use variables.  <<run cashX(forceNeg($contractCost), "slaveTransfer", $activeSlave)>>.  forceNeg makes sure that whatever value $contractCost has is negative, and will therefore be recorded as an expense.  You don't have to use it if you're sure the number you are passing along is negative.

A full list of categories (slaveMod, slaveTransfer, event) are in the widget "setupLastWeeksCash", currently found in costsWidgets.tw.  It's important to match your cost to one of those categories (or add a new one there, and display it in costsBudget.tw.)

The third category, the "slave slot" is completely optional.  Sometimes you just want to spend money by yourself.

*/
window.cashX = function(cost, what, who) {
	const V = State.variables;

	if (typeof cost !== "number") {
		V.lastWeeksErrors += `"${cost}" at ${what} is not of type "number",
		`;
	} else if (Number.isNaN(cost)) {
		V.lastWeeksErrors += `"${cost}" at ${what} is NaN,
		`;
	} else if (cost === -Infinity || cost === Infinity) {
		V.lastWeeksErrors += `"${cost}" at ${what} is infinity,
		`;
	} else {

		//Spend the money
		V.cash += cost;

		//INCOME
		if(cost > 0) {

				//record the action
				if (typeof V.lastWeeksCashIncome[what] !== 'undefined') {
					V.lastWeeksCashIncome[what] += cost;
				} else {
					V.lastWeeksErrors += `Unknown place "${what}" gained you ${cost},`;
				}

				//record the slave, if available
				if (typeof who !== 'undefined'){
						who.lastWeeksCashIncome += cost;
						who.lifetimeCashIncome += cost;
				}
		}

		//EXPENSES
		else if(cost < 0) {

			//record the action
			if (typeof V.lastWeeksCashExpenses[what] !== 'undefined') {
				V.lastWeeksCashExpenses[what] += cost;
			} else {
				V.lastWeeksErrors += `Unknown place "${what}" charged you ${cost},`;
			}

			//record the slave, if available
			if (typeof who !== 'undefined'){
				if (what === "slaveTransfer"){
					who.slaveCost = cost;
				} else {
					who.lifetimeCashExpenses += cost;
				}
			}
		}
		return cost;
	}
};

window.forceNeg = function(x) {
	return -Math.abs(x);
};

Number.prototype.toFixedHTML = function() {
	return commaNum(Number.prototype.toFixed.apply(this, arguments)).replace(/\.0+$/, '<span style="opacity: 0.3">$&</span>');
};

repo_name: Echoart/fc