code:
App.Events.RESosBombing = class RESosBombing extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.seeCats !== 0,
			() => V.projectN.status > 4
		];
	}

	execute(node) {
		let r = [];
		const impRaid = () => raidOutcome("Imperial Knights");
		const romanRaid = () => raidOutcome("Roman");
		const mercenaries = () => raidOutcome("Mercenaries");
		const standard = () => raidOutcome("Standard");
		const bombPlotHappened = either(1, 2, 3, 4) === 1;
		V.encyclopedia = "The Sons of Sekhmet";

		r.push(`While working at your desk, your security team sends you an urgently-marked bulletin. The security update enclosed is fairly simple; the security staff think they've got a "reasonable lead" on a potential terrorist operation by the Sons of Sekhmet to bomb a few essential locations in the arcology, sometime in the next few days.`);
		App.Events.addParagraph(node, r);
		r = [];
		r.push(`Although they're not positive, they're requesting a few thousand credits in additional funding for the resources they'll need to properly investigate the potential cell and launch a raid. Although a public operation would no doubt look bad in the public eye if it failed to produce results, not to mention the money you'd spend, ignoring the potential of a bombing campaign could be absolutely disastrous - and cost you many times more that whatever you'd spend shutting down Sekhmeti cells. Ultimately, though, the decision on whether to give the raid the go-ahead or not is up to you.`);
		App.Events.addParagraph(node, r);
		r = [];

		const choices = [];
		if (V.arcologies[0].FSNeoImperialistLaw1 === 1) {
			choices.push(new App.Events.Result(`Authorize the raid, but give your Imperial Knights authority over it`, impRaid));
		}
		if (V.arcologies[0].FSRomanRevivalistLaw === 1) {
			choices.push(new App.Events.Result(`Authorize the raid, but delegate it to your armed citizens instead of the security team`, romanRaid));
		}
		if (V.mercenaries === 5) {
			choices.push(new App.Events.Result(`Authorize the raid, but transfer authority to your ${V.mercenariesTitle}`, mercenaries));
		}
		choices.push(new App.Events.Result(`Authorize the raid with standard drones and guard officers`, standard));
		choices.push(new App.Events.Result(`Refuse authorization`, refuse));
		App.Events.addResponses(node, choices);

		function refuse() {
			const frag = new DocumentFragment();
			let r = [];
			if (bombPlotHappened) {
				r.push(`You tell the security team to stop chasing ghosts and focus on real issues in the arcology. Two days later, while you're working in your office, a distant booming sound rumbles out from outside the penthouse. Turning to look out the window, you see a column of smoke coming up from the marketplace, and the distant glow of a raging flame. A few seconds later, another muffled boom rings out, this time from the direction of the plaza, and then a third, and a fourth. Sounds of gunshots in the distance ring out shortly afterwards. From here, there's nothing you can do but watch as the series of explosions devastates the arcology's hard-built prosperity - and its citizens. This time wasn't just a fluke, it seems. You don't even need to turn on the TV to imagine what the media is already saying about your failure to stop this attack before it happened. It's not going to be good.`);
				V.arcologies[0].prosperity -= 5;
				cashX(-24000, "event");
				repX(-5000, "event");
			} else {
				r.push(`You tell the security team to stop chasing ghosts and focus on real issues in the arcology. A few days later, someone from the team bashfully sends you an "update" bulletin notifying you that it was a false alarm after all and they'd just gotten worked up over some intimidating posts on social media.`);
			}
			App.Events.addParagraph(frag, r);
			return frag;
		}

		/**
		 * @param {String} operationalCommand
		 * @returns {DocumentFragment}
		 */
		function raidOutcome(operationalCommand) {
			const frag = new DocumentFragment();
			const r = ["You authorize the raid"];
			switch(operationalCommand) {
				case "Imperial Knights":
					r.push("but bring in your Knights to captain the effort.");
					break;
				case "Roman":
					r.push("but transfer authority for its execution over to a group of loyal, free citizens, who are all too eager to defend the Republic.");
					break;
				case "Mercenaries":
					r.push(`but head up the effort with your better-trained ${V.mercenariesTitle}.`);
					break;
				case "Standard":
					r.push("with no further comments.");
			}

			r.push("Early in the morning the next day,");
			switch(operationalCommand) {
				case "Imperial Knights":
					if (bombPlotHappened) {
						r.push(`the heavily-armed Knights burst into an old warehouse in the lower sectors, interrupting a group of Sekhmeti terrorists building a series of improvised bombs on a bunch of turned-over crates. As the bombmakers scramble for their weapons, your Knights absolutely slaughter the group, blasting the scene to pieces with heavy firepower and shrugging off the helpless potshots the terrorists make with their ultra-heavy Imperial Plate. Although the overzealous Knights don't leave you anything left to enslave, the media are on the scene within minutes, and the interviews with smiling, unscathed Knights in their elegant coats of armor in front of the scene of an utterly destroyed terrorist cell is a good look for your arcology.`);
					} else {
						r.push(`the heavily-armed Knights burst into a small apartment in the lower sectors, nearly giving the old woman in the living room a heart attack. Multiple men in ultra-heavy Imperial Plate all but burst through the walls of the apartment to tackle a single scrawny teenager in his room before they realize that this place probably isn't a radical Sons of Sekhmet cell. Even though this raid ended up being wasted money, your Knights eloquently apologize to the edgy teenager they nearly crushed, and your arcology loves the romanticized Knights enough that there's no lasting reputation damage from the unfortunate raid.`);
					}
					break;
				case "Roman":
					if (bombPlotHappened) {
						r.push(`a group of Praetorians in full gear burst into a small apartment in the lower sectors, interrupting a small group of Sekhmeti terrorists building a series of improvised bombs in the barren living room. Before the bombmakers can even grab their guns, the well-disciplined citizens gun them down in an explosive hail of fire, killing the whole cluster indiscriminately. Although the zealous citizenry don't leave anything left for you to enslave, a group of proud, well-trained free citizens voluntarily defending the arcology from a terrorist plot - and doing so without so much as a scratch - is a good look.`);
					} else {
						r.push(`a group of Praetorians in full gear burst into a dilapidated warehouse in the lower sectors, shocking the small group of transport workers carrying crates around for some secondhand shipment. Although the citizens nearly fire on the rough-looking warehouse hands, they realize that this isn't a group of Sekhmeti radicals before they do and apologize for the insurrection. Although this raid ultimately turned out to be wasted money, the free citizens conducting it and their disciplined Roman nature prevents any lasting reputation damage.`);
					}
					break;
				case "Mercenaries":
					if (bombPlotHappened) {
						r.push(`the deadly mercenaries kick down the door of a small office building in the lower sectors, interrupting a group of Sekhmeti terrorists in the middle of constructing a series of improvised bombs in the converted office space. The ${V.mercenariesTitle} blow the entire group to pieces with high-power weaponry, absolutely annihilating the careful plot with overwhelming force and indiscriminate destruction before they can fire off a single shot. Although the rough mercenaries don't leave anything left for you to enslave, the well-trained mercs emerging from the smoldering ruin of what used to be a terrorist field HQ is a good look for the security of your arcology.`);
					} else {
						r.push(`the deadly mercenaries burst into the back of a small shop in the lower sectors, nearly giving the lower-class workers heart attacks with the sudden appearance of their scarred - over faces in full battle gear. Although they tackle two of the poor workers to the ground, it doesn't take the ${V.mercenariesTitle} long to realize that this isn't a radical Sons of Sekhmet terrorist cell, and they leave before the media can show up to report on it. Even though this raid turned out to be wasted money, your mercenaries conduct themselves well enough that there's no lasting reputation damage.`);
					}
					break;
				case "Standard":
					if (bombPlotHappened) {
						r.push(`a group of security officers burst into the back of a small shop in the lower sectors, interrupting a group of Sekhmeti terrorists in the middle of constructing a series of improvised bombs, having removed all the machinery from the back of the shop to make space. The drones fire their tasers at the Sons, but they're better-armed and trained than to get so easily disabled, and your officers and the bombmakers get drawn into a prolonged firefight over the next ten minutes that draws arcology media to the scene like a candle. Although a few of the officers involved limp out with bullet wounds and slashes, they manage to completely eradicate the terrorist cell, and brave officers coming out from the destroyed Sekhmeti HQ is a good look, even if they didn't manage to leave you any of the terrorists alive to enslave.`);
					} else {
						r.push(`a group of security officers kick down the door of a small office building in the lower sectors, practically giving the workers inside heart attacks. One of the drones misinterprets a panicked worker dropping a file as an aggressive action and tases him, much to the chagrin of the rest of the officer team. Arcology media is on the scene in minutes, and on top of being a waste of money, you've also got a PR nightmare on your hands as the tased worker complains on live media about the "unfair paranoia" of the arcology and how much his taser burns hurt. What a shit-show.`);
					}
			}

			if (operationalCommand !== "Standard" && bombPlotHappened) {
				repX(2500, "event");
			} else {
				repX((bombPlotHappened ? 1500 : -500), "event");
			}

			cashX(-5000, "event");
			App.Events.addParagraph(frag, r);
			return frag;
		}
	}
};


