From d70e0303265fa83b4f5ada778060357fed7e5bec Mon Sep 17 00:00:00 2001
From: Jones <Jones>
Date: Sat, 7 Dec 2019 15:50:11 +0100
Subject: [PATCH] Automatic whore distribution

---
 src/endWeek/saWhore.js                      | 19 ++++-
 src/js/economyJS.js                         | 81 ++++++++++++++-------
 src/uncategorized/slaveAssignmentsReport.tw | 10 +--
 3 files changed, 76 insertions(+), 34 deletions(-)

diff --git a/src/endWeek/saWhore.js b/src/endWeek/saWhore.js
index c94e2d1f96c..ec23532b7b8 100644
--- a/src/endWeek/saWhore.js
+++ b/src/endWeek/saWhore.js
@@ -245,7 +245,24 @@ window.saWhore = (function saWhore() {
 	 * @param {App.Entity.SlaveState} slave
 	 */
 	function usageCountDescriptions(slave) {
-		r += ` ${His} appearance attracted ${beauty} ${customers} members of the public (${Math.trunc(beauty / 7)} a day)`;
+		r += ` ${His} appearance`;
+		if (slave.maxWhoreClass > slave.effectiveWhoreClass) {
+			let customers2;
+			if (slave.maxWhoreClass === 4) {
+				customers2 = "extremely wealthy";
+			} else if (slave.maxWhoreClass === 3) {
+				customers2 = "upper class";
+			} else if (slave.maxWhoreClass === 2) {
+				customers2 = "middle class";
+			} else {
+				customers2 = "ERROR";
+			}
+			r += ` can attract the ${customers2}, but they already had plenty slaves to fuck so she `;
+		}
+		r += ` attracted ${beauty} ${customers} members of the public (${Math.trunc(beauty / 7)} a day)`;
+		if (slave.maxWhoreClass > slave.effectiveWhoreClass) {
+			` instead`;
+		}
 		if (beauty > 160) {
 			r += `, so many that `;
 			if (canDoVaginal(slave) && canDoAnal(slave)) {
diff --git a/src/js/economyJS.js b/src/js/economyJS.js
index c215a4f6789..4a707dceb4b 100644
--- a/src/js/economyJS.js
+++ b/src/js/economyJS.js
@@ -904,8 +904,8 @@ window.NPCSexSupply = function(lowerDemandLeft, lowerTotalDemand, middleDemandLe
 		} else {
 			NPCSexSupply.lowerClass = Math.trunc(NPCSexSupply.lowerClass * (1 + normalRandInt(0, 20) / 1000)); // Some random fluxuations whenever the NPC supply is roughly on target.
 		}
-	} else { // Increase NPC supply slightly if it drops below the standard minimum share of supply
-		NPCSexSupply.lowerClass = Math.trunc(NPCSexSupply.lowerClass * (1 + normalRandInt(30, 10) / 1000));
+	} else { // Increase NPC supply if it drops below the standard minimum share of supply
+		NPCSexSupply.lowerClass += Math.max(Math.trunc(NPCSexSupply.lowerClass * (normalRandInt(150, 10) / 1000)), 500);
 	}
 
 
@@ -922,7 +922,7 @@ window.NPCSexSupply = function(lowerDemandLeft, lowerTotalDemand, middleDemandLe
 			NPCSexSupply.middleClass = Math.trunc(NPCSexSupply.middleClass * (1 + normalRandInt(0, 20) / 1000));
 		}
 	} else {
-		NPCSexSupply.middleClass = Math.trunc(NPCSexSupply.middleClass * (1 + normalRandInt(30, 10) / 1000));
+		NPCSexSupply.middleClass += Math.max(Math.trunc(NPCSexSupply.middleClass * (normalRandInt(150, 10) / 1000)), 500);
 	}
 
 	// Upper class Calculations
@@ -938,7 +938,7 @@ window.NPCSexSupply = function(lowerDemandLeft, lowerTotalDemand, middleDemandLe
 			NPCSexSupply.upperClass = Math.trunc(NPCSexSupply.upperClass * (1 + normalRandInt(0, 20) / 1000));
 		}
 	} else {
-		NPCSexSupply.upperClass = Math.trunc(NPCSexSupply.upperClass * (1 + normalRandInt(30, 10) / 1000));
+		NPCSexSupply.upperClass += Math.max(Math.trunc(NPCSexSupply.upperClass * (normalRandInt(150, 10) / 1000)), 500);
 	}
 
 	// Top class calculations
@@ -954,14 +954,14 @@ window.NPCSexSupply = function(lowerDemandLeft, lowerTotalDemand, middleDemandLe
 			NPCSexSupply.topClass = Math.trunc(NPCSexSupply.topClass * (1 + normalRandInt(0, 20) / 1000));
 		}
 	} else {
-		NPCSexSupply.topClass = Math.trunc(NPCSexSupply.topClass * (1 + normalRandInt(30, 10) / 1000));
+		NPCSexSupply.topClass += Math.max(Math.trunc(NPCSexSupply.topClass * (normalRandInt(150, 10) / 1000)), 500);
 	}
 
 	return NPCSexSupply;
 };
 
 // The function for calculating and storing a slave's sexual interaction with citizens/'the outside'
