diff --git a/src/endWeek/saPorn.js b/src/endWeek/saPorn.js
index 93ab5aa36c52c40b03030d04339859f23b79298d..d988e7048009e725cb659d8fc453f0dd5f492e95 100644
--- a/src/endWeek/saPorn.js
+++ b/src/endWeek/saPorn.js
@@ -1,116 +1,393 @@
-/* to later be rolled into saPorn */
+window.saPorn = (function saPorn() {
+	"use strict";
 
-/**
- * @param {App.Entity.SlaveState} slave
- * @returns {object}
- */
-window.getHighestPorn = function(slave) {
-	let max = {value: 0, type: "none"};
+	let r;
+	let he, him, his, hers, himself, girl, loli, He, His;
+	let decayRate;
+	let viewership;
 
-	if (slave.porn.fame.general > max.value) {
-		max = {value: slave.porn.fame.general, type: "generic"};
-	}
-	if (slave.porn.fame.fuckdoll > max.value) {
-		max = {value: slave.porn.fame.fuckdoll, type: "fuckdoll"};
-	}
-	if (slave.porn.fame.rape > max.value) {
-		max = {value: slave.porn.fame.rape, type: "rape"};
-	}
-	if (slave.porn.fame.preggo > max.value) {
-		max = {value: slave.porn.fame.preggo, type: "preggo"};
-	}
-	if (slave.porn.fame.BBW > max.value) {
-		max = {value: slave.porn.fame.BBW, type: "BBW"};
-	}
-	if (slave.porn.fame.gainer > max.value) {
-		max = {value: slave.porn.fame.gainer, type: "weight gain"};
-	}
-	if (slave.porn.fame.stud > max.value) {
-		max = {value: slave.porn.fame.stud, type: "big dick"};
-	}
-	if (slave.porn.fame.loli > max.value) {
-		max = {value: slave.porn.fame.loli, type: "underage"};
-	}
-	if (slave.porn.fame.deepThroat > max.value) {
-		max = {value: slave.porn.fame.deepThroat, type: "deepthroat"};
-	}
-	if (slave.porn.fame.struggleFuck > max.value) {
-		max = {value: slave.porn.fame.struggleFuck, type: "unwilling"};
-	}
-	if (slave.porn.fame.painal > max.value) {
-		max = {value: slave.porn.fame.painal, type: "hardcore anal"};
-	}
-	if (slave.porn.fame.tease > max.value) {
-		max = {value: slave.porn.fame.tease, type: "softcore"};
-	}
-	if (slave.porn.fame.romantic > max.value) {
-		max = {value: slave.porn.fame.romantic, type: "romantic"};
-	}
-	if (slave.porn.fame.pervert > max.value) {
-		max = {value: slave.porn.fame.pervert, type: "really perverted"};
-	}
-	if (slave.porn.fame.caring > max.value) {
-		max = {value: slave.porn.fame.caring, type: "voyeur"};
-	}
-	if (slave.porn.fame.unflinching > max.value) {
-		max = {value: slave.porn.fame.unflinching, type: "unspeakable"};
-	}
-	if (slave.porn.fame.sizeQueen > max.value) {
-		max = {value: slave.porn.fame.sizeQueen, type: "huge insertion"};
-	}
-	if (slave.porn.fame.neglectful > max.value) {
-		max = {value: slave.porn.fame.neglectful, type: "orgasm denial"};
-	}
-	if (slave.porn.fame.cumAddict > max.value) {
-		max = {value: slave.porn.fame.cumAddict, type: "cum addiction"};
-	}
-	if (slave.porn.fame.analAddict > max.value) {
-		max = {value: slave.porn.fame.analAddict, type: "anal addiction"};
-	}
-	if (slave.porn.fame.attentionWhore > max.value) {
-		max = {value: slave.porn.fame.attentionWhore, type: "exhibition"};
-	}
-	if (slave.porn.fame.breastGrowth > max.value) {
-		max = {value: slave.porn.fame.breastGrowth, type: "breast expansion"};
-	}
-	if (slave.porn.fame.abusive > max.value) {
-		max = {value: slave.porn.fame.abusive, type: "abuse"};
-	}
-	if (slave.porn.fame.malicious > max.value) {
-		max = {value: slave.porn.fame.malicious, type: "sexual torture"};
+	return saPorn;
+
+	function saPorn(slave) {
+		({
+			he, him, his, hers, himself, girl, He, His, loli
+		} = getPronouns(slave));
+
+		let oldFame = slave.porn.viewerCount;
+
+		if (V.studio === 1 && slave.porn.feed === 1) {
+			calcBaseViewership(slave);
+
+			r += `The studio regularly releases clips of ${his} daily affairs. `;
+			if (V.cheatMode === 1) {
+				r += `(Decay: ${decayRate} Viewership: ${viewership} Last week's fame: ${oldFame}) `;
+			}
+
+			prestigeCommentary(slave);
+			faceCommentary(slave);
+			hack();
+
+			genreViews(slave);
+			updateViewerCount(slave);
+			if (oldFame > slave.porn.viewerCount) {
+				r += `Overall, ${his} online fame <span class="red">dropped</span> this week. `;
+			} else if (oldFame < slave.porn.viewerCount) {
+				r += `Overall, ${his} online fame <span class="green">rose</span> this week. `;
+				if (oldFame < 100 && slave.porn.viewerCount >= 100 && V.studioFeed === 1) {
+					r += `${He} <span class="yellow">has accrued enough views to determine prospective porn genres.</span> `;
+				}
+			} else if (slave.porn.viewerCount !== 0) {
+				r += `Surprisingly, ${his} online fame <span class="yellow">remained consistent</span> this week despite how fickle watchers can be. `;
+			} else {
+				r += `${He} went <span class="red">completely overlooked</span> this week and failed to gain any hits at all. `;
+			}
+
+			if (slave.porn.viewerCount > 0) {
+				let donations = Math.floor(slave.porn.viewerCount / jsRandom(10, 15 + viewerSoaking));
+				if (donations > 0) {
+					r += `Fans donated a total of <span class="cash inc">${cashFormat(donations)}</span> to ${his} account this week. `;
+					cashX(donations, "porn", slave);
+				}
+			}
+			if (slave.porn.spending > 0) {
+				cashX(forceNeg(slave.porn.spending / V.PCSlutContacts), "porn", slave);
+			}
+
+			prestigeGen(slave);
+		} else { /* popularity decay from lack of new content */
+			if (slave.porn.prestige > 1) { // 500k
+				decayRate = 5000;
+			} else if (slave.porn.prestige > 0) { // 10k
+				decayRate = 500;
+			} else {
+				decayRate = 30;
+			}
+
+			genreDecay(slave);
+			updateViewerCount(slave);
+
+			prestigeDecay(slave);
+		}
+
+		return r;
 	}
-	if (slave.porn.fame.selfHating > max.value) {
-		max = {value: slave.porn.fame.selfHating, type: "self hating"};
+
+	function genreDecay(slave) {
+		for (let genre of App.Porn.getAllGenres()) {
+			if (slave.porn.fame[genre.fameName] > 0) {
+				const oldPorn = slave.porn.fame[genre.fameName];
+				slave.porn.fame[genre.fameName] = Math.clamp(slave.porn.fame[genre.fameName] - (decayRate * 2), 0, 100000);
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameName]);
+			}
+		}
 	}
-	if (slave.porn.fame.breeder > max.value) {
-		max = {value: slave.porn.fame.breeder, type: "breeder"};
+
+	function updateViewerCount(slave) {
+		slave.porn.viewerCount = Math.trunc(App.Porn.getAllGenres().reduce((acc, cur) => acc + slave.porn.fame[cur.fameVar]));
 	}
-	if (slave.porn.fame.sub > max.value) {
-		max = {value: slave.porn.fame.sub, type: "submissive"};
+
+	function prestigeDecay(slave) {
+		if (slave.porn.prestige > 0) {
+			const genre = App.Porn.getGenreByFameName(slave.porn.fameType);
+			if (slave.porn.fame[genre.fameName] < 40000 && slave.porn.prestige === 2) {
+				slave.porn.prestige = 1;
+				slave.porn.prestigeDesc = `$He has a following in slave pornography. ${genre.prestigeDesc1}.`;
+				r +=`With the lack of any new content, <span class="red">${his} popularity in ${slave.porn.fameType} pornography has dropped considerably,</span> though some viewers still cling to the hope that ${he}'ll come back. `;
+			} else if (slave.porn.fame[genre.fameName] < 5000) {
+				slave.porn.prestige = 0;
+				slave.porn.prestigeDesc = 0;
+				slave.porn.fameType = "none";
+				r += `With no new ${slave.porn.fameType} content coming out, <span class="red">${his} popularity has faded away.</span> `;
+			}
+		}
 	}
-	if (slave.porn.fame.cumSlut > max.value) {
-		max = {value: slave.porn.fame.cumSlut, type: "cum"};
+
+	function calcBaseViewership(slave) {
+		let face;
+
+		if (slave.porn.prestige > 1) {
+			decayRate = 5000;
+			if (slave.fuckdoll > 0) {
+				face = 50;
+			} else if (slave.collar === "porcelain mask") {
+				face = 20;
+			} else {
+				face = slave.face * 4;
+			}
+			viewership = ((500/V.HackingSkillMultiplier)+(slave.porn.spending)+(face)+(slave.prestige*10)-(decayRate));
+		} else if (slave.porn.prestige > 0) {
+			decayRate = 500;
+			if (slave.fuckdoll > 0) {
+				face = 20;
+			} else if (slave.collar === "porcelain mask") {
+				face = 50;
+			} else {
+				face = slave.face * 2;
+			}
+			viewership = ((900/V.HackingSkillMultiplier)+(slave.porn.spending/15)+(face)+(slave.prestige*250)-(decayRate));
+		} else {
+			decayRate = 30;
+			if (slave.fuckdoll > 0) {
+				face = 0;
+			} else if (slave.collar === "porcelain mask") {
+				face = 0;
+			} else {
+				face = slave.face / 20;
+			}
+			viewership = ((300/V.HackingSkillMultiplier)+(slave.porn.spending > 0 ? 1.01*slave.porn.spending : 200)+(face)+(slave.prestige*20)-(decayRate));
+		}
+		viewership = Math.trunc(viewership);
 	}
-	if (slave.porn.fame.anal > max.value) {
-		max = {value: slave.porn.fame.anal, type: "buttslut"};
+
+	function prestigeCommentary(slave) {
+		if (slave.porn.prestige > 1) {
+			r += `${He} is widely regarded in ${slave.porn.fameType} porn, but with so many watchers, turn over is high. `;
+		} else if (slave.porn.prestige > 0) {
+			if (slave.porn.fameType === "generic") {
+				r += `${He} has claimed a niche in slave porn, so there is a constant cycle of new arrivals and bored ex-watchers. `;
+			} else {
+				r += `${He} has claimed a niche for ${himself} in ${slave.porn.fameType} porn, so there is a constant cycle of new arrivals and bored ex-watchers. `;
+			}
+		} else {
+			r += `${He} is borderline unknown, so $he tends to hold onto the few watchers ${he} has. `;
+		}
+
+		if (slave.prestige > 1) {
+			r += `Interest in porn of ${him} is very high, since ${he}'s already quite prestigious. `;
+		} else if (slave.prestige > 0) {
+			r += `Interest in porn of ${him} is high, since ${he}'s already prestigious. `;
+		}
 	}
-	if (slave.porn.fame.humiliation > max.value) {
-		max = {value: slave.porn.fame.humiliation, type: "humiliating"};
+
+	function faceCommentary(slave) {
+		if (slave.fuckdoll > 0) {
+			return;
+		} else if (slave.collar === "porcelain mask") {
+			r += `${His} mask adds an air of mystery to $him, `;
+			if (slave.porn.prestige > 1) {
+				r += `but people wonder if $he is trying to hide something. `;
+			} else if (slave.porn.prestige > 0) {
+				r += `tantalizing viewers with what it hides. `;
+			} else {
+				r += `but viewers are more interested in ${his} body. `;
+			}
+		} else if (slave.face > 95) {
+			r += `${His} unbelievably beautiful face seizes the attention of all who see it. `;
+		} else if (slave.face > 40) {
+			r += `${His} gorgeous face draws viewers in and keeps them watching. `;
+		} else if (slave.face > 10) {
+			r += `${His} pretty face draws viewers in. `;
+		} else if (slave.face > 0) {
+			r += `${His} face is attractive, but it attracts few viewers. `;
+		} else if (slave.face === 0) {
+			r += `${His} face is completely average and makes no difference to ${his} viewers. `;
+		} else if (slave.face >= -10) {
+			r += `${His} face, while attractive, is so average it turns off some viewers. `;
+		} else if (slave.face >= -40) {
+			r += `${His} unattractive face causes viewers to move on. `;
+		} else if (slave.face >= -95) {
+			r += `${His} ugly face drives viewers off. `;
+		} else {
+			r += `${His} face chases off all but the most desperate viewers. `;
+		}
 	}
-	if (slave.porn.fame.boobs > max.value) {
-		max = {value: slave.porn.fame.boobs, type: "breast"};
+
+	function hack() {
+		if (V.PC.skill.hacking > 10) {
+			r += `With your hacking skills, you manage to tweak search algorithms to display ${his} content more often. `;
+		} else if (V.PC.skill.hacking < 0) {
+			r += `With your lack of skill with computers you manage to misidentify ${his} content, complicating searches. `;
+			IncreasePCSkills('hacking', 0.1);
+		}
+		IncreasePCSkills('hacking', 0.1);
 	}
-	if (slave.porn.fame.dom > max.value) {
-		max = {value: slave.porn.fame.dom, type: "dominant"};
+
+	function viewershipDelta(newPorn, oldPorn) {
+		if (newPorn > oldPorn) {
+			return `Viewership <span class="green">increased</span> this week. `;
+		} else if (newPorn < oldPorn) {
+			return `Viewership <span class="red">decreased</span> this week. `;
+		} else {
+			return `Viewership <span class="yellow">was stable</span> this week. `;
+		}
 	}
-	if (slave.porn.fame.sadist > max.value) {
-		max = {value: slave.porn.fame.sadist, type: "sadistic"};
+
+	function cheatDelta(name, oldPorn, newPorn) {
+		if (V.cheatMode === 1) {
+			return `(${name}: ${oldPorn} to ${newPorn}). `;
+		}
+		return ``;
 	}
-	if (slave.porn.fame.masochist > max.value) {
-		max = {value: slave.porn.fame.masochist, type: "masochistic"};
+
+	function genreViews(slave) {
+		let viewerSoaking = 1;
+		let adjustedViewership = viewership;
+
+		/* Paraphilias have the highest take of viewers */
+		for (let genre of App.Porn.getGenresByType('paraphilia')) {
+			const oldPorn = slave.porn.fame[genre.fameVar];
+			if (genre.valid(slave)) {
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					adjustedViewership = viewership * 1.5;
+				} else if (slave.porn.focus !== "none") {
+					adjustedViewership = viewership * 0.5;
+				}
+				slave.porn.fame[genre.fameVar] += adjustedViewership + slave.fetishStrength*2 - ((decayRate/10)*(V.pornStars[genre.fameVar].p1count-1));
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar], 0, 150000);
+				viewerSoaking++;
+
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					r += `${hitText} `;
+					r += viewershipDelta(slave.porn.fame[genre.fameVar], oldPorn);
+				}
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			} else if (slave.porn.fame[genre.fameVar] > 0) {
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar] - decayRate*2, 0, 150000);
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			}
+		}
+
+		/* Fetishes */
+		for (let genre of App.Porn.getGenresByType('fetish')) {
+			const oldPorn = slave.porn.fame[genre.fameVar];
+			if (genre.valid(slave)) {
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					adjustedViewership = viewership * 2.0;
+				} else if (slave.porn.focus !== "none") {
+					adjustedViewership = viewership * 0.5;
+				}
+				slave.porn.fame[genre.fameVar] += adjustedViewership/viewerSoaking + slave.fetishStrength - ((decayRate/10)*(V.pornStars[genre.fameVar].p1count-1));
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar], 0, 150000);
+				viewerSoaking++;
+
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					r += `${hitText} `;
+					r += viewershipDelta(slave.porn.fame[genre.fameVar], oldPorn);
+				}
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			} else if (slave.porn.fame[genre.fameVar] > 0) {
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar] - decayRate*2, 0, 150000);
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			}
+		}
+
+		/* General */
+		for (let genre of App.Porn.getGenresByType('general')) {
+			const oldPorn = slave.porn.fame[genre.fameVar];
+			if (genre.valid(slave)) {
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					adjustedViewership = viewership * 4.0;
+				} else if (slave.porn.focus !== "none") {
+					adjustedViewership = viewership * 0.5;
+				}
+				slave.porn.fame[genre.fameVar] += adjustedViewership/viewerSoaking - ((decayRate/10)*(V.pornStars[genre.fameVar].p1count-1));
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar], 0, 150000);
+				viewerSoaking++;
+
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					r += `${hitText} `;
+					r += viewershipDelta(slave.porn.fame[genre.fameVar], oldPorn);
+				}
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			} else if (slave.porn.fame[genre.fameVar] > 0) {
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar] - decayRate*2, 0, 150000);
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			}
+		}
+
+		/* Quirks are low and unlikely, requiring focus to push into the limelight */
+		for (let genre of App.Porn.getGenresByType('quirk')) {
+			const oldPorn = slave.porn.fame[genre.fameVar];
+			if (genre.valid(slave)) {
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					adjustedViewership = viewership * 6.0;
+				} else if (slave.porn.focus !== "none") {
+					adjustedViewership = viewership * 0.5;
+				}
+				slave.porn.fame[genre.fameVar] += adjustedViewership/viewerSoaking - ((decayRate/10)*(V.pornStars[genre.fameVar].p1count-1));
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar], 0, 150000);
+
+				if (slave.porn.focus === genre.focusName || slave.porn.fameType === genre.fameName) {
+					r += `${hitText} `;
+					r += viewershipDelta(slave.porn.fame[genre.fameVar], oldPorn);
+				}
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			} else if (slave.porn.fame[genre.fameVar] > 0) {
+				slave.porn.fame[genre.fameVar] = Math.clamp(slave.porn.fame[genre.fameVar] - decayRate*2, 0, 150000);
+				r += cheatDelta(genre.uiName(), oldPorn, slave.porn.fame[genre.fameVar]);
+			}
+		}
 	}