code:
App.Events.RESosSniper = class RESosSniper extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.seeCats !== 0,
			() => V.projectN.status > 4
		];
	}

	execute(node) {
		let r = [];

		V.encyclopedia = "The Sons of Sekhmet";

		if (S.Bodyguard) {
			App.Events.drawEventArt(node, S.Bodyguard);
		}
		const {
			He,
			he, his,
		} = getPronouns(S.Bodyguard ? S.Bodyguard : {pronoun: App.Data.Pronouns.Kind.neutral});

		const slave = GenerateNewSlave(null, {minAge: 18, maxAge: 32, disableDisability: 1});
		generateSalonModifications(slave);
		slave.face = random(50, 100);
		slave.origin = "$He was an elite sniper of the Sons of Sekhmet, nearly successful in assassinating you with a long-range shot from across the arcology before being captured by your Imperial Knights in a tense shootout.";
		slave.intelligenceImplant = 30;
		slave.career = "a sniper";
		slave.skill.combat = 1;
		slave.devotion = -100;
		slave.trust = random(-20, 10);
		slave.weight = random(-20, 30);
		slave.muscles = random(20, 80);
		slave.waist = random(10, 80);
		eyeSurgery(slave, "both", "normal");
		slave.shouldersTat = either("flowers", "tribal patterns", "asian art");
		slave.custom.tattoo = "$He has the orange sun of the Sons of Sekhmet tattooed on $his neck, marking $him as a high-ranking operative.";

		r.push(`It's a particularly nice day out and you've decided to visit some of the more prominent citizens of your arcology, the kind of people you might call 'friends' if genuine friendship wasn't a quick way to get stabbed in the back in the Free Cities. As you're having a pleasant conversation with one of these notables on the raised porch of his luxurious apartment, ${V.assistant.name} pings you with an urgent message: DUCK.`);
		App.Events.addParagraph(node, r);
		r = [];

		if (V.PC.skill.warfare >= 60) {
			r.push(`You don't even think about it. Combat reflexes kicking in, you dive onto the ground as something whizzes past your head close enough to shave off a lock of hair, slamming into the ground next to you with enough force to shatter the wood. Before you consciously realize what's going on, your body understands that you're being shot at,`);
			if (S.Bodyguard) {
				r.push(`${S.Bodyguard.slaveName} helping you up and shielding you with ${his} body as you're hustled into the citizen's apartment with the citizen himself.`);
			} else {
				r.push(`leaping up and springing towards the citizen's apartment with the frightened man himself.`);
			}
			r.push(`Two more shots slam into the wood around you as you all but leap into the citizen's apartment, one more bullet splintering the wood of the door as you close it shut behind you.`);
		} else if (!S.Bodyguard || S.Bodyguard.intelligence + S.Bodyguard.intelligenceImplant <= 60) {
			V.gameover = "snipedbysons";
			setTimeout(() => Engine.play("Gameover"), Engine.minDomActionDelay); // TODO: uhh, do we not want to delay here? If so, need to suspend the rest of the content.
		} else {
			r.push(`You blink once at the message in confusion before ${S.Bodyguard.slaveName} tackles you, ${his} shoulder bursting out with an explosion of red as ${he} catches a bullet clearly meant for you. ${He} cries out in pain, but it doesn't look like the hit was lethal; even injured and bleeding, ${he} shields you as you hustle into the open apartment along with the spooked citizen, more shots impacting around you as you wildly scramble into cover. A final bullet splinters the wood of the door as you close it shut behind you.`);
			cashX(-1000, "event", S.Bodyguard);
			healthDamage(S.Bodyguard, 60);
		}
		App.Events.addParagraph(node, r);
		r = [];
		r.push(`As you pant in the wake of the sudden attack, ${V.assistant.name} notifies you that they've traced the origin of the shots to somewhere in Sector Three. Having missed the killing shot, the sniper is undoubtedly packing up and moving to leave right now. You'll have to get a search party moving immediately if you want any hope of tracking them down.`);
		App.Events.addParagraph(node, r);

		const choices = [];
		if (V.arcologies[0].FSNeoImperialistLaw1 === 1) {
			choices.push(new App.Events.Result(`Lock down the sector and notify your Imperial Knights to lead citizens out in searching for the shooter`, impResponse));
		}
		if (V.arcologies[0].FSRomanRevivalistLaw === 1) {
			choices.push(new App.Events.Result(`Lock down the sector and notify your Praetorians to lead the citizens out in searching for the shooter`, romanResponse));
		}
		if (V.mercenaries === 5) {
			choices.push(new App.Events.Result(`Lock down the sector and notify your ${V.mercenariesTitle} to send in hunter-killer teams`, mercResponse));
		}
		choices.push(new App.Events.Result(`Just lock down the sector and send out search parties for the shooter`, lockdown));

		App.Events.addResponses(node, choices);

		function impResponse() {
			const frag = new DocumentFragment();
			let r = [];
			let sniperEscape;
			if (either(1, 2, 3) === 1) {
				sniperEscape = 1;
				r.push(`After a few hours of searching, your brightly-painted Knights return empty-handed, cursing that it was like no one was ever there.`);
			} else if (random(1, 2) === 1) {
				sniperEscape = 2;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your Knights are searching. About five minutes later, a Knight in bright red armor brings you a body slung over his shoulder and thumps it in the ground in front of you, riddled in bullet holes and marked with the logo of the Sons of Sekhmet. "We got the bastard, m'lord." The Knight drawls. With his other hand, he tosses a broken sniper rifle next to the corpse.`);
			} else {
				sniperEscape = 3;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your Knights are searching. About five minutes later, a Knight in bright red armor brings you a captive bound heavily with iron shackles, covered in body armor bearing the logo of the Sons of Sekhmet. In his other hand, he tosses a broken sniper rifle on the ground in front of you.`);
			}
			App.Events.addParagraph(frag, r);
			r = [];
			if (sniperEscape === 1) {
				r.push(`The assassination attempt and subsequent escape has scared many of your citizens, even after you pay for the damage the sniper bullets caused to the wealthy elite's property. A few days later, a trader passing by the arcology hands you an envelope, saying that he was told to pass it onto you. Inside is a single slip of paper, totally blank except for a single line of text: "See you soon." The envelope is emblazoned with the logo of an orange sun.`);
				V.arcologies[0].prosperity -= 1;
				repX(-2000, "event");
			} else if (sniperEscape === 2) {
				r.push(`Surviving the assassination attempt and killing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. This was not the first attempt we have made, and it will not be the last. Next time, we will inscribe his name on the bullet that finishes the job. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
			} else if (sniperEscape === 3) {
				r.push(`Surviving the assassination attempt and capturing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. The enemy has captured one of ours - in time we will free them, or perhaps they will awake to find a dagger in their back. Soon, they will understand the grave mistake of taking a Son into their ranks, even as a slave. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
				newSlave(slave);
			}
			App.Events.addParagraph(frag, r);
			return frag;
		}

		function romanResponse() {
			const frag = new DocumentFragment();
			let r = [];
			let sniperEscape;
			if (either(1, 2, 3) === 1) {
				sniperEscape = 1;
				r.push(`After a few hours of searching, your armed Praetorian citizens return empty-handed, cursing that it was like no one was ever there.`);
			} else if (random(1, 2) === 1) {
				sniperEscape = 2;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your citizens are searching. About five minutes later, a group of rough-looking Hastati citizens bring you a body slung over his shoulder and thumps it in the ground in front of you, riddled in bullet holes and marked with the logo of the Sons of Sekhmet. "We got the filth." The Hastati carrying the body enunciates. With his other hand, he tosses a broken sniper rifle next to the corpse.`);
			} else {
				sniperEscape = 3;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your citizens are searching. About five minutes later, a group of rough-looking Hastati citizens bring you a captive bound heavily with iron shackles, covered in body armor bearing the logo of the Sons of Sekhmet. In his other hand, he tosses a broken sniper rifle on the ground in front of you.`);
			}
			App.Events.addParagraph(frag, r);
			r = [];
			if (sniperEscape === 1) {
				r.push(`The assassination attempt and subsequent escape has scared many of your citizens, even after you pay for the damage the sniper bullets caused to the wealthy elite's property. A few days later, a trader passing by the arcology hands you an envelope, saying that he was told to pass it onto you. Inside is a single slip of paper, totally blank except for a single line of text: "See you soon." The envelope is emblazoned with the logo of an orange sun.`);
				V.arcologies[0].prosperity -= 1;
				repX(-2000, "event");
			} else if (sniperEscape === 2) {
				r.push(`Surviving the assassination attempt and killing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. This was not the first attempt we have made, and it will not be the last. Next time, we will inscribe his name on the bullet that finishes the job. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
			} else if (sniperEscape === 3) {
				r.push(`Surviving the assassination attempt and capturing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. The enemy has captured one of ours - in time we will free them, or perhaps they will awake to find a dagger in their back. Soon, they will understand the grave mistake of taking a Son into their ranks, even as a slave. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
				newSlave(slave);
			}
			App.Events.addParagraph(frag, r);
			return frag;
		}

		function mercResponse() {
			const frag = new DocumentFragment();
			let r = [];
			let sniperEscape;
			if (either(1, 2, 3) === 1) {
				sniperEscape = 1;
				r.push(`After a few hours of searching, your ${V.mercenariesTitle} return empty-handed, cursing that it was like no one was ever there.`);
			} else if (random(1, 2) === 1) {
				sniperEscape = 2;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your ${V.mercenariesTitle} are searching. About five minutes later, a small squad of heavily-armored mercenaries bring you a body, dragging it in the ground in front of you, riddled in bullet holes and marked with the logo of the Sons of Sekhmet. "We got the fucker." The sergeant of the mercenary squad grumbles. With his other hand, he tosses a broken sniper rifle next to the corpse.`);
			} else {
				sniperEscape = 3;
				r.push(`After maybe thirty minutes, you hear loud gunfire coming from the sector where your ${V.mercenariesTitle} are searching. About five minutes later, a small squad of heavily-armored mercenaries bring you a captive bound heavily with iron shackles, covered in body armor bearing the logo of the Sons of Sekhmet. One of the tosses a broken sniper rifle on the ground in front of you.`);
			}
			App.Events.addParagraph(frag, r);
			r = [];
			if (sniperEscape === 1) {
				r.push(`The assassination attempt and subsequent escape has scared many of your citizens, even after you pay for the damage the sniper bullets caused to the wealthy elite's property. A few days later, a trader passing by the arcology hands you an envelope, saying that he was told to pass it onto you. Inside is a single slip of paper, totally blank except for a single line of text: "See you soon." The envelope is emblazoned with the logo of an orange sun.`);
				V.arcologies[0].prosperity -= 1;
				repX(-2000, "event");
			} else if (sniperEscape === 2) {
				r.push(`Surviving the assassination attempt and killing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. This was not the first attempt we have made, and it will not be the last. Next time, we will inscribe his name on the bullet that finishes the job. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
			} else if (sniperEscape === 3) {
				r.push(`Surviving the assassination attempt and capturing the assassin earns you admiration of the citizenry, who see you as strong for punishing the assassin, particularly after you pay for the damage the sniper bullets caused to the wealthy elite's property. The next day, the Sons of Sekhmet release a 'news' update featuring their balaclava-wearing figurehead in front of his professional desk.`);
				App.Events.addParagraph(frag, r);
				r = [];
				r.push(`"Today a servant of Sekhmet was martyred while making an attempt on the life of a hedonistic new-world plutocrat. The enemy has captured one of ours - in time we will free them, or perhaps they will awake to find a dagger in their back. Soon, they will understand the grave mistake of taking a Son into their ranks, even as a slave. Abna Sakhmat damk-" You turn off the report. That's about all you need to see.`);
				V.arcologies[0].prosperity += 1;
				repX(2000, "event");
				newSlave(slave);
			}
			App.Events.addParagraph(frag, r);
			return frag;
		}

		function lockdown() {
			const frag = new DocumentFragment();
			let r = [];
			r.push(`After a few hours of searching, your guards return empty-handed, cursing that it was like no one was ever there. The assassination attempt and subsequent escape has scared many of your citizens, even after you pay for the damage the sniper bullets caused to the wealthy elite's property. A few days later, a trader passing by the arcology hands you an envelope, saying that he was told to pass it onto you. Inside is a single slip of paper, totally blank except for a single line of text: "See you soon." The envelope is emblazoned with the logo of an orange sun.`);
			V.arcologies[0].prosperity -= 1;
			repX(-2000, "event");
			App.Events.addParagraph(frag, r);
			return frag;
		}
	}
};

repo_name: Pecanus/fc

license: mit