-window.slaveJobValues = function() {
+window.slaveJobValues = function(lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef) {
 	const slaveJobValues = {
 		arcade: 0,
 		club: 0,
@@ -1198,6 +1198,11 @@ window.slaveJobValues = function() {
 		SJVClub(V.slaves[V.slaveIndices[ID]]);
 	});
 
+	// Saturation penalty for public servants. Even the most beautiful slaves lose some of their shine if they have too much competition.
+	if (slaveJobValues.club > 0) {
+		slaveJobValues.clubSP = (Math.pow(slaveJobValues.club / 1000, 0.95) * 1000) / slaveJobValues.club;
+	}
+
 	// Street whores adding to 'brothel'
 	V.JobIDArray["whore"].forEach(ID => {
 		SJVBrothel(V.slaves[V.slaveIndices[ID]]);
@@ -1205,7 +1210,7 @@ window.slaveJobValues = function() {
 
 	// Brothel whores adding to 'brothel'
 	V.BrothiIDs.forEach(ID => {
-		SJVBrothel(V.slaves[V.slaveIndices[ID]]);
+		SJVBrothel(V.slaves[V.slaveIndices[ID]], lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef);
 	});
 
 	function SJVClub(s) {
@@ -1400,7 +1405,7 @@ window.slaveJobValues = function() {
 		}
 	}
 
-	function SJVBrothel(s) {
+	function SJVBrothel(s, lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef) {
 		let toTheBrothel = 0;
 		let beautyMultiplier = 1;
 		s.minorInjury = 0;
@@ -1586,61 +1591,81 @@ window.slaveJobValues = function() {
 
 		// The whoreScore function finds the appropriate customer class and then calculates the whore income stats associated with that class and adds to the class supply.
 		// whoreClass is the MAXIMUM player set class the whore is allowed to service, if the whore is not eligable it will service the highest it is capable of servicing properly. A whoreClass of 0 means it is on auto (always service the highest possible class).
-		function whoreScore(s) {
+		function whoreScore(s, lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef) {
 			let income = s.sexAmount * s.sexQuality;
-			effectiveWhoreClass(s);
+			s.effectiveWhoreClass = effectiveWhoreClass(s);
+			s.maxWhoreClass = s.effectiveWhoreClass;
+
+			// Automatically changing effectiveWhoreClass
+			// what is the initial effective whore class? Are we providing more sex than overal demand? Is the ratio of supply/demand for this tier higher than the one below it?
+			// This also takes into consideration public sluts and ignores the NPC market and arcades
+			const topSDRatio = slaveJobValues.brothel.topClass / topClassSexDemandRef;
+			const upperSDRatio = slaveJobValues.brothel.upperClass / upperClassSexDemandRef;
+			const middleClubSupply = slaveJobValues.club * slaveJobValues.clubSP * (middleClassSexDemandRef / (lowerClassSexDemandRef + middleClassSexDemandRef));
+			const middleSupply = slaveJobValues.brothel.middleClass + middleClubSupply;
+			const middleSDRatio = middleSupply / middleClassSexDemandRef;
+			const lowerClubSupply = slaveJobValues.club * slaveJobValues.clubSP * (lowerClassSexDemandRef / (lowerClassSexDemandRef + middleClassSexDemandRef));
+			const lowerSupply = slaveJobValues.brothel.lowerClass + lowerClubSupply;
+			const lowerSDRatio = lowerSupply / lowerClassSexDemandRef;
+			if (s.effectiveWhoreClass === 4 && topSDRatio > 1 && topSDRatio > upperSDRatio) {
+				s.effectiveWhoreClass -= 1;
+			}
+			if (s.effectiveWhoreClass === 3 && upperSDRatio > 1 && upperSDRatio > middleSDRatio) {
+				s.effectiveWhoreClass -= 1;
+			}
+			if (s.effectiveWhoreClass === 2 && middleSDRatio > 1 && middleSDRatio > lowerSDRatio) {
+				s.effectiveWhoreClass -= 1;
+			}
 
 			// Calculate the stats
 			if (s.effectiveWhoreClass === 4) {
 				s.sexAmount = normalRandInt(50, 4); // Bringing sex amount into the desired range
 				s.sexQuality = Math.trunc(Math.min((income * 1.2) / s.sexAmount, V.whoreBudget.topClass * 0.2)); // Adjusting the price to the correct sex amount with 20% bonus for being of the highest tier
-				slaveJobValues.brothel.topClass += Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.topClass * 0.2); // Registering the job value in the right slot
+				slaveJobValues.brothel.topClass += Math.trunc(Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.topClass * 0.2)); // Registering the job value in the right slot
 			} else if (s.effectiveWhoreClass === 3) {
 				s.sexAmount = normalRandInt(60, 5);
 				s.sexQuality = Math.min(Math.trunc((income * 1.05) / s.sexAmount), V.whoreBudget.upperClass * 0.5); // The upper class will pay a maximum of 60% of their weekly budget per service
-				slaveJobValues.brothel.upperClass += Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.upperClass * 0.6);
+				slaveJobValues.brothel.upperClass += Math.trunc(Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.upperClass * 0.6));
 			} else if (s.effectiveWhoreClass === 2) {
 				s.sexAmount = normalRandInt(70, 6);
 				s.sexQuality = Math.min(Math.trunc((income * 0.9) / s.sexAmount), V.whoreBudget.middleClass); // The middle class will pay a maximum of 125% of their weekly budget per service
-				slaveJobValues.brothel.middleClass += Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.middleClass * 1.25);
+				slaveJobValues.brothel.middleClass += Math.trunc(Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.middleClass * 1.25));
 			} else {
 				s.sexAmount = normalRandInt(80, 7);
-				s.sexQuality = Math.clamp((income * 0.75) / s.sexAmount, 2, V.whoreBudget.lowerClass * 2); // The lower class will pay a maximum of 300% of their weekly budget per service and a minimum of 3
-				slaveJobValues.brothel.lowerClass += Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.lowerClass * 3);
+				s.sexQuality = Math.clamp((income * 0.75) / s.sexAmount, 2, V.whoreBudget.lowerClass * 2); // The lower class will pay a maximum of 300% of their weekly budget per service and a minimum of 2
+				slaveJobValues.brothel.lowerClass += Math.trunc(Math.min(s.sexAmount * s.sexQuality, s.sexAmount * V.whoreBudget.lowerClass * 3));
 			}
 		}
 
 		if (typeof s.whoreClass === 'undefined') {
 			s.whoreClass = 0;
 		}
-		whoreScore(s);
+		whoreScore(s, lowerClassSexDemandRef, middleClassSexDemandRef, upperClassSexDemandRef, topClassSexDemandRef);
 	}
 
-	// Saturation penalty for public servants. Even the most beautiful slaves lose some of their shine if they have too much competition.
-	if (slaveJobValues.club > 0) {
-		slaveJobValues.clubSP = (Math.pow(slaveJobValues.club / 1000, 0.95) * 1000) / slaveJobValues.club;
-	}
 	return slaveJobValues;
 };
 
 window.effectiveWhoreClass = function(s) {
 	let score = s.sexAmount * s.sexQuality;
+	let result;
 	if (typeof s.whoreClass === 'undefined' || s.whoreClass === 0) {
-		s.effectiveWhoreClass = 4;
+		result = 4;
 	} else {
-		s.effectiveWhoreClass = s.whoreClass;
+		result = s.whoreClass;
 	}
 	// Find maximum eligable class
 	// these could be refined further if needed.
-	if (s.effectiveWhoreClass === 4 && !(score > 5000 && s.skill.whoring > 80 && s.skill.entertainment > 50)) {
-		s.effectiveWhoreClass -= 1;
+	if (result === 4 && !(score > 5000 && s.skill.whoring > 80 && s.skill.entertainment > 50)) {
+		result -= 1;
 	}
-	if (s.effectiveWhoreClass === 3 && !(score > 2500 && s.skill.whoring > 50)) {
-		s.effectiveWhoreClass -= 1;
+	if (result === 3 && !(score > 2500 && s.skill.whoring > 50)) {
+		result -= 1;
 	}
-	if (s.effectiveWhoreClass === 2 && (score <= 1000)) {
-		s.effectiveWhoreClass -= 1;
+	if (result === 2 && (score <= 1000)) {
+		result -= 1;
 	}
+	return result;
 };
 
 window.getSlaveStatisticData = function(s, facility) {
diff --git a/src/uncategorized/slaveAssignmentsReport.tw b/src/uncategorized/slaveAssignmentsReport.tw
index fa248c9daef..b8ec2375276 100644
--- a/src/uncategorized/slaveAssignmentsReport.tw
+++ b/src/uncategorized/slaveAssignmentsReport.tw
@@ -31,14 +31,14 @@ _topClassSexDemandRef = Math.max(_topClassSexDemand, 1),
 $whorePriceAdjustment = {lowerClass: 0, middleClass: 0, upperClass: 0, topClass: 0},
 $NPCMarketShare = {lowerClass: 0, middleClass: 0, upperClass: 0, topClass: 0},
 $sexDemandResult = {lowerClass: 0, middleClass: 0, upperClass: 0, topClass: 0},
-$slaveJobValues = slaveJobValues()>>
+$slaveJobValues = slaveJobValues(_lowerClassSexDemandRef, _middleClassSexDemandRef, _upperClassSexDemandRef, _topClassSexDemandRef)>>
 
 /*Supply of sexual services*/
 <<if ($cheatMode == 1) || ($debugMode == 1)>>
-	<br>Lower Class SD: <<print _lowerClassSexDemand>>
-	<br>Middle Class SD: <<print _middleClassSexDemand>>
-	<br>Upper Class SD: <<print _upperClassSexDemand>>
-	<br>Top Class SD: <<print _topClassSexDemand>>
+	<br>Lower Class SD: <<print _lowerClassSexDemandRef>>
+	<br>Middle Class SD: <<print _middleClassSexDemandRef>>
+	<br>Upper Class SD: <<print _upperClassSexDemandRef>>
+	<br>Top Class SD: <<print _topClassSexDemandRef>>
 	<br>Club SP: <<print Math.trunc($slaveJobValues.club * $slaveJobValues.clubSP)>>
 	<br>Arcade SP: <<print $slaveJobValues.arcade>>
 	<br>Brothel SP: <<print $slaveJobValues.brothel.lowerClass>>, <<print $slaveJobValues.brothel.middleClass>>, <<print $slaveJobValues.brothel.upperClass>>, <<print $slaveJobValues.brothel.topClass>>
-- 
GitLab