-	if (slave.porn.fame.pregnancy > max.value) {
-		max = {value: slave.porn.fame.pregnancy, type: "pregnancy fetish"};
+
+	function prestigeGen(slave) {
+		const highestPorn = getHighestPorn(slave);
+		if (slave.porn.prestige === 0 && slave.porn.viewerCount >= 100000) {
+			const pornFameGrabBag = App.Porn.getAllGenres().filter((g) => slave.porn.fame[g.fameVar] >= 10000);
+			if (pornFameGrabBag.length > 0) {
+				const genre = pornFameGrabBag.pluck();
+				slave.porn.fameType = genre.fameName;
+				slave.porn.prestige = 1;
+
+				r += `<span style="green">${He} has gained a following in ${slave.porn.fameType} pornography!</span> ${genre.prestigeDesc1}, but he isn't famous enough to be called presigious yet. `;
+				slave.porn.prestigeDesc = `$He has a following in slave pornography. ${genre.prestigeDesc1}.`;
+				if (genre.type === "fetish" && slave.fetishKnown !== 1) {
+					slave.fetishKnown = 1;
+				}
+			}
+		} else if (slave.porn.prestige === 1) {
+			const swapPoint = 1.2;
+			const genre = App.Porn.getGenreByFameName(slave.porn.fameType);
+			if (slave.porn.fame[genre.fameVar] >= 50000) {
+				slave.porn.prestige = 2;
+				slave.porn.prestigeDesc = `$He is well known from $his career in slave pornography. ${genre.prestigeDesc2}.`;
+				r += `<span class="green">${He} has gained a hold in ${slave.porn.fameType} pornography!</span> ${genre.prestigeDesc2}, so it is now prestigious to own ${him}. `;
+			} else if (highestPorn.value >= slave.porn.fame[genre.fameVar] * swapPoint) {
+				r += `${His} fame in ${slave.porn.fameType} pornography has been overwhelmed by $his surging popularity in other aspects. <span class="yellow">${He} is now better known for ${his} ${highestPorn.type} porn.</span> `;
+				const newGenre = App.Porn.getGenreByFameName(highestPorn.type);
+				slave.porn.fameType = newGenre.fameName;
+				slave.porn.prestigeDesc = `$He has a following in slave pornography. ${newGenre.prestigeDesc1}.`;
+				if (newGenre.type === "fetish" && slave.fetishKnown !== 1) {
+					slave.fetishKnown = 1;
+				}
+			} else if (slave.porn.fame[genre.fameVar] < 5000) {
+				slave.porn.prestige = 0;
+				slave.porn.prestigeDesc = 0;
+				r += `<span class="red">${His} popularity in ${slave.porn.fameType} pornography has faded.</span> ${He} is once again relatively unknown. `;
+				slave.porn.fameType = "none";
+			}
+		} else if (slave.porn.prestige === 2) {
+			const genre = App.Porn.getGenreByFameName(slave.porn.fameType);
+			if (slave.porn.fame[genre.fameVar] >= 150000 && V.pornStars[genre.fameVar].p3ID === 0) {
+				slave.porn.prestige = 3;
+				slave.porn.fame[genre.fameVar] = 250000;
+				slave.porn.viewerCount = 250000;
+				V.pornStars[genre.fameVar].p3ID = slave.ID;
+				slave.porn.prestigeDesc = `$He is world famous for $his career in slave pornography. ${genre.prestigeDesc3}.`;
+				r += `<span class="green">${He} has become world famous for ${his} career in ${slave.porn.fameType} pornography!</span> ${genre.prestigeDesc3}, so it is now extremely presitigious to own ${him}. `;
+				V.trinkets.push(`a framed shot from porn starring ${slave.slaveName} ${genre.trinketShotDesc}`);
+
+				r += `Further paid publicity cannot increase ${his} fame, so subsidy of porn featuring ${him} has stopped. `;
+				slave.porn.spending = 0;
+			} else if (slave.porn.fame[genre.fameVar] < 40000) {
+				slave.porn.prestige = 1;
+				slave.porn.prestigeDesc = `$He has a following in slave pornography. ${genre.prestigeDesc1}.`;
+				r += `<span class="red">${His} popularity in ${slave.porn.fameType} pornography has dropped considerably,</span> though ${he} still retains a core fanbase. `;
+			}
+		}
+	}
+})();
+
+/**
+ * @param {App.Entity.SlaveState} slave
+ * @returns {object}
+ */
+window.getHighestPorn = function(slave) {
+	let max = {value: 0, type: "none"};
+
+	for (const genre of App.Porn.getAllGenres()) {
+		if (slave.porn.fame[genre.fameVar] > max.value) {
+			max = {value: slave.porn.fame[genre.fameVar], type: genre.fameName};
+		}
 	}
 
 	return max;
diff --git a/src/js/porn.js b/src/js/porn.js
new file mode 100644
index 0000000000000000000000000000000000000000..a08723cd1f2d590a0a720ac32ba1a3cc09dd2df0
--- /dev/null
+++ b/src/js/porn.js
@@ -0,0 +1,36 @@
+App.Porn = {};
+App.Porn.Genre = {};
+
+App.Porn.Genre.cumAddict = {
+	fameVar: "cumAddict",
+	fameName: "cum addiction",
+	focusName: "cum addict",
+	hitText: "$His complete obsession with cum makes $him a hit with viewers that enjoy bukkake and cum drinking.",
+	type: "paraphilia", /* paraphilia, fetish, general, quirk */
+	prestigeDesc1: "Thousands have enjoyed watching $him do anything and everything for cum",
+	prestigeDesc2: "$His many fans relish the sight of $him doing anything for cum",
+	prestigeDesc3: "Millions are intimately familiar with the sight of $him doing anything for cum",
+	trinketShotDesc: "showing $him bathing in a tub of cum",
+	valid: function(slave) { return slave.sexualFlaw === "cum addict"; },
+	uiName: function() { return capFirstChar(this.focusName); }
+
+	/* TODO: need pornstarvar too (probably reorg pornstar stuff to $pornStar[fameVar].p2count/p3ID */
+	/* TODO: hit up updateSlaveObject with the porn-prestige description reset stuff */
+	/* TODO: hit up slaveInteract too */
+};
+
+App.Porn.getGenreByFameName = function(fameName) {
+	return App.Porn.Genre.values().find((g) => g.fameName === fameName);
+};
+
+App.Porn.getGenreByFocusName = function(focusName) {
+	return App.Porn.Genre.values().find((g) => g.focusName === focusName);
+};
+
+App.Porn.getAllGenres = function() {
+	return App.Porn.Genre.values();
+};
+
+App.Porn.getGenresByType = function(type) {
+	return App.Porn.Genre.values().filter((g) => g.type === type);
+};