code:
App.Events.SEProjectNbubbles = class SEProjectNbubbles extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 3,
			() =>	(V.projectN.wellFunded === 1 && App.Events.effectiveWeek() >= V.projectN.phase2 + 4) ||
					(V.projectN.wellFunded !== 1 && App.Events.effectiveWeek() >= V.projectN.phase2 + 6)
		];
	}

	execute(node) {
		V.projectN.status = 4;
		V.projectN.phase3 = App.Events.effectiveWeek();

		App.Events.addParagraph(node, [`You check in on the genelab, and are pleased to see that project N is making a great deal of progress. What was once an indistinguishable fleshy mass in a tube of mysterious green liquid looks now more like a small, unconscious humanoid body, pink-fleshed and nude, with a set of pointy triangular ears atop its bald head that vaguely resemble a cat. Doctor Nieskowitz steps up to you as you examine the growing body, actually smiling for once.`]);
		App.Events.addParagraph(node, [`"Ah, I see you've seen our latest progress. I'm quite proud of the team's work so far, you know - five months ago I would have said this kind of genetic splicing was simply impossible. But there's no better scientific feeling than proving yourself wrong, eh? Anyway, we've started to refer to the subject as 'Bubbles', on account of the-" The doctor is interrupted by a slight thrashing movement from the unconscious body, which produces a short span of bubbling within the tube's liquid. "- Well, that. But anyway, since you're funding the project and she'll be your property once we finish, I thought it best to ask you your opinion on the matter of a real name before we proceed any further."`]);

		App.Events.addResponses(node, [
			new App.Events.Result(`Bubbles is a good name`, bubbles),
			new App.Events.Result(`I had something else in mind`, rename)
		]);

		function bubbles() {
			return `You tell Nieskowitz that you like the name the scientists have chosen. He nods a couple times, jots something down in a notepad, and then returns to his work on subject delta - or, rather, on Bubbles.`;
		}

		function rename() {
			const frag = new DocumentFragment();
			let r = [];
			r.push(
				`You tell the waiting bioengineers that you'll be formally naming the growing catgirl `,
				App.UI.DOM.makeTextBox(V.subjectDeltaName, (v) => V.subjectDeltaName = v),
				`Nieskowitz nods, jots something down in a notepad, and then lets you know that he'll update you on the situation as it develops.`
			);
			r.push();
			App.Events.addParagraph(frag, r);
			return frag;
		}
	}
};


code:
App.Events.SEProjectNInitialized = class SEProjectNInitialized extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 1
		];
	}

	execute(node) {
		V.projectN.status = 2;
		V.projectN.phase1 = App.Events.effectiveWeek();

		App.Events.addParagraph(node, [`After giving the order to your personal assistant to begin preparations for your transhumanist genetic engineering project, you spend much of the next week getting in contact with the most renowned geneticists and biologists of the old world. Many of these gray-bearded intellectuals scoff as you initially explain your goal of making a 'catgirl', but change their tune almost immediately when you mention the paycheck attached. By the end of the week, you've acquired an impressive team of high-profile minds en route to the arcology, many of them already setting up workstations and tables full of imported chemicals within your laboratory.`]);
		App.Events.addParagraph(node, [`The first few days within the lab are spent organizing the new staff's structure; under your supervision, they create a formal research team headed by the ancient Doctor Nieskowitz, dubbed "Project N" both for the research head and as a dry joke by the ever-sardonic science team for their cat focused intentions. As things begin to formalize into the process of initial research, you realize you've got a decision to make before you've even started the project itself - are you going to keep Project N a secret from the rest of your arcology, or immediately bring it out into the public eye?`]);

		const choices = [];
		choices.push(new App.Events.Result(`Announce Project N to the public`, announce));
		choices.push(new App.Events.Result(`Keep Project N quiet`, quiet));
		App.Events.addResponses(node, choices);

		function announce() {
			let r = [];
			r.push(`You hold a press release the next day with a number of prominent journalists in the arcology noting the research team that you've gathered for project N, and its intention to create the world's first fully natural catgirl. Even though you've got absolutely no results to show and the project is still in its fully theoretical stage, the attending journalists react to the news with obvious excitement and you spend the next few hours answering conceptual questions that run on arcology media for the next couple of days, which helps promote your public image. When you return to the lab, Dr. Nieskowitz is clearly irritated, telling you that the media attention is almost guaranteed to slow down progress in these early stages.`);
			if (V.arcologies[0].FSBodyPurist !== "unset") {
				r.push(`This announcement absolutely <span class="red">horrifies</span> the body purists in your arcology.`);
			}
			repX(2000, "event");
			V.projectN.public = 1;
			V.bodyPuristRiot = 1;
			if (V.arcologies[0].FSBodyPurist !== "unset") {
				repX(-4000, "event");
			}
			return r;
		}

		function quiet() {
			return `You opt to keep Project N on the down-low for now, probably the safer choice. Lack of media attention will surely make your scientists <span class="green"> work faster</span> undisturbed, too.`;
		}
	}
};


code:
App.Events.SEProjectNMoMoney = class SEProjectNMoMoney extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 2,
			() => (V.projectN.public === 0 && App.Events.effectiveWeek() >= V.projectN.phase1 + 3) ||
				(V.projectN.public === 1 && App.Events.effectiveWeek() >= V.projectN.phase1 + 5)
		];
	}

	execute(node) {
		V.projectN.status = 0;
		V.projectN.phase2 = App.Events.effectiveWeek();

		App.Events.addParagraph(node, [`After a few weeks of theoretical designs and planning, the project N bioengineers finally have a set of results ready to present to you. Doctor Nieskowitz, heading up the team, presents you with a neat clipboard full of technical information as you enter the lab, telling you that he's got both good news and bad news.`]);
		App.Events.addParagraph(node, [`The balding man launches into the good news first. "We've got our first sample prepared, and we've begun physical work manipulating the growing genetic structure of the subject." Nieskowitz gestures behind him to one of the genetic tubes, a vat filled with thick green liquid, where a fist-sized cluster of pink flesh-like material floats in the center. "We call her subject Delta."`]);
		App.Events.addParagraph(node, [`"Unfortunately, it's not all rainbows and sunshine." Nieskowitz continues. "As it turns out, this work isn't going to be cheap. The initial cash injection let us get set up with our materials and provides the team a nice little paycheck, but we're going to need a lot more equipment to make this dream of yours a reality. We'll need top-line genetic sequencers, automated manipulation tools for precision physical work, a lot of new sterilization staff, complete AI integration with the equipment for constant monitoring..."`]);

		const askPrice = 220000;
		const adequatePrice = 125000;
		const barePrice = 50000;
		const choices = [];
		if (V.cash >= askPrice) {
			choices.push(new App.Events.Result(`Give Nieskowitz however much he wants`, unlimited, `equivalent to ${cashFormat(askPrice)}.`));
		}
		if (V.cash >= adequatePrice) {
			choices.push(new App.Events.Result(`Provide Project N with adequate funding`, adequate, `equivalent to ${cashFormat(adequatePrice)}.`));
		}
		if (V.cash >= barePrice) {
			choices.push(new App.Events.Result(`Give Project N the bare minimum to limp on`, bare, `equivalent to ${cashFormat(barePrice)}.`));
		}
		choices.push(new App.Events.Result(`Discontinue Project N`, cancel));
		App.Events.addResponses(node, choices);

		function unlimited() {
			cashX(-askPrice, "personalBusiness");
			V.projectN.status = 3;
			V.projectN.wellFunded = 1;
			return `You inform Nieskowitz that project N will have as much money as it needs. As the doctor starts rattling off necessities and upgrades to the genelab, the eventual list proves hugely expensive, but the good doctor assures you that the money spent - over double your initial investment - will make sure project N runs smoothly and rapidly.`;
		}

		function adequate() {
			cashX(-adequatePrice, "personalBusiness");
			V.projectN.status = 3;
			return `You agree to spend, the amount of money Nieskowitz requests, but set a strict budgetary limit for them to follow. The doctor seems pleased, and doesn't contest this ruling as he starts to prepare the expensive upgrades and redesigns that'll be needed to keep the project going at a steady rate.`;
		}

		function bare() {
			cashX(-barePrice, "personalBusiness");
			V.projectN.status = 3;
			V.projectN.poorlyFunded = 1;
			return `You give Nieskowitz the bare minimum budget needed to keep project N operational. The doctor seems obviously displeased and makes no effort to hide his frustration with your limited budget, noting that your stinginess will almost inevitably have consequences to the sensitive nature of their work. Time will tell if he's correct.`;
		}

		function cancel() {
			V.projectN.status = 0;
			return `You casually hand back the clipboard to Nieskowitz and tell him that you didn't plan on spending a cent more on project N - and still don't. As the frustrated biologists start to pack up their things, Nieskowitz tells you that if you "get your head out of your ass" they'll be available to resume work on the project, provided you have the money to fund it this time.`;
		}
	}
};


code:
App.Events.SEProjectNSaboteur = class SEProjectNSaboteur extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 4,
			() => (V.projectN.poorlyFunded !== 1) && (App.Events.effectiveWeek() >= V.projectN.phase3 + 4) ||
				((V.projectN.public === 1) && (V.projectN.poorlyFunded === 1) && (App.Events.effectiveWeek() >= V.projectN.phase3 + 6))
		];
	}

	execute(node) {
		V.encyclopedia = "The Sons of Sekhmet";
		V.projectN.status = 9;
		V.projectN.phase4 = App.Events.effectiveWeek();

		App.Events.addParagraph(node, [`You're awoken in the middle of the night by your virtual assistant, informing you that there's a security breach in the genetics lab. It seems like a small team of well-trained saboteurs is currently in the process of planting an explosive on the project N equipment, detected via the AI's routine scanning of the entire arcology's camera system and noticing errors in the genetics lab consistent with outside interference.`]);
		App.Events.addParagraph(node, [`You're not going to be able to get down there in time yourself, but you'll need to respond quickly, or else whoever sent these armed radicals will blow the lab, destroying all your work on project N and potentially killing a number of the nearby scientists, too. Over the cameras, as your assistant brings them back online, you can see one of them hastily spraying the symbol of a rising sun on the outside of the genelab, along with a short phrase in red paint - "THE SONS OF SEKHMET HAVE YOUR BLOOD".`]);

		const choices = [];
		if (V.projectN.wellFunded === 1) {
			choices.push(new App.Events.Result(`Activate the lab's integrated security`, security));
		}
		if (V.mercenaries === 5) {
			choices.push(new App.Events.Result(`Send for your ${V.mercenariesTitle} to bring in a rapid-response bomb defusal team`, mercenaries));
		}
		choices.push(new App.Events.Result(`Desperately try to make it to the lab yourself in time`, yourself));
		App.Events.addResponses(node, choices);

		function security() {
			V.projectN.status = 5;
			const frag = new DocumentFragment();
			let r = [];
			r.push(`You briefly thank your lucky stars that you invested enough in project N to provide for integrated security, then call for the lab's drones to activate. Over the cameras, you smugly watch from your comfortable bed as the security drones tear two of the mysterious saboteurs to shreds, then pin down and apprehend the third. Seems like all they managed to get you today was a brand new slave.`);
			repX(1500, "event");
			const slave = GenerateNewSlave(null, {minAge: 16, maxAge: 32, disableDisability: 1});
			generateSalonModifications(slave);
			slave.origin = "$He is an enslaved Son of Sekhmet, one of many idealistic rebel groups violently opposed to you.";
			slave.devotion = random(-90, -70);
			slave.trust = random(-20, 30);
			slave.weight = random(-20, 30);
			slave.muscles = random(20, 80);
			slave.waist = random(10, 80);
			r.push(App.UI.newSlaveIntro(slave));
			App.Events.addParagraph(frag, r);
			return frag;
		}

		function mercenaries() {
			V.projectN.status = 5;
			return `You immediately call for your ${V.mercenariesTitle} to send a bomb defusal team to the lab, and be ready to fight with the saboteurs on arrival. In the few minutes it takes for them to assemble a QRF and send it rocketing up to your laboratory, the saboteurs have already planted their bomb and vanished out the genelab's doors, disappearing into the dead of night. The mercenary team arrives shortly afterwards, bursts through the doors, and disables the ticking bomb with less than a minute left on the clock. Panting and sweating with nerves rather than fatigue, one of them flashes you a thumbs-up over the security cameras. Crisis narrowly averted.`;
		}

		function yourself() {
			V.projectN.status = 9;
			return `As you sprint towards the lab, an ear-shatteringly loud explosion freezes you in your tracks. Pieces of rubble and glass rain down to your sides for a few seconds, as you watch flames engulf the majority of your genetics laboratory, bright reds and oranges staining your retinas. Most of the project N scientists slept in rooms attached to the lab itself - you don't even need to read your AI report to know that Nieskowitz and the core team are dead already. It'd shock you more if they hadn't died with the project they worked so hard to build.`;
		}
	}
};


code:
App.Events.SEProjectNTechRelease = class SEProjectNTechRelease extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status >= 6,
			() => V.projectN.status !== 9,
			() => V.projectN.decisionMade !== 1,
			() => App.Events.effectiveWeek() >= V.projectN.phase4 + 5
		];
	}

	execute(node) {
		App.Events.addParagraph(node, [`Project N is complete. You've successfully engineered the world's first natural catgirl, ${V.subjectDeltaName}, whose face you occasionally see splayed out on some media report excitedly detailing "new scientific innovations". Despite the unwanted attention from radicals and terrorists the project generated, seeing your face displayed in the news as a patron of progress certainly helps to make it feel as though it was all worth it in the end.`]);
		App.Events.addParagraph(node, [`But even though the project's been finished, you still have one last decision to make. Media attention in the wake of ${V.subjectDeltaName}'s release has generated significant public interest in the technologies and documentation used in project N, and as the sole proprietor of the tech, you've received generous offers from other arcology owners interested in creating their own catgirls along with public petitions to release all documents for public use. If you don't release the project N tech now, it's possible that a competitor might figure out how to engineer their own catgirls in the future, so if you want to capitalize on the short-lived media craze for either public face or money, it'd be best to make a decision on the matter immediately.`]);

		const choices = [];
		choices.push(new App.Events.Result(`Release the Project N documentation and technology for free`, free));
		choices.push(new App.Events.Result(`Patent the Project N technologies and sell them selectively to other wealthy elites`, patent));
		choices.push(new App.Events.Result(`Hoard the Project N technologies to yourself to create a monopoly on catgirl production`, secret, `(WARNING: This decision will PERMANENTLY DISABLE any natural generation of catgirls outside the ones you create yourself.)`));
		App.Events.addResponses(node, choices);

		function free() {
			repX(3000, "event");
			V.projectN.decisionMade = 1;
			V.projectN.techReleased = 1;
			return `You opt to release the project N technologies for free public use, a decision you announce in a brief media conference the next day. The unexpectedly generous decision is met with praise by the general public, although, of course, the only people who have the capital to actually apply the tech will be other exclusive plutocrats. You're sure that at least some nerd in his basement is going to use your open-source code to try and grow a catgirl in his basement, though. Godspeed.`;
		}

		function patent() {
			cashX(80000, "project N tech sale");
			V.projectN.decisionMade = 1;
			V.projectN.techReleased = 1;
			return `You create a patent on the project N technologies and sell them to a select few arcology owners, mostly friends of yours. Although mildly disappointing to some who hoped for an open-source release, this is more or less business as usual in the free cities, and you get to line your own pockets while other elites prepare their own labs to corner the emerging demand for catgirl slaves alongside you. The rich get richer. Just another day in paradise.`;
		}

		function secret() {
			V.projectN.decisionMade = 1;
			repX(-1500, "event");
			return `You opt to leave the other elites completely out of it and refuse to sell or release the project N technologies at all. This decision is seen as open greed by both the general public and other wealthy elites, who make a point of bashing you over the choice on major news networks. Although you've ruffled some feathers by choosing to corner the market yourself, you now have a total monopoly on catgirl production, and as long as you can keep Nieskowitz and the other scientists churning out catgirls, you could make an absolute killing as their sole proprietor...`;
		}
	}
};


code:
App.Events.recRunawayCat = class recRunawayCat extends App.Events.BaseEvent {
	constructor(actors, params) {
		super(actors, params);
	}

	eventPrerequisites() {
		return [
			() => V.seeCats !== 0,
			() => V.projectN.techReleased !== 0,
			() => ((V.rep/400) > random(1, 100) || V.cheatMode === 1)
		];
	}

	get eventName() {
		return "Runaway Cat";
	}

	execute(node) {
		const slave = makeSlave();
		const {He, he, his, him, girl} = getPronouns(slave);
		let r = [];

		r.push(`Coming to your desk in the morning, you see that your assistant has noted that you have a 'pressing' piece of business to attend to. With a wave of your hand, you bring up the notification to see that a runaway slave has apparently been waiting outside your penthouse for the entire night. What's more, ${he}'s owned by a prominent rival arcology owner within the cluster - and a cat${girl}, to boot. Apparently, according to the short, hand-scrawled letter that ${V.assistant.name} has digitized into the notification, ${his} former master regularly beat ${him} both out of frustration and for pleasure, and ${he}'s approached you hoping for a better life in the slavery that's defined ${his} existence.`);
		App.Events.addParagraph(node, r);
		r = [];
		r.push(`The exclusive and novel nature of cat${girl}s makes ${him} an exceptionally valuable slave, and the genetic modification that created ${him} has ensured that the feline face you look over in the automated overview is particularly attractive, but taking ${him} under your wing away from another wealthy plutocrat would cause a whole score of problems. Deciding it'd be best not to make more enemies than you have to, you call up the rival on your personal phone, who answers after only a few seconds. After you explain the situation, the abusive oligarch chuckles, admits that he hadn't even noticed the cat${girl} leaving, and says he'll formally sell ${him} over to you for "just" twenty thousand credits. Otherwise, he'll give you a little cash to show his appreciation for sending the runaway back home.`);
		App.Events.addParagraph(node, r);
		r = [];
		r.push(App.UI.DOM.makeElement("span", `The other oligarch's offer is a pretty heavy ${cashFormat(20000)}, although his cat${girl} slave is pretty, young, and seems fairly well-trained. ${He}'s still got a few scars from regular abuse, though, both mental and physical, and despite ${his} offer of self-enslavement seems exceptionally skittish and scared around you.`, "note"));

		App.Events.addParagraph(node, r);
		const contractCost = 20000;
		const cost = slaveCost(slave) - contractCost;
		const responses = [];
		if (V.cash >= contractCost) {
			responses.push(new App.Events.Result(`Enslave ${him}`, enslave));
		} else {
			responses.push(new App.Events.Result(null, null, `You lack the necessary funds to enslave ${him}`));
		}
		responses.push(new App.Events.Result(`Return ${him} to ${his} owner`, sell, `This will bring in ${cashFormat(cost)}`));

		node.append(App.Desc.longSlave(slave, {market: "generic"}));

		App.Events.addResponses(node, responses);

		function enslave() {
			const el = new DocumentFragment();
			let r = [];
			cashX(forceNeg(contractCost), "slaveTransfer", slave);
			r.push(`You agree to the other plutocrat's offer and after a quick transfer of credits between your private lines, he casually congratulates you on your new slave, makes a joke about sloppy seconds, and hangs up. You call down for a guard outside to bring the skittish new cat${girl} up to your office so you can get your fresh new slave better introduced into your harem.`);
			r.push(App.UI.newSlaveIntro(slave));
			App.Events.addNode(el, r);
			return el;
		}

		function sell() {
			const el = new DocumentFragment();
			let r = [];
			cashX(cost, "slaveTransfer");
			r.push(`You shrug to yourself and tell the other oligarch you'll send ${him} back over. The rival plutocrat tells you that he'll send you offer a little 'gift' to show his appreciation as you hang up and order some of the arcology guards to take the nervous cat${girl} outside away. A few days later, the abusive oligarch makes good on his promise and you find a generous sum of money, about the estimate worth of the cat${girl}, deposited into your account. Easiest money you've ever made.`);
			App.Events.addNode(el, r);
			return el;
		}

		function makeSlave() {
			const slave = GenerateNewSlave(null, {minAge: 16, maxAge: 22, race: "catgirl"});
			slave.face = random(25, 75);
			slave.origin = "$He was bioengineered by a rival arcology owner. After being mistreated, $he escaped and came to you for protection.";
			slave.slaveName = setup.catSlaveNames.random();
			slave.birthName = slave.slaveName;
			slave.weight = 10;
			slave.muscles = 0;
			slave.waist = 10;
			slave.slaveSurname = "";
			slave.birthSurname = "";
			slave.career = "an orphan";
			slave.intelligenceImplant = 0;
			slave.teeth = "fangs";
			slave.devotion = random(-25, -10);
			slave.trust = random(-25, -10);
			slave.earShape = "none";
			slave.earT = "cat";
			slave.earTColor = slave.hColor;
			slave.earImplant = 1;
			slave.tailShape = "cat";
			slave.tailColor = slave.hColor;
			slave.eye.left.pupil = "catlike";
			slave.eye.right.pupil = "catlike";
			slave.accent = 2;
			slave.canRecruit = 0;
			App.Medicine.Modification.addScar(slave, "back", "whip");
			slave.behavioralFlaw = "odd";
			return slave;
		}
	}
};


code:
App.Events.SEVatCatBoy = class SEVatCatBoy extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 8,
			() => V.growingNewCat === 0,
		];
	}

	execute(node) {
		const slave = GenerateNewSlave("XY", {
			minAge: 16, maxAge: 16, nationality: "Stateless", disableDisability: 1, race: "catgirl"
		});
		slave.origin = "$He is a vat-grown catboy created by Dr. Nieskowitz and the science team in your genelab.";
		slave.face = random(55, 95);
		slave.faceShape = "feline";
		slave.slaveName = setup.catSlaveNames.random();
		slave.birthName = slave.slaveName;
		slave.slaveSurname = "";
		slave.birthSurname = "";
		slave.career = "an orphan";
		slave.intelligenceImplant = 0;
		slave.teeth = "fangs";
		slave.devotion = 20;
		slave.trust = 30;
		slave.earShape = "none";
		slave.earT = "cat";
		slave.earTColor = slave.hColor;
		slave.earImplant = 1;
		slave.tailShape = "cat";
		slave.tailColor = slave.hColor;
		slave.eye.left.pupil = "catlike";
		slave.eye.right.pupil = "catlike";
		slave.weight = 10;
		slave.muscles = 0;
		slave.waist = 10;
		slave.skill.oral = 0;
		slave.skill.anal = 0;
		slave.anus = 0;
		slave.skill.whoring = 0;
		slave.skill.entertainment = 0;
		slave.accent = 4;
		slave.canRecruit = 0;

		App.Events.addParagraph(node, [
			`With their latest genemodding project complete, Dr. Nieskowitz proudly presents to you a healthy, unconscious catboy, floating suspended in the tube of thick green liquid you use to grow them. "Looks like he came out just fine." The aging doctor says with an authoritative gesture. "Another successful project. ${slave.slaveName} is going to make a lovely addition to your little collection."`
		]);

		App.Events.addResponses(node, [new App.Events.Result(`Bring your new slave back home`, home)]);

		function home() {
			const frag = new DocumentFragment();
			let r = [];
			r.push(`There's no obnoxious media attention this time getting in the way of you and your brand new, confusedly meowing catboy. It's just you, the rising sun over your arcology, and a fresh new cat to lead up to the penthouse.`);
			V.projectN.status = 6;
			r.push(App.UI.newSlaveIntro(slave));
			App.Events.addParagraph(frag, r);
			return frag;
		}
	}
};


code:
App.Events.SEVatCatGirl = class SEVatCatGirl extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => V.projectN.status === 7,
			() => V.growingNewCat === 0,
		];
	}

	execute(node) {
		const slave = GenerateNewSlave("XX", {
			minAge: 16, maxAge: 16, nationality: "Stateless", disableDisability: 1, race: "catgirl"
		});
		slave.origin = "$He is a vat-grown catgirl created by Dr. Nieskowitz and the science team in your genelab.";
		slave.face = random(55, 95);
		slave.faceShape = "feline";
		slave.slaveName = setup.catSlaveNames.random();
		slave.birthName = slave.slaveName;
		slave.slaveSurname = "";
		slave.birthSurname = "";
		slave.career = "an orphan";
		slave.intelligenceImplant = 0;
		slave.devotion = 20;
		slave.trust = 30;
		slave.earShape = "none";
		slave.teeth = "fangs";
		slave.earT = "cat";
		slave.earTColor = slave.hColor;
		slave.earImplant = 1;
		slave.tailShape = "cat";
		slave.tailColor = slave.hColor;
		slave.eye.left.pupil = "catlike";
		slave.eye.right.pupil = "catlike";
		slave.weight = 10;
		slave.muscles = 0;
		slave.waist = 10;
		slave.skill.vaginal = 0;
		slave.vagina = 0;
		slave.skill.oral = 0;
		slave.skill.anal = 0;
		slave.anus = 0;
		slave.skill.whoring = 0;
		slave.skill.entertainment = 0;
		slave.accent = 4;
		slave.canRecruit = 0;
		App.Events.addParagraph(node, [`With their latest genemodding project complete, Dr. Nieskowitz proudly presents to you a healthy, unconscious catgirl, floating suspended in the tube of thick green liquid you use to grow them. "Looks like she came out just fine." The aging doctor says with an authoritative gesture. "Another successful project. ${slave.slaveName} is going to make a lovely addition to your little collection."`]);

		App.Events.addResponses(node, [new App.Events.Result(`Bring your new slave back home`, home)]);

		function home() {
			const frag = new DocumentFragment();
			let r = [];
			r.push(`There's no obnoxious media attention this time getting in the way of you and your brand new, confusedly meowing catgirl. It's just you, the rising sun over your arcology, and a fresh new cat to lead up to the penthouse.`);
			V.projectN.status = 6;
			r.push(App.UI.newSlaveIntro(slave));
			App.Events.addParagraph(frag, r);
			return frag;
		}
	}
};


code:
/**
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {DocumentFragment}
 */
App.Interact.fPet = function(slave) {
	const node = new DocumentFragment();
	let r = [];

	const {
		He, His,
		he, his, him, himself, girl
	} = getPronouns(slave);

	const {title: Master} = getEnunciation(slave);

	r.push(`You tell ${slave.slaveName} to`);
	if (!hasAnyLegs(slave)) {
		r.push(`have another slave set ${him} up onto your desk.`);
	} else if (tooBigBreasts(slave)) {
		r.push(`have another slave help ${him} heft ${his} tits so ${he} can be near you.`);
	} else if (tooBigBelly(slave)) {
		r.push(`have another slave help ${him} heft ${his} belly so ${he} can be near you.`);
	} else if (tooBigButt(slave)) {
		r.push(`have another slave help ${him} heft ${his} ass cheeks so ${he} can be near you.`);
	} else if (tooBigDick(slave)) {
		r.push(`have another slave help ${him} heft ${his} cock so ${he} can be near you.`);
	} else if (tooBigBalls(slave)) {
		r.push(`have another slave help ${him} heft ${his} balls so ${he} can be near you.`);
	} else if (tooFatSlave(slave)) {
		r.push(`have another slave help ${him} up so ${he} can be near you.`);
	} else {
		r.push(`move closer towards you.`);
	}

	r.push(`Once ${he}'s at an acceptable petting range, you bring the overgrown cat into your embrace and gingerly run a hand from the top of ${his} head down ${his} spine, coursing through the soft fur in a massage-like petting motion.`);

	if (slave.fetish === "mindbroken") {
		r.push(`${He} accepts the petting robotically, barely even comprehending what you're doing. The pleasant scritch of your fingers against ${his} back produces some sort of guttural purring sound, but that conveys the extent of the mindbroken cat's response.`);
	} else if (slave.devotion > 75) {
		r.push(`The cat${girl} loudly purrs and eagerly rubs up against you, arching ${his} back to give you a full range over the silky fur. ${His} tail flicks back and forth happily as you pet ${him}, flicking up to tease against your chin as you pet the purring kitten. When you finally retract your hand, ${slave.slaveName} bats ${his} eyelashes at you, still purring a little.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${His}`);
			if (canSee(slave)) {
				r.push(`eyes glimmer`);
			} else {
				r.push(`expression stares`);
			}
			r.push(`at you contentedly.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} signs a little thank-you for the pets.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"Why'd you stop, ${Master}? Can I suck you off so you keep going?"`));
		}
	} else if (slave.devotion > 50) {
		r.push(`The cat${girl} presents ${his} back to you eagerly for petting, purring softly as you stroke your hand up and down ${his} soft fur. It's easy to let your hand practically get lost in the silky material, especially since the eager cat does all ${he} can to keep your hand running up and down ${his} spine in tender petting motions. When you finally retract your hand, ${slave.slaveName} bats ${his} eyelashes at you, still purring a little.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${His}`);
			if (canSee(slave)) {
				r.push(`eyes glimmer`);
			} else {
				r.push(`expression stares`);
			}
			r.push(`at you contentedly.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} signs that ${he} loves you.`);
		} else {
			r.push(`${He} mrowls,`);

			r.push(Spoken(slave, `"That felt great, ${Master}. Can you pet me more often?"`));
		}
	} else if (slave.devotion > 20) {
		r.push(`The cat${girl} puts up a little façade of resistance, but just about immediately melts into your hands, giving you a full range of motion to pet and rub at ${his} silky fur. ${He} lets you pet along the full length of ${his} spine, rubbing under ${his} chin and exploring the curves of ${his} ass and breasts in a way that's, for once, entirely nonsexual. After about a minute, ${he} starts to purr at your petting, rumbling against your chest, and ${he} flutters ${his} eyelashes wistfully when you finally remove your hand.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${His}`);
			if (canSee(slave)) {
				r.push(`eyes glint`);
			} else {
				r.push(`expression stares`);
			}
			r.push(`at you with a smug aura.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} signs a little thank-you for the pets.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"Thanks, ${Master}. That was nice."`));
		}
	} else if (slave.devotion < -20 && slave.trust >= 20) {
		r.push(`${He} glowers at you as you start to pet ${him}, clearly doing ${his} best to express nonverbal dissatisfaction with you. Despite that, ${he} can't help but to shiver somewhat as you gingerly pet the fluffy cat, rumbling with a deep purr that betrays ${his} distaste for you. ${He} glares up at you the entire time, even as ${he} purrs, ${his} tail swishing about in the air as you pet the furry thing vigorously. When you finally retract your hand, ${he} acts like ${he} didn't enjoy it.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${His}`);
			if (canSee(slave)) {
				r.push(`eyes glower`);
			} else {
				r.push(`expression glowers`);
			}
			r.push(`at you.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} makes a rude sign gesture.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"I'm a person, not some kind of dumb animal."`));
		}
	} else if (slave.devotion >= -20 && slave.trust >= -20) {
		r.push(`${He} flinches back when you raise your hand towards ${him}, expecting to be hit. The tender petting that occurs obviously shocks ${him}, and for the first minute it looks like ${he}'s expecting you to change your mind at any moment and hit ${him} anyway. When you don't, only then does the scared cat${girl} allow ${himself} to get pet properly, slightly arching ${his} back to give you a range of motion against the silky fur as you stroke and play with your bioengineered cat${girl}. When you finally retract your hand, ${he} bats ${his} eyelashes at you, confused.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${He} just stares at you, blinking a few times.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} makes a confused sign gesture.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"I-Is that it, ${Master}..?"`));
		}
	} else if (slave.trust < -20) {
		r.push(`${He} flinches back when you raise your hand towards ${him}, expecting to be hit. The tender petting that occurs obviously shocks ${him}, and for the first minute it looks like ${he}'s expecting you to change your mind at any moment and hit ${him} anyway. When you don't, only then does the scared cat${girl} allow ${himself} to get pet properly, slightly arching ${his} back to give you a range of motion against the silky fur as you stroke and play with your bioengineered cat${girl}. When you finally retract your hand, ${he} bats ${his} eyelashes at you, confused.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${He} just stares at you, blinking a few times.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} makes a confused sign gesture.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"I-Is that it, ${Master}..?"`));
		}
	} else {
		r.push(`The cat${girl} puts up a little façade of resistance, but doesn't really fight back against your petting, giving you a full range of motion to pet and rub at ${his} silky fur. ${He} lets you pet along the full length of ${his} spine, rubbing under ${his} chin and exploring the curves of ${his} ass and breasts in a way that's, for once, entirely nonsexual. Despite ${himself}, ${he} can't help but to purr a little by the time you finally remove ${his} hands.`);
		if (!hasAnyArms(slave) && !canTalk(slave)) {
			r.push(`${His}`);
			if (canSee(slave)) {
				r.push(`eyes glint`);
			} else {
				r.push(`expression stares`);
			}
			r.push(`at you uncertainly.`);
		} else if (!canTalk(slave)) {
			r.push(`${He} makes a huffy mrowling noise.`);
		} else {
			r.push(`${He} mrowls,`);
			r.push(Spoken(slave, `"Thanks, I guess..."`));
		}
	}
	App.Events.addParagraph(node, r);
	return node;
};


code:
:: Dinner Party Preparations [nobr]

<<set $nextButton = "Cancel The Event", $nextLink = "Main">>

Hosting of high society dinner parties will increase your prestige significantly and it is expected for someone of your station. Since there is a lack of animal meat, human meat is served at these events to illustrate the wealth and power of the host. The success of the evening is judged by how well the human offering is prepared. Guests to these events are encouraged to rate the dishes and special dishes are expected. If the host receives 5 stars on all the dishes, they will receive the coveted title "<<if $PC.title == 1>>Master<<else>>Mistress<</if>> of The Culinary Arts". You ask $assistant.name to show you a list of cooking instructions for those dishes.

Cooking Instructions and Recipes:

''Roast Long Pig'' – Made with the meat of a human roasted on a spit. Young meat is better than old meat. There should be some amount of fat to make the meat juicy, but not too much fat that makes the meat greasy. Muscles affect the texture of the meat. Meat without muscles lacks texture while an excess will be tough and hard to chew. Remember that you can't eat silicone, and virgin meat is highly sought after.<<if $seePreg != 0>> If the human is pregnant, a veal dish can also be made from the fetus.<</if>>

''Dicky Roll'' – An erect penis made into a spring roll. For best results, harvest the penis at the moment of ejaculation. The size of the penis and the amount of accumulated ejaculate all affect the quality of the dish. Too big of a dick and too thin of semen can both ruin a dish.

''Testy Meat Balls'' – Testicles made into meat balls served with a sauce. Size matters.

''Titty Tartare'' – Made from finely chopped breasts, mixed with onions, capers, and seasonings, and served raw. Big breasts make the best quality meat for this dish. To add a hint of creamy taste, make sure the breasts are lactating. Remember that you can't eat silicone.

''Camel Toe à l'Orange'' – Pussy grilled like a steak. A big clit and big labia will improve the quality of the dish.

Your assistant will take care of the invitations and all the arrangements; all you need to do is pick the meat.

__Select Your Meat:__

<<includeDOM App.UI.SlaveList.slaveSelectionList(
		s => assignmentVisible(s) && s.fuckdoll === 0,
		App.UI.SlaveList.SlaveInteract.stdInteract,
		null,
		(s) => {
			const p = getPronouns(s);
			return App.UI.DOM.passageLink(`Make ${p.him} the main course`, "Dinner Party Execution",
				() => { $activeSlave = s; })
		}
	)>>


code:
App.Reminders = (function() {
	const displayDiv = document.createElement("div");
	const addDiv = document.createElement("div");
	return {
		add: add,
		list: list,
		fullDisplay: fullDisplay,
		slaveDisplay: slaveDisplay,
		slaveLink: slaveLink,
		dialog: dialog,
	};

	/**
	 * @param {string|Node} message
	 * @param {number} week
	 * @param {string} [category]
	 * @param {number} [slaveID]
	 */
	function add(message, week, category = "manual", slaveID = 0) {
		if (message === "" || message === null) {
			return;
		}
		const entry = {message: message, week: week, category: category};
		if (slaveID) {
			entry.slaveID = slaveID;
		}

		// V.reminders is sorted by week from low to high, we insert at the correct place so it remains sorted.
		const index = V.reminders.findIndex(e => e.week >= week);
		if (index === -1) {
			V.reminders.push(entry);
		} else {
			V.reminders.splice(index, 0, entry);
		}
	}

	/**
	 * @param {Object} [obj]
	 * @param {number} [obj.maxFuture] how far into the future should reminders be displayed.
	 * @param {function(FC.ReminderEntry):boolean} [obj.filter]
	 * @param {boolean} [obj.link] permit passage links
	 * @returns {HTMLSpanElement|DocumentFragment}
	 */
	function list({maxFuture = Number.POSITIVE_INFINITY, filter = null, link = false} = {}) {
		if (V.reminders.length === 0) {
			return document.createDocumentFragment();
		}

		if (!filter) {
			filter = () => true;
		}

		const outerSpan = document.createElement("span");

		function replace() {
			App.UI.DOM.replace(outerSpan, list({maxFuture, filter, link}));
			App.Utils.scheduleSidebarRefresh();
		}

		/**
		 * @param {FC.ReminderEntry} entry
		 */
		function clearEntry(entry) {
			V.reminders.splice(V.reminders.indexOf(entry), 1);
			replace();
		}

		// We only want to remove visible entries
		function clearOverdue() {
			V.reminders = V.reminders.filter(e => e.week >= V.week || e.week > V.week + maxFuture || !filter(e));
			replace();
		}

		function clearAll() {
			V.reminders = V.reminders.filter(e => e.week > V.week + maxFuture || !filter(e));
			replace();
		}

		let overdue = 0;
		let any = false;

		V.reminders.filter(e => e.week <= V.week + maxFuture && filter(e))
			.forEach(entry => {
				any = true;
				let week;
				let classes = []; // has to be an array, because makeElement takes no empty strings, but empty arrays.
				if (entry.week < V.week) {
					classes = ["red"];
					week = `${numberWithPluralOne(-(entry.week - V.week), 'week')} ago`;
					overdue++;
				} else if (entry.week === V.week) {
					classes = ["orange"];
					week = "today";
				} else {
					if (entry.week <= V.week + 5) {
						classes = ["green"];
					}
					week = `in ${numberWithPluralOne(entry.week - V.week, 'week')}`;
				}
				const div = document.createElement("div");
				div.append(entry.message, " ", App.UI.DOM.makeElement("span", week.toString(), classes));
				const slave = getSlave(entry.slaveID);
				if (slave) {
					const slaveName = link ?
						App.UI.DOM.passageLink(SlaveFullName(slave), "Slave Interact", () => {
							V.AS = slave.ID;
							if (Dialog.isOpen()) {
								Dialog.close();
							}
						}) :
						SlaveFullName(slave);
					div.append(" (for ", slaveName, ")");
				}
				div.append(" ", App.UI.DOM.link("Clear", clearEntry, [entry]));
				outerSpan.append(div);
			});

		if (overdue > 0) {
			outerSpan.append(App.UI.DOM.makeElement("div", App.UI.DOM.link("Clear Overdue", clearOverdue)));
		}
		if (any) {
			outerSpan.append(App.UI.DOM.makeElement("div", App.UI.DOM.link("Clear all", clearAll)));
		}

		return outerSpan;
	}

	/**
	 * @param {function():void} refresh
	 * @param {string} [category]
	 * @param {number} [slaveID]
	 * @returns {HTMLDivElement}
	 */
	function addField(refresh, category, slaveID) {
		jQuery(addDiv).empty();

		let entry = "";
		let week = 0;

		addDiv.append(
			App.UI.DOM.makeTextBox("", v => { entry = v; }),
			" in ", App.UI.DOM.makeTextBox(0, v => { week = v; }, true), " weeks.",
			" ", App.UI.DOM.link("Add", () => {
				add(entry, V.week + week, category, slaveID);
				refresh();
				App.Utils.scheduleSidebarRefresh();
				jQuery(addDiv).append(addField(refresh, category, slaveID));
			})
		);

		return addDiv;
	}

	/**
	 * @param {boolean} [link=false] show passage links
	 * @returns {HTMLElement}
	 */
	function fullDisplay(link) {
		jQuery(displayDiv).empty();
		displayDiv.append(App.UI.DOM.makeElement("h2", "Reminders"));

		const listEl = list({link});
		if (listEl !== null) {
			displayDiv.append(App.UI.DOM.makeElement("p", listEl, "indent"));
		}

		displayDiv.append(App.UI.DOM.makeElement("h3", "Add new"));
		displayDiv.append(App.UI.DOM.makeElement("p", addField(() => fullDisplay(link))));

		return displayDiv;
	}

	/**
	 * @param {number} slaveID
	 * @returns {HTMLElement}
	 */
	function slaveDisplay(slaveID) {
		jQuery(displayDiv).empty();
		displayDiv.append(App.UI.DOM.makeElement("h2", `Reminders for ${SlaveFullName(getSlave(slaveID))}`));

		const listEl = list({filter: e => e.slaveID === slaveID});
		if (listEl !== null) {
			displayDiv.append(App.UI.DOM.makeElement("p", listEl, "indent"));
		}

		displayDiv.append(App.UI.DOM.makeElement("h3", "Add new"));
		displayDiv.append(App.UI.DOM.makeElement("p", addField(() => slaveDisplay(slaveID), "slave", slaveID)));

		return displayDiv;
	}

	/**
	 * @param {number} slaveID
	 * @returns {HTMLElement}
	 */
	function slaveLink(slaveID) {
		return App.UI.DOM.link(String.fromCharCode(0x23f0), () => {
			dialog(slaveID);
		});
	}

	/**
	 * @param {number} [slaveID]
	 */
	function dialog(slaveID) {
		if (Dialog.isOpen()) {
			Dialog.close();
		}
		Dialog.setup("Reminders", "reminders");
		const showLinks = Story.lookup("tags", "jump-from-safe").some(x => x.title === passage());
		$(Dialog.body()).empty().append(slaveID ? slaveDisplay(slaveID) : fullDisplay(showLinks));
		Dialog.open();
	}
})();


code:
App.SecExp.BC = (function() {
	return {
		propHub,
		riotCenter,
		barracks,
		weaponsManufacturing,
		transportHub,
		secHub
	};

	function propHub() {
		if (V.SecExp.buildings.pr === null) {
			delete V.SecExp.buildings.pr;
		}
		if (V.SecExp.buildings.pr) {
			V.SecExp.buildings.propHub = V.SecExp.buildings.pr;
			delete V.SecExp.buildings.pr;
		}

		if (V.SecExp.buildings.propHub) {
			delete V.SecExp.buildings.propHub.active;
		}
		if (V.SecExp.buildings.propHub && Object.entries(V.SecExp.buildings.propHub).length === 0) {
			delete V.SecExp.buildings.propHub;
		} else if (V.propHub || (V.SecExp.buildings.propHub && Object.entries(V.SecExp.buildings.propHub).length > 0)) {
			V.SecExp.buildings.propHub = V.SecExp.buildings.propHub || {};
			V.SecExp.buildings.propHub.upgrades = V.SecExp.buildings.propHub.upgrades || {};
			V.SecExp.buildings.propHub.recruiterOffice = V.SecExp.buildings.propHub.recruiterOffice || V.SecExp.buildings.propHub.recuriterOffice || V.recuriterOffice || V.RecuriterOffice || 0;
			delete V.SecExp.buildings.propHub.recuriterOffice;

			V.SecExp.buildings.propHub.upgrades.campaign = V.SecExp.buildings.propHub.upgrades.campaign || V.SecExp.buildings.propHub.campaign || V.propCampaign || 0;
			delete V.SecExp.buildings.propHub.campaign;
			V.SecExp.buildings.propHub.upgrades.miniTruth = V.SecExp.buildings.propHub.upgrades.miniTruth || V.SecExp.buildings.propHub.miniTruth || V.miniTruth || 0;
			delete V.SecExp.buildings.propHub.miniTruth;

			V.SecExp.buildings.propHub.upgrades.secretService = V.SecExp.buildings.propHub.upgrades.secretService || V.SecExp.buildings.propHub.secretService || V.SecExp.buildings.propHub.SS || V.secretService || 0;
			delete V.SecExp.buildings.propHub.secretService;
			delete V.SecExp.buildings.propHub.SS;

			V.SecExp.buildings.propHub.focus = V.SecExp.buildings.propHub.focus || "social engineering";
			if (V.propFocus && V.propFocus !== "none") {
				V.SecExp.buildings.propHub.focus = V.propFocus;
			}

			V.SecExp.buildings.propHub.upgrades.fakeNews = V.SecExp.buildings.propHub.upgrades.fakeNews || V.SecExp.buildings.propHub.fakeNews || V.fakeNews || 0;
			delete V.SecExp.buildings.propHub.fakeNews;

			V.SecExp.buildings.propHub.upgrades.controlLeaks = V.SecExp.buildings.propHub.upgrades.controlLeaks || V.SecExp.buildings.propHub.controlLeaks || V.controlLeaks || 0;
			delete V.SecExp.buildings.propHub.controlLeaks;

			V.SecExp.buildings.propHub.upgrades.marketInfiltration = V.SecExp.buildings.propHub.upgrades.marketInfiltration || V.SecExp.buildings.propHub.marketInfiltration || V.marketInfiltration || 0;
			delete V.SecExp.buildings.propHub.marketInfiltration;

			V.SecExp.buildings.propHub.upgrades.blackOps = V.SecExp.buildings.propHub.upgrades.blackOps || V.SecExp.buildings.propHub.blackOps || V.blackOps || 0;
			delete V.SecExp.buildings.propHub.blackOps;
		}
	}

	function riotCenter() {
		if (V.riotCenter || (V.SecExp.buildings.riotCenter && Object.entries(V.SecExp.buildings.riotCenter).length > 0)) {
			V.SecExp.buildings.riotCenter = V.SecExp.buildings.riotCenter || {};
			V.SecExp.buildings.riotCenter.upgrades = V.SecExp.buildings.riotCenter.upgrades || V.riotUpgrades || {};
			V.SecExp.buildings.riotCenter.fort = V.SecExp.buildings.riotCenter.fort || V.fort || {};

			V.SecExp.buildings.riotCenter.upgrades.freeMedia = V.SecExp.buildings.riotCenter.upgrades.freeMedia || 0;
			V.SecExp.buildings.riotCenter.upgrades.rapidUnit = V.SecExp.buildings.riotCenter.upgrades.rapidUnit || 0;
			V.SecExp.buildings.riotCenter.upgrades.rapidUnitSpeed = V.SecExp.buildings.riotCenter.upgrades.rapidUnitSpeed || 0;

			V.SecExp.buildings.riotCenter.fort.reactor = V.SecExp.buildings.riotCenter.fort.reactor || 0;
			V.SecExp.buildings.riotCenter.fort.waterway = V.SecExp.buildings.riotCenter.fort.waterway || 0;
			V.SecExp.buildings.riotCenter.fort.assistant = V.SecExp.buildings.riotCenter.fort.assistant || 0;

			V.SecExp.buildings.riotCenter.sentUnitCooldown = V.SecExp.buildings.riotCenter.sentUnitCooldown || V.sentUnitCooldown || 0;
			V.SecExp.buildings.riotCenter.advancedRiotEquip = V.SecExp.buildings.riotCenter.advancedRiotEquip || V.advancedRiotEquip || 0;
			V.SecExp.buildings.riotCenter.brainImplant = V.SecExp.buildings.riotCenter.brainImplant || -1;
			if (jsDef(V.brainImplant)) {
				V.SecExp.buildings.riotCenter.brainImplant = V.brainImplant;
			}
			V.SecExp.buildings.riotCenter.brainImplantProject = V.SecExp.buildings.riotCenter.brainImplantProject || V.brainImplantProject || 0;
		}
	}

	function barracks() {
		if (V.SecExp.buildings.barracks) {
			delete V.SecExp.buildings.barracks.active;
		}
		if (V.SecExp.buildings.barracks && Object.entries(V.SecExp.buildings.barracks).length === 0) {
			delete V.SecExp.buildings.barracks;
		} else if (V.secBarracks || (V.SecExp.buildings.barracks && Object.entries(V.SecExp.buildings.barracks).length > 0)) {
			V.SecExp.buildings.barracks = V.SecExp.buildings.barracks || V.secBarracksUpgrades || {};
			V.SecExp.buildings.barracks.size = V.SecExp.buildings.barracks.size || 0;
			V.SecExp.buildings.barracks.luxury = V.SecExp.buildings.barracks.luxury || 0;
			V.SecExp.buildings.barracks.training = V.SecExp.buildings.barracks.training || 0;
			V.SecExp.buildings.barracks.loyaltyMod = V.SecExp.buildings.barracks.loyaltyMod || 0;
			if (V.SecExp.buildings.barracks.upgrades) {
				V.SecExp.buildings.barracks.size = V.SecExp.buildings.barracks.upgrades.size;
				V.SecExp.buildings.barracks.luxury = V.SecExp.buildings.barracks.upgrades.luxury;
				V.SecExp.buildings.barracks.training = V.SecExp.buildings.barracks.upgrades.training;
				V.SecExp.buildings.barracks.loyaltyMod = V.SecExp.buildings.barracks.upgrades.loyaltyMod;
				delete V.SecExp.buildings.barracks.upgrades;
			}
		}
	}

	function weaponsManufacturing() {
		if (V.weapManu || (V.SecExp.buildings.weapManu && Object.entries(V.SecExp.buildings.weapManu).length > 0)) {
			V.SecExp.buildings.weapManu = V.SecExp.buildings.weapManu || {};
			V.SecExp.buildings.weapManu.space = V.SecExp.buildings.weapManu.space || 500;
			V.SecExp.buildings.weapManu.menials = V.SecExp.buildings.weapManu.menials || V.weapMenials || V.weapHelots || 0;
			V.SecExp.buildings.weapManu.productivity = V.SecExp.buildings.weapManu.productivity || V.weapProductivity || 1;
			V.SecExp.buildings.weapManu.lab = V.SecExp.buildings.weapManu.lab || V.weapLab || 1;
			V.SecExp.buildings.weapManu.sellTo = V.SecExp.buildings.weapManu.sellTo || V.sellTo || {};
			if (!jsDef(V.SecExp.buildings.weapManu.sellTo.citizen)) {
				V.SecExp.buildings.weapManu.sellTo.citizen = 1;
			}
			if (!jsDef(V.SecExp.buildings.weapManu.sellTo.raiders)) {
				V.SecExp.buildings.weapManu.sellTo.raiders = 1;
			}
			if (!jsDef(V.SecExp.buildings.weapManu.sellTo.oldWorld)) {
				V.SecExp.buildings.weapManu.sellTo.oldWorld = 1;
			}
			if (!jsDef(V.SecExp.buildings.weapManu.sellTo.FC)) {
				V.SecExp.buildings.weapManu.sellTo.FC = 1;
			}

			V.SecExp.buildings.weapManu.upgrades = V.SecExp.buildings.weapManu.upgrades || {};
			V.SecExp.buildings.weapManu.upgrades.completed = V.SecExp.buildings.weapManu.upgrades.completed || V.completedUpgrades || [];
			if (!App.SecExp.weapManu.completed()) {
				V.SecExp.buildings.weapManu.upgrades.queue = V.SecExp.buildings.weapManu.upgrades.queue || [];
				if (jsDef(V.currentUpgrade)) {
					if (!jsDef(V.currentUpgrade.ID)) {
						if (V.currentUpgrade.name === "magnetic based ballistic weaponry") {
							V.currentUpgrade.ID = 0;
						} else if (V.currentUpgrade.name === "ceramo-metallic alloys") {
							V.currentUpgrade.ID = 1;
						} else if (V.currentUpgrade.name === "rapid action stimulants") {
							V.currentUpgrade.ID = 2;
						} else if (V.currentUpgrade.name === "fast response neural stimulant") {
							V.currentUpgrade.ID = 3;
						} else if (V.currentUpgrade.name === "universal cyber enhancements") {
							V.currentUpgrade.ID = 4;
						} else if (V.currentUpgrade.name === "remote neural links") {
							V.currentUpgrade.ID = 5;
						} else if (V.currentUpgrade.name === "combined training regimens with the special force") {
							V.currentUpgrade.ID = 6;
						} else if (V.currentUpgrade.name === "a variant of the stimulant cocktail that the special force created") {
							V.currentUpgrade.ID = 7;
						} else if (V.currentUpgrade.name === "a mesh network based off the custom network of the special force") {
							V.currentUpgrade.ID = 8;
						} else if (V.currentUpgrade.name === "dynamic battle aware AI") {
							V.currentUpgrade.ID = -1;
						} else if (V.currentUpgrade.name === "adaptive armored frames") {
							V.currentUpgrade.ID = -2;
						} else if (V.currentUpgrade.name === "advanced synthetic alloys") {
							V.currentUpgrade.ID = -3;
						}
					}
					V.SecExp.buildings.weapManu.upgrades.queue.push({ID: V.currentUpgrade.ID, time: V.currentUpgrade.time});
				}

				if (jsDef(V.SecExp.buildings.weapManu.upgrades.current)) {
					if (V.SecExp.buildings.weapManu.upgrades.current.time > 0) {
						V.SecExp.buildings.weapManu.upgrades.queue.push(V.SecExp.buildings.weapManu.upgrades.current);
					}
					delete V.SecExp.buildings.weapManu.upgrades.current;
				}
			} else {
				delete V.SecExp.buildings.weapManu.upgrades.queue;
			}
		}
	}

	function transportHub() {
		if (V.transportHub || (V.SecExp.buildings.transportHub && Object.entries(V.SecExp.buildings.transportHub).length > 0)) {
			V.SecExp.buildings.transportHub = V.SecExp.buildings.transportHub || {};
			V.SecExp.buildings.transportHub.airport = V.SecExp.buildings.transportHub.airport || V.airport || 1;
			V.SecExp.buildings.transportHub.security = V.SecExp.buildings.transportHub.security || V.hubSecurity || 1;
			V.SecExp.buildings.transportHub.surfaceTransport = V.SecExp.buildings.transportHub.surfaceTransport || 1;
			if (V.releaseID < 1093) {
				if (V.terrain !== "oceanic" && V.terrain !== "marine") {
					V.SecExp.buildings.transportHub.surfaceTransport = V.railway || 1;
				} else {
					V.SecExp.buildings.transportHub.surfaceTransport = V.docks || 1;
				}
			}
		}
	}

	function secHub() {
		if (V.secHQ || (V.SecExp.buildings.secHub && Object.entries(V.SecExp.buildings.secHub).length > 0)) {
			V.SecExp.buildings.secHub = V.SecExp.buildings.secHub || {};
			V.SecExp.buildings.secHub.menials = V.SecExp.buildings.secHub.menials || V.secMenials || V.secHelots || 0;
			V.SecExp.buildings.secHub.coldstorage = V.SecExp.buildings.secHub.coldstorage || 0;

			V.SecExp.buildings.secHub.upgrades = V.SecExp.buildings.secHub.upgrades || {};
			V.SecExp.buildings.secHub.upgrades.security = V.SecExp.buildings.secHub.upgrades.security || {};
			if (V.secUpgrades) {
				V.SecExp.buildings.secHub.coldstorage = V.secUpgrades.coldstorage;
				V.SecExp.buildings.secHub.upgrades.security = {
					nanoCams: V.secUpgrades.nanoCams,
					cyberBots: V.secUpgrades.cyberBots,
					eyeScan: V.secUpgrades.eyeScan,
					cryptoAnalyzer: V.secUpgrades.cryptoAnalyzer,
				};
			}
			V.SecExp.buildings.secHub.upgrades.crime = V.SecExp.buildings.secHub.upgrades.crime || V.crimeUpgrades || {};
			V.SecExp.buildings.secHub.upgrades.intel = V.SecExp.buildings.secHub.upgrades.intel || V.intelUpgrades || {};
			V.SecExp.buildings.secHub.upgrades.readiness = V.SecExp.buildings.secHub.upgrades.readiness || V.readinessUpgrades || {};

			V.SecExp.buildings.secHub.upgrades.security.nanoCams = V.SecExp.buildings.secHub.upgrades.security.nanoCams || 0;
			V.SecExp.buildings.secHub.upgrades.security.cyberBots = V.SecExp.buildings.secHub.upgrades.security.cyberBots || 0;
			V.SecExp.buildings.secHub.upgrades.security.eyeScan = V.SecExp.buildings.secHub.upgrades.security.eyeScan || 0;
			V.SecExp.buildings.secHub.upgrades.security.cryptoAnalyzer = V.SecExp.buildings.secHub.upgrades.security.cryptoAnalyzer || 0;

			V.SecExp.buildings.secHub.upgrades.crime.autoTrial = V.SecExp.buildings.secHub.upgrades.crime.autoTrial || 0;
			V.SecExp.buildings.secHub.upgrades.crime.autoArchive = V.SecExp.buildings.secHub.upgrades.crime.autoArchive || 0;
			V.SecExp.buildings.secHub.upgrades.crime.worldProfiler = V.SecExp.buildings.secHub.upgrades.crime.worldProfiler || 0;
			V.SecExp.buildings.secHub.upgrades.crime.advForensic = V.SecExp.buildings.secHub.upgrades.crime.advForensic || 0;

			V.SecExp.buildings.secHub.upgrades.intel.sensors = V.SecExp.buildings.secHub.upgrades.intel.sensors || 0;
			V.SecExp.buildings.secHub.upgrades.intel.radar = V.SecExp.buildings.secHub.upgrades.intel.radar || 0;
			V.SecExp.buildings.secHub.upgrades.intel.signalIntercept = V.SecExp.buildings.secHub.upgrades.intel.signalIntercept || 0;

			V.SecExp.buildings.secHub.upgrades.readiness.earlyWarn = V.SecExp.buildings.secHub.upgrades.readiness.earlyWarn || 0;
			V.SecExp.buildings.secHub.upgrades.readiness.rapidPlatforms = V.SecExp.buildings.secHub.upgrades.readiness.rapidPlatforms || 0;
			V.SecExp.buildings.secHub.upgrades.readiness.pathways = V.SecExp.buildings.secHub.upgrades.readiness.pathways || 0;
			V.SecExp.buildings.secHub.upgrades.readiness.rapidVehicles = V.SecExp.buildings.secHub.upgrades.readiness.rapidVehicles || 0;
		}
	}
})();


code:
App.SecExp.propHub = (function() {
	return {
		Init,
		GUI
	};

	function Init() {
		V.SecExp.buildings.propHub = {
			recruiterOffice: 0,
			upgrades: {
				campaign: 0,
				miniTruth: 0,
				fakeNews: 0,
				controlLeaks: 0,
				secretService: 0,
				blackOps: 0,
				marketInfiltration: 0,
			},
			focus: "social engineering",
		};
	}

	function GUI() {
		const node = new DocumentFragment();
		V.nextButton = "Back";
		V.nextLink = "Main";
		let r = [];

		node.append("The propaganda hub is a surprisingly inconspicuous building, dimly lit from the outside. You are well aware however of its role and its importance. You confidently enter its unassuming doorway and calmly review the work being done here.");
		if (V.RecruiterID !== 0) {
			App.Events.addParagraph(node, []);
			const hasOffice = V.SecExp.buildings.propHub.recruiterOffice === 1;
			node.append(App.UI.DOM.link(
				`${hasOffice ? "Revoke" : "Grant"} ${SlaveFullName(S.Recruiter)} office privileges.`,
				() => {
					V.SecExp.buildings.propHub.recruiterOffice = (hasOffice ? 0 : 1);
					App.UI.reload();
				}
			));
			App.UI.DOM.appendNewElement("div", node, "Providing them with an office will help boost your chosen campaign. This also stacks with the campaign boost edict.", "note");
		}
		App.Events.addParagraph(node, []);

		if (V.SecExp.buildings.propHub.upgrades.campaign === 0) {
			node.append(makeLink(
				"Set up a propaganda campaign to align your citizens with your goals",
				5000,
				"Will provide the focused resource each week, but will cost reputation in upkeep.",
				"campaign"
			));
		} else {
			const focusOptions = ["social engineering", "immigration", "enslavement"];
			App.UI.DOM.appendNewElement("div", node, "You have set up a team of trained slaves and expert propagandists, ready to spread the message of your choosing to the population.");
			if (V.SecExp.edicts.defense.militia >= 1) {
				focusOptions.push("recruitment");
			}

			for (const focus of focusOptions) {
				if (V.SecExp.buildings.propHub.focus === focus) {
					r.push(focus);
				} else {
					r.push(App.UI.DOM.link(
						`${focus}`,
						() => {
							V.SecExp.buildings.propHub.focus = focus;
							App.UI.reload();
						}
					));
				}
			}
			node.append("Focus on: ", App.UI.DOM.generateLinksStrip(r));

			r = ["You are concentrating your propaganda efforts towards"];
			switch(V.SecExp.buildings.propHub.focus) {
				case "social engineering":
					r.push("increasing the acceptance of your chosen future societies.");
					break;
				case "immigration":
					r.push("convincing more people to immigrate to your arcology.");
					break;
				case "enslavement":
					r.push("convincing more people to voluntarily enslave themselves.");
					break;
				case "recruitment":
					r.push("convincing more citizens to enter the militia.");
					break;
			}
			App.UI.DOM.appendNewElement("div", node, r.join(" "));

			if (V.SecExp.buildings.propHub.upgrades.campaign < 5) {
				App.UI.DOM.appendNewElement("div", node, makeLink(
					"Invest more resources into the project to increase its effectiveness",
					5000,
					"Will provide more of the focused resource each week but increase reputation upkeep.",
					"campaign",
					true
				));
			} else {
				App.UI.DOM.appendNewElement("div", node, "You upgraded your propaganda machine to its limits.");
			}
		}
		App.Events.addParagraph(node, []);

		if (V.SecExp.buildings.propHub.upgrades.miniTruth === 0) {
			node.append(makeLink(
				"Set up the authenticity department",
				5000,
				"Will provide authority and unlock special upgrades, but will increase upkeep.",
				"miniTruth",
				false,
				"Set up a department tasked with guaranteeing the authenticity of all information available in the arcology. Of course if reality is against what is best for the arcology, then it should be redacted as well"
			));
		} else {
			if (V.SecExp.buildings.propHub.upgrades.miniTruth < 5) {
				node.append(makeLink(
					"Enlarge the authenticity department",
					5000,
					"Will provide more authority each week, but increases upkeep.",
					"miniTruth",
					false,
					"Invest more resources into the project to increase its effectiveness.",
				));
			} else {
				node.append("You have upgraded the authenticity department to its maximum.");
			}

			if (V.SecExp.buildings.propHub.upgrades.fakeNews === 0) {
				App.UI.DOM.appendNewElement("div", node, makeLink(
					"Install a news generator",
					10000,
					"The authenticity dept. now provides a small amount of reputation as well as authority, but increases upkeep.",
					"fakeNews",
					false,
					"Install an automatic news generator, able to fabricate thousands of plausible sounding news every day."
				));
			} else {
				App.UI.DOM.appendNewElement("div", node);
				node.append(
					"You have installed an automatic news generator. ",
					App.UI.DOM.link(
						"Remove news generator",
						() => {
							V.SecExp.buildings.propHub.upgrades.fakeNews = 0;
							App.UI.reload();
						}
					)
				);
			}

			if (V.SecExp.buildings.propHub.upgrades.controlLeaks === 0) {
				App.UI.DOM.appendNewElement("div", node, makeLink(
					"Institute controlled leaks protocols",
					10000,
					"The authenticity dept. now provides a small amount of reputation as well as authority, but increases upkeep.",
					"controlLeaks",
					true,
					"Institute a system able to release erroneous, but plausible, information about your business, leading your competitors to prepared financial traps. "
				));
			} else {
				App.UI.DOM.appendNewElement("div", node);
				node.append(
					"You have instituted controlled leaks protocols, able to create fabricated leaks of sensible information. ",
					App.UI.DOM.link(
						"Shut down leak protocols",
						() => {
							V.SecExp.buildings.propHub.upgrades.controlLeaks = 0;
							App.UI.reload();
						}
					)
				);
			}
		}
		App.Events.addParagraph(node, []);

		if (V.SecExp.buildings.propHub.upgrades.secretService === 0) {
			node.append(makeLink(
				"Set up personal secret service",
				10000,
				"Will provide authority and unlock special upgrades, but will increase upkeep.",
				"secretService",
				false,
				"Set up a department tasked with the protection of your person, as well as operations requiring a delicate approach."
			));
		} else {
			if (V.SecExp.buildings.propHub.upgrades.secretService < 5 && V.rep >= (V.SecExp.buildings.propHub.upgrades.secretService * 1000) + 5000) {
				node.append(makeLink(
					"Expand the secret service",
					5000,
					"Will provide more authority each week, but increases upkeep.",
					"secretService",
					false,
					"Invest more resources into the project to increase its effectiveness."
				));
			} else if (V.SecExp.buildings.propHub.upgrades.secretService < 5) {
				node.append("You lack the reputation to further expand operations.");
			} else {
				node.append("You have upgraded the secret service to its maximum.");
			}

			if (V.SecExp.buildings.propHub.upgrades.blackOps === 0) {
				App.UI.DOM.appendNewElement("div", node, makeLink(
					"Create a black ops team",
					10000,
					"The secret services now provides security as well as authority, but increases upkeep.",
					"blackOps",
					false,
					"Create a black ops team, ready to carry out corporate sabotage and sensitive operations to further your goals."
				));
			} else {
				App.UI.DOM.appendNewElement("div", node, "You have created a black ops team.");
			}

			if (V.SecExp.buildings.propHub.upgrades.marketInfiltration === 0) {
				App.UI.DOM.appendNewElement("div", node, makeLink(
					"Infiltrate the black market in order to manipulate it",
					10000,
					"The secret services now provides cash as well as authority each week, but will increase crime growth",
					"marketInfiltration"
				));
			} else {
				App.UI.DOM.appendNewElement("div", node);
				node.append(
					"You have infiltrated the black market and are now in partial control of it. ",
					App.UI.DOM.link(
						"Withdraw from the black market",
						() => {
							V.SecExp.buildings.propHub.upgrades.marketInfiltration = 0;
							App.UI.reload();
						}
					)
				);
			}
		}

		return node;

		function makeLink(text, price, note, type, applyHacking=true, message=null) {
			const r = new DocumentFragment();
			r.append(App.UI.DOM.link(text,
				() => {
					cashX(forceNeg(getCost()), "capEx");
					if (applyHacking) {
						V.PC.skill.hacking++;
					}
					V.SecExp.buildings.propHub.upgrades[type]++;
					if (V.SecExp.buildings.propHub.upgrades.campaign === 1) {
						V.SecExp.buildings.propHub.focus = "social engineering";
					}
					App.UI.reload();
				},
			));
			if (message) {
				App.UI.DOM.appendNewElement("div", r, `${message}`);
			}
			App.UI.DOM.appendNewElement("div", r, `Costs ${cashFormat(getCost())}. ${note}`, "note");
			return r;

			function getCost() {
				const HistoryDiscount = ["capitalist", "celebrity", "wealth"].includes(V.PC.career) ? 0.5 : 1;
				return Math.trunc(price * V.upgradeMultiplierArcology * (V.SecExp.buildings.propHub.upgrades[type] + 1) * HistoryDiscount * (applyHacking ? V.HackingSkillMultiplier : 1));
			}
		}
	}
})();