code:
/*Mostly made for myself, this adds a repeatable event at the start of the Friday parties for PCs which refused the offer from the Rich kid twice. This is basically a variation on the EventQuadPartyMilkshakeBullied chain of passages/events with options to go along, submit, or back out.*/

/*Added new continue link to QuadPartyAttend for the repeat event. This event would only fire if the player went through with the bullied event by refusing the offer twice.*/

:: QuadPartyAttend [event] {"position":"725,11100","size":"100,100"}
<<set _bff to "The Best Friend">>\
<<set $quadparty.chatgroup to [setup.people_niches()[_bff]]>>\
<<firstname _bff>> is waiting for you at the edge of the quad.

"Hey <<nickname $pc>>," <<ps _bff>> <<conj say>>. <<psc>> <<conj grin>>. "Ready to party?"
<<set $quadparty.popularity++>>\

<<nobr>>
<<if setup.people.pronouns("PC").gender is "female" and $quadparty.popularity gte 6 and $quadparty.milkshakes gte 2 and !$quadparty.milkshakesuspicion>>
    <<link "Continue" EventQuadPartyMilkshakeSpecial>><</link>>
<<elseif setup.people.pronouns("PC").gender is "female" and $quadparty.popularity gte 9 and $quadparty.milkshakes lt 2 and $quadparty.milkshakesrefused gte 2 and !$quadparty.milkshakesuspicion>>
    <<link "Continue" EventQuadPartyMilkshakeObserved>><</link>>
<<elseif setup.people.pronouns("PC").gender is "female" and $quadparty.popularity gte 18 and !$quadparty.milkshakesuspicion>>
    <<link "Continue" EventQuadPartyMilkshakeNotOffered>><</link>>
<<elseif $quadparty.popularity gte 9 and !$quadparty.milkshakesuspicion and setup.people.pronouns("PC").gender isnot "female">>
    <<link "Continue" EventQuadPartyMilkshakeTold>><</link>>
<<elseif $quadparty.milkshakeproposal and $quadparty.milkshakedeal is false and !$quadparty.bullied>>
    <<link "Continue" EventQuadPartyMilkshakeBullied>><</link>>
<<elseif $quadparty.bullied and State.random() lte 0.4 and $kinkcontent.includes("dubcon")>>
    <<link "Continue" EventQuadPartyMilkshakeBulliedRepeat>><</link>>
<<else>>
    <<link "Continue" QuadParty>><</link>>
<</if>>
<</nobr>>

/*New event that repeats every party after the player turns down the milkshake offer twice with multiple options. Excludes the Rich Kid, but grabs three other NPCs from Helleborine Hall to use.*/

:: EventQuadPartyMilkshakeBulliedRepeat [event] {"position":"350,11725","size":"100,100"}
<<nobr>>
    <<set _bff to "The Best Friend">><<set _p1 to $niches["The Rich Kid"]>>
    <<set _students to setup.people.people_of_type("student")>>

    <<set _p2 to setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, notfriend: true, inclinations: "active", exclude: [_p1], gender: ["male"]}, _students) || setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, inclinations: "active", exclude: [_p1], gender: ["male"]}, _students)>>

    <<set _p3 to setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, notfriend: true, inclinations: "active", exclude: [_p1, _p2], gender: ["male"]}, _students) || setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, inclinations: "active", exclude: [_p1, _p2]}, _students)>>

    <<set _p4 to setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, notfriend: true, inclinations: "active", exclude: [_p1, _p2, _p3], gender: ["male"]}, _students) || setup.Events.pick_person({type: "student", residence: "Helleborine Hall", attitudes: ["friendship"], inverseattitude: true, inclinations: "active", exclude: [_p1, _p2, _p3]}, _students)>>

    <<set $eventnpc to _p2>>
    <<set $eventnpc2 to _p3>>
    <<set $eventnpc3 to _p4>>
<</nobr>>\
<<firstname _bff>> heads onto the quad, and you make to follow when <<anonorfullname _p2>> steps in your way stopping you. You're pretty sure <<pss>> one of the guys from Helleborine...

"Word's spread that you know about our little secret here," <<ps>> <<conj say>>. "Me and a couple of the guys went ahead and made you a special milkshake. You should follow me."

<<skillgate Disinhibition 3 "Check out this \"special\" milkshake" EventQuadPartyMilkshakeBulliedRepeatEager>><</skillgate>>
<<skillgate Submission 2 "Follow" EventQuadPartyMilkshakeBulliedRepeatFollow>><</skillgate>>
<<link "Refuse" EventQuadPartyMilkshakeBulliedRepeatRefuse>><</link>>

/*Passage for PC that williningly follows with variation for if the PC has Cumslut inclination.*/

:: EventQuadPartyMilkshakeBulliedRepeatEager [event nobr] {"position":"475,11725","size":"100,100"}
You follow <<po>> into <<pp>> dorm room and notice <<anonorfullname $eventnpc2>> coming in behind you, closing the door. It looks like <<ps $eventnpc2>> <<conj were>> making sure you didn't back out.

Inside you see <<anonorfullname $eventnpc3>> with <<pp>> cock out, unloading a few thick ropes of cum into a red cup in <<pp $eventnpc3>> hand. <<psc $eventnpc3>> <<conj look>> up at you and <<conj grin>>, taking the freshly filled cup and offering it to you.

You wordlessly accept the offered cup, bringing it right under your nose and taking a deep breath. The scent hits you and you are sure of it now. There is no milkshake to be found here, just pure warm, thick cum. Several loads of it, judging by how full the cup is.

The guys watch you expectantly as you take a sip from the "special" milkshake they all helped prepare. The taste of thick salty-sweet cum fills your mouth and you close your eyes as you gulp it down.

<<if $pc.has_inclination("Cumslut")>>
    You pour the rest of the cum into your mouth, savouring the taste. You look at the Helleborine guys and open your mouth, showing them how full of their cum it is, before swallowing it all down. <<dalterneed Arousal 300 true>>
<<else>>
    You continue to take small sips from the cup. All the while, the Helleborine guys are watching each mouthful of cum slide down your throat. <<dalterneed Arousal 100 true>>
<</if>>
<<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>>

"Fuck, that's hot," <<anonorfirstname $eventnpc>> <<conj say>>.

The deed now done, you toss the empty cup in the trash and leave the room.

<<trueattracted $eventnpc>><<lust $eventnpc 100>><</trueattracted>>
<<trueattracted $eventnpc2>><<lust $eventnpc2 100>><</trueattracted>>
<<trueattracted $eventnpc3>><<lust $eventnpc3 100>><</trueattracted>>
<<rpromiscuity $eventnpc 100>>
<<rpromiscuity $eventnpc2 100>>
<<rpromiscuity $eventnpc3 100>>
<<unset $eventnpc>><<unset $eventnpc2>><<unset $eventnpc3>>
<<link "Continue" QuadParty>><</link>>

/*Passage for PC that submits and follows.*/

:: EventQuadPartyMilkshakeBulliedRepeatFollow [event nobr] {"position":"600,11725","size":"100,100"}
You follow <<po>> into <<pp>> dorm room and notice <<anonorfullname $eventnpc2>> coming in behind you, closing the door. It looks like <<ps $eventnpc2>> <<conj were>> making sure you didn't back out.

Inside you see <<anonorfullname $eventnpc3>> with <<pp>> cock out, unloading a few thick ropes of cum into a red cup in <<pp $eventnpc3>> hand. <<psc $eventnpc3>> looks up at you and grins, taking the freshly filled cup and offering it to you.

"Umm, is it too late to decline?" you ask.

"Very much so," <<danonorfirstname $eventnpc2>> <<conj say>>, resting a hand on your shoulder.

You sigh and accept the offered cup. The smell hits you right away — there is no milkshake here, just pure warm, thick cum. Several loads of it, judging by how full the cup is.

You look down at the filled cup, then back at the guys. Seeing little choice, you take a sip from the "special" milkshake they all helped prepare. The taste of thick salty-sweet cum fills your mouth and you close your eyes as you gulp it down. <<if $pc.has_inclination("Cumslut")>><<dalterneed Arousal 300 true>><</if>>

You continue to take small sips from the cup. All the while, the Helleborine guys are watching each mouthful of cum slide down your throat. <<dalterneed Humiliation 150 true>>
<<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>><<run $pc.swallow_cum()>>

"Fuck, that's hot," <<anonorfirstname $eventnpc>> <<conj say>>. "All right, you can go now."

You toss the empty cup in the trash and quickly leave the room, lest they ask you to do more.

<<trueattracted $eventnpc>><<lust $eventnpc 100>><</trueattracted>>
<<trueattracted $eventnpc2>><<lust $eventnpc2 100>><</trueattracted>>
<<trueattracted $eventnpc3>><<lust $eventnpc3 100>><</trueattracted>>
<<rpromiscuity $eventnpc 50>>
<<rpromiscuity $eventnpc2 50>>
<<rpromiscuity $eventnpc3 50>>
<<unset $eventnpc>><<unset $eventnpc2>><<unset $eventnpc3>>
<<link "Continue" QuadParty>><</link>>

/*Passage for if the player refuses. The first check is if PC isn't interested in "male" or has dubcon content disabled. Submit option to let it play out otherwise, and an "escape" passage to stop the event altogether.*/

:: EventQuadPartyMilkshakeBulliedRepeatRefuse [event nobr] {"position":"725,11725","size":"100,100"}
"I think I'll pass," you say moving around <<anonorfirstname $eventnpc>>

"That wasn't really an offer," <<ps>> <<conj say>>. Before you can move past, someone places their hand over your mouth and grabs hold of your arm.

<<if !$pcsexualprefs.includes("male") or !$kinkcontent.includes("dubcon")>>
    <<if $pc.physicality_check(8)>>
        Twisting your arm, you pull yourself free causing <<anonorfullname $eventnpc2>>, who had snuck up behind you, to stagger back.
    <<else>>
        Twisting your arm, you manage to slip free from <<anonorfullname $eventnpc2>> who had snuck up behind you.
    <</if>>
    You stare at the two of them before continuing onto the quad, neither one trying to stop you anymore.

    <<alterneed Pain 100>>
    <<unset $eventnpc>><<unset $eventnpc2>><<unset $eventnpc3>>
    <<link "Continue" QuadParty>><</link>>
<<else>>
    It appears that <<anonorfullname $eventnpc2>> had snuck up behind you while you were talking. The two of them ignore your struggles while bringing you to one of the dorm rooms. They shove you inside, closing the door behind them.

    You take a moment to gather yourself before you see <<anonorfullname $eventnpc3>> with <<pp>> cock out, unloading a few thick ropes of cum into a red cup in <<pp $eventnpc3>> hand. <<psc $eventnpc3>> <<conj look>> up at you and <<conj grin>>.

    "Guess <<ps $pc>> didn't want to coperate?" <<ps $eventnpc3>> <<conj ask>>.

    "Nope, so we're doing this the hard way instead," <<danonorfirstname $eventnpc>> <<conj say>>. <<psc>> <<conj reach>> out and <<conj take>> hold of your face, forcing you to look at <<po>> while <<danonorfirstname $eventnpc2>> holds onto both of your arms.

    <<link "Submit" EventQuadPartyMilkshakeBulliedRepeatRefuse2>><</link>>

    <<link "No way you're letting this happen!" EventQuadPartyMilkshakeBulliedRepeatRefuse3>><</link>>
<</if>>

/*Passage for when PC refused, and then submitted.*/

:: EventQuadPartyMilkshakeBulliedRepeatRefuse2 [event nobr] {"position":"850,11725","size":"100,100"}
Your arms and face secured, you can only watch as <<anonorfirstname $eventnpc3>> walks up to you with the cup, bringing it to your lips. The smell hits you right away and looking down reveals that there is no milkshake in that cup. Just pure warm, thick cum. Several loads of it, judging by how full the cup is.

<<psc>> <<conj raise>> the cup, tilting it until you feel warm sticky cum lap against your closed lips. <<psc>> <<conj pinch>> your nose, cutting off your breath.

You try and hold out, but it doesn't take long before you have to give in. You part your lips to take in a breath and the thick cum fills your mouth. The guys all laugh and <<anonorfirstname $eventnpc>> covers your mouth, preventing you from spitting the cum out.

Gulp. "Ngh..." you close your eyes and groan as the thick salty-sweet cum slides down your throat. You open your eyes only to see the cup being brought back to your lips. <<dalterneed Humiliation 100 true>>
<<run $pc.swallow_cum()>><<run $pc.swallow_cum()>>

"You've still got a lot more to drink," <<danonorfirstname $eventnpc3>> <<conj say>>. Face burning and knowing you won't be able to resist, you let <<po>> pour more of the cum into your mouth. You feel it pooling at the back of your throat, and with no other course of actions you swallow. <<dalterneed Humiliation 100 true>>

Gulp. Everyone watches your throat spasm as you swallow down the second mouthful of their cum.
<<run $pc.swallow_cum()>><<run $pc.swallow_cum()>>

"Almost done. Be a good <<pet $pc>> and finish your drink," <<ps $eventnpc3>> <<conj say>>. You don't have much choice as the cup is emptied into your mouth.

Gulp. And with that, you've swallowed the last of their cum. <<dalterneed Humiliation 100 true>>
<<run $pc.swallow_cum()>><<run $pc.swallow_cum()>>

"Fuck, that's hot," <<danonorfirstname $eventnpc>> <<conj say>>. "All right, you can go now."

Finding yourself free from their grasp, you quickly leave the room, lest they ask you to do more.

<<trueattracted $eventnpc>><<lust $eventnpc 100>><</trueattracted>>
<<trueattracted $eventnpc2>><<lust $eventnpc2 100>><</trueattracted>>
<<trueattracted $eventnpc3>><<lust $eventnpc3 100>><</trueattracted>>
<<unset $eventnpc>><<unset $eventnpc2>><<unset $eventnpc3>>
<<link "Continue" QuadParty>><</link>>

/*Passage for PC that definetely does not want part of this event.*/

:: EventQuadPartyMilkshakeBulliedRepeatRefuse3 [event nobr] {"position":"975,11725","size":"100,100"}
You kick <<anonorfirstname $eventnpc>> in the shin, causing <<po>> to let go of your face. Caught off guard by your struggle, you are able to free yourself from <<anonorfirstname $eventnpc2>>'s grasp and bolt for the door. <<alterneed Pain 100>>

You leave the dorm room and head out to the quad. It doesn't appear that any of the guys are following you.

<<friendship $eventnpc -50>><<friendship $eventnpc2 -50>><<friendship $eventnpc3 -50>>
<<unset $eventnpc>><<unset $eventnpc2>><<unset $eventnpc3>>
<<link "Continue" QuadParty>><</link>>


code:
/*Watersports event that based off the existing doorless bathroom events at the Friday parties.*/

/*Adjusts EventQuadPartyBathroomDoorless to trigger the new passages with 30% chance to fire if watersports content is enabled, otherwise runs the usual events instead.*/

:: EventQuadPartyBathroomDoorless [event nobr kinkdubcon] {"position":"1225,10725","size":"100,100"}
Seeing the doorless stall available, you push through the line and claim it as your own, hoping nobody here is perverted enough to want to watch you pee.

<<set _standing to $pc.has_part("penis") and setup.people.is_masc($pc, true)>>
<<set _p to setup.Events.pick_person({attitudes: ["lust"], gender: ["male"], inclinations: setup.archetypes.inclination_sets.voyeur, strictinclinations: true, notfriend: true, unknownonly: true})>>

<<if _p is null or State.random() lte 0.5>>
    Luckily you manage to get done what you need to do without incident and quickly make your way back out of the bathroom.

    <<alterneed Bladder 1000>>
    <<continuelink>>
<<elseif _standing>>
    <<if setup.people.mutual_attraction(_p) and State.random() lte 0.3 and $kinkcontent.includes("watersports")>>
        <<set $eventnpc to _p>>
        <<include EventQuadPartyBathroomDoorlessWatersports>>
    <<else>>
        <<anonorfullnamec _p>> abruptly stumbles in, unzips, and stands beside you as <<ps>> <<conj start>> starts peeing into the bowl right next to you. <<psc _p>> <<conj be>> staggeringly drunk and hardly <<conj seem>> to notice you're there. Guess that's a time-saver.
        <<if setup.people.attracted_enough_to_pc(_p)>><<lust _p 10 15>><</if>>

        <<alterneed Bladder 1000>>
        <<if setup.people.mutual_attraction(_p)>>
            You find yourself glancing down at <<pp>> cock, and realize <<pss>> sneaking glances at you too. <<ppc>> cock twitches, beginning to stiffen.

            <<set $eventnpc to _p>>
            <<skillgate Disinhibition 4 "Let <<po>> finish in your mouth" EventQuadPartyBathroomDoorlessWankerMouth>><<set $header to "You interrupt your flow just long enough to turn and sit down instead, putting yourself at eye level with the swelling cock.">><</skillgate>>
            <<skillgate Voyeurism 1 "Look at it" EventQuadPartyBathroomDoorlessWanker>><<set $header to "You interrupt your flow just long enough to turn and sit down instead, putting yourself at eye level with the swelling cock.">><</skillgate>>
            <<link "Ignore it" EventQuadPartyBathroomDoorlessWankerStandingIgnore>><<alterneed Humiliation 100>><</link>> <<dalterneed Humiliation 100>>
        <<else>>
            <<continuelink>>
        <</if>>
    <</if>>
<<else>>
    You <<= setup.and($pc.clothing_displacements_to_expose("crotch", 2))>><<if $pc.wearing_underwear()>> and pull down your <<underwear $pc>><</if>> as you sit down on the toilet.

    You've just begun peeing when <<anonorfullname _p>> abruptly stumbles in, already unzipping. <<psc _p>> <<conj freeze>> when <<ps>> <<conj see>> you there.

    "Hey, I'm in here!" you say, blocking <<pp>> view with your hands.

    "I know," <<ps>> <<conj say>>, then <<conj laugh>>. "Well, I thought I saw a guy come in here. I was wrong."

    "Get out!"

    <<if !setup.people.mutual_attraction(_p)>>
        "Right," <<ps>> <<conj say>>, backing out. "Sorry."

        Fucking drunk people. You manage to complete your task without further incident.<<alterneed Bladder 1000>>

        <<continuelink>>
    <<elseif State.random() lte 0.3 and $kinkcontent.includes("watersports")>>
        <<set $eventnpc to _p>>
        <<include EventQuadPartyBathroomDoorlessWatersports>>
    <<else>>
        "I'll go after you," <<ps>> <<conj say>>. <<ppc>> dick is still in <<pp>> hand. "Gonna piss myself if I have to wait any longer." <<pssc>> looking at you, and you see <<pp>> cock twitch as it starts getting hard. "Oops, sorry. At least if it's hard I won't pee."

        <<set $eventnpc to _p>>
        <<skillgate Voyeurism 1 "Look at it" EventQuadPartyBathroomDoorlessWanker>><</skillgate>>
        <<link "Ignore it" EventQuadPartyBathroomDoorlessWankerIgnore>><<alterneed Humiliation 100>><</link>> <<dalterneed Humiliation 100>>
    <</if>>
<</if>>

/*Opening passage for the watersports event. Multiple options depending on PC's disinhibition levels or if they don't wish to continue.*/

:: EventQuadPartyBathroomDoorlessWatersports [event nobr] {"position":"1100,10850","size":"100,100"}
<<set _p to $eventnpc>>
<<if $pc.has_part("penis") and setup.people.is_masc($pc, true)>>
    <<anonorfullnamec _p>> abruptly stumbles in, unzips, and stands beside you, preparing to pee into the bowl right next to you. <<psc _p>> <<conj be>> staggeringly drunk and hardly <<conj seem>> to notice you're there. Guess that's a time-saver.

    <<skillgate Disinhibition 6 "Tell <<po>> to piss in your mouth" EventQuadPartyBathroomDoorlessWatersportsMouth>><<set $header to "You interrupt your flow just long enough to turn and sit down instead, putting yourself at eye level with <<pp>> cock. You part your lips, opening your mouth and looking at <<po>> expectantly.">><</skillgate>>
    <<skillgate Disinhibition 4 "Tell <<po>> to piss on you" EventQuadPartyBathroomDoorlessWatersportsOn>><<set $header to "You interrupt your flow just long enough to turn and sit down instead, putting yourself at eye level with <<pp>> cock.">><</skillgate>>
    <<link "Ignore <<po>>" EventQuadPartyBathroomDoorlessWatersportsIgnore>><</link>>
<<else>>
    "Gonna piss myself if I have to wait any longer," <<ps>> <<conj say>>. <<ppc>> dick is still in <<pp>> hand. "Just spread your legs, I'll make sure not to get any on you."

    <<skillgate Disinhibition 6 "Open your mouth" EventQuadPartyBathroomDoorlessWatersportsMouth>><<set $header to "Instead of answering, you part your lips, opening your mouth and looking at <<po>> expectantly.">><</skillgate>>
    <<skillgate Disinhibition 4 "Tell <<po>> to piss on you instead" EventQuadPartyBathroomDoorlessWatersportsOn>><</skillgate>>
    <<skillgate Disinhibition 2 "Spread your legs" EventQuadPartyBathroomDoorlessWatersportsSpread>><</skillgate>>
    <<link "Get. Out." EventQuadPartyBathroomDoorlessWatersportsOut>><</link>>
<</if>>

/*WS passage for PC sitting on the toilet, spreading legs while NPC pisses between spread legs.*/

:: EventQuadPartyBathroomDoorlessWatersportsSpread [event nobr] {"position":"1225,10850","size":"100,100"}
<<set _p to $eventnpc>>
"Just hurry it up," you say while spreading your legs, exposing your <<bits $pc>> to <<po>>. <<flashcrotch _p>>

"Right, right," <<ps>> <<conj say>>, aiming <<pp>> cock and letting loose. The stream of piss threads between your open legs into the toilet, joining your own stream.

You sit in silence, watching <<anonorfullname _p>> piss in front of you. <<psc>> <<conj appear>> to be focused on trying to avoid getting you wet, made more difficult by how drunk <<ps>> <<conj are>>.
<<if $pc.has_inclination("Golden Showerer")>>
    A part of you even hopes that <<pp>> concentration slips and <<ps>> <<conj piss>> all over you.
<</if>>

Eventually your pee slows to a trickle and then stops, and you don't have to wait much longer until <<pp>> stream stops as well. You grab some toilet paper to clean yourself up with, then hurry out of the stall before this gets any more awkward.<<alterneed Bladder 1000>>

<<unset $eventnpc>>
<<link "Continue" HelleborineHall>><</link>>

/*WS passage for PC sitting on the toilet, letting the NPC piss on them and their clothes.*/

:: EventQuadPartyBathroomDoorlessWatersportsOn [event nobr] {"position":"850,10600","size":"100,100"}
<<set _p to $eventnpc>>
"Why don't you piss on me instead?" you tell <<po>> as you lean back on the toilet seat, keeping your legs closed but baring your chest to <<po>>.

<<if setup.people.has_inclination(_p, "Watersports Enthusiast")>>
    "If that's what you want," <<ps>> <<conj say>> while grinning.
<<else>>
    "Don't blame me then for the mess," <<ps>> <<conj say>>.
<</if>>
Not a moment later, a strong, steady stream of piss escapes <<pp>> cock and lands on you. <<psc>> <<conj shift>> <<pp>> aim, painting your stomach with <<pp>> piss before moving up to your chest.
<<if $pc.wearing_shirt()>>
    Pee soaks into your <<shortshirt $pc>>
    <<run $pc.add_stain($pc.outermost_covering("chest"), "pee")>>
<<elseif $pc.wearing_bra()>>
    Pee soaks into your <<bra $pc>>
    <<run $pc.add_stain($pc.underwear_covering("chest"), "pee")>>
<<elseif $pc.has_breasts()>>
    Pee runs off your <<breasts $pc bare>>
<<else>>
    Pee runs off your chest
<</if>>
and pools between your thighs. <<psc>> soon <<conj bring>> the stream up and onto your face. You close your eyes and keep your lips pressed shut as <<ps>> <<conj drench>> you in the yellow liquid. <<dalterneed Hygiene -600 true>>

You've finished your business well before <<ps $eventnpc>> <<conj have>>, but make no move to stop <<po>>.<<alterneed Bladder 1000>>
<<if $pc.has_inclination("Golden Showerer")>>
    You can't help but feel a flush of arousal from being covered like this. <<dalterneed Arousal 300 true>>
<<else>>
    You sit and wonder what perverse whim made you think to do this. <<dalterneed Humiliation 300 true>>
<</if>>

Soon <<pp>> own stream trickles to a stop and <<ps>> <<conj shake>> the last few drops from <<pp>> dick while staring <<if setup.people.has_inclination(_p, "Watersports Enthusiast")>>lustfully<</if>> at your soaked clothes.

"Damn, you are one dirty <<pet $pc>>," <<ps _p>> <<conj say>>. You just laugh and grab some toilet paper to try and clean up with, then hurry out of the stall before things get awkward.

<<unset $eventnpc>>
<<link "Continue" HelleborineHall>><</link>>

/*WS passage for PC sitting on the toilet, letting the NPC piss in their mouth.*/

:: EventQuadPartyBathroomDoorlessWatersportsMouth [event nobr] {"position":"225,10975","size":"100,100"}
<<set _p to $eventnpc>>
<<alterneed Bladder 1000>>
<<if setup.people.has_inclination(_p, "Watersports Enthusiast")>>
    <<psc>> <<conj grin>> and <<conj adjust>> <<pp>> angle a little, before flooding the inside of your mouth.
<<else>>
    <<psc>> <<conj raise>> an eyebrow at your actions. You only need to wait for just a moment as <<ps>> <<conj adjust>> <<pp>> angle a little before letting loose.
<</if>>
The stream is strong and steady, <<pp>> aim suprisingly accurate for someone so apparently drunk.

Soon, your mouth is filled with <<pp>> piss, but <<ps>> <<conj make>> no sign that <<pss>> done. Left with little choice, you close your mouth, swallowing down the mouthful of bitter pee, all the while letting your face get covered. <<dalterneed Hygiene -400 true>>

Quickly, you open your mouth and <<ps>> <<conj resume>> filling it. Here you are, peeing while getting peed on and in.
<<if $pc.has_inclination("Golden Showerer")>>
    And you can't help but love it. <<dalterneed Arousal 300 true>>
<<else>>
    You wonder what perverse whim made you think to do this. <<dalterneed Humiliation 300 true>>
<</if>>

<<ppc>> stream trickles off and <<anonorfullname _p>> shakes the last few drops from <<pp>> dick while staring <<if setup.people.has_inclination(_p, "Watersports Enthusiast")>>lustfully<</if>> at your piss-filled mouth. You look <<po>> in the eyes and swallow, before opening and showing <<po>> your empty mouth.

"Damn, you are one dirty <<pet $pc>>," <<ps _p>> <<conj say>>. You just laugh and grab some toilet paper to clean up with, then hurry out of the stall before things get awkward.

<<unset $eventnpc>>
<<link "Continue" HelleborineHall>><</link>>

/*WS passage for PC refusing to cooperate with the NPC. If dubcon is enabled, 30% chance the NPC just pisses on PC anyways.*/

:: EventQuadPartyBathroomDoorlessWatersportsOut [event nobr] {"position":"350,10975","size":"100,100"}
<<set _p to $eventnpc>>
<<if State.random() lte 0.3 and $kinkcontent.includes("dubcon")>>
    "Too late," <<ps>> <<conj say>>.

    "Wait, don't—" you try to say before a strong, steady stream of piss escapes <<pp>> cock and lands on you. <<psc>> <<conj shift>> <<pp>> aim, painting your stomach with <<pp>> piss before moving up to your chest.
    <<if $pc.wearing_shirt()>>
        Pee soaks into your <<shortshirt $pc>>
        <<run $pc.add_stain($pc.outermost_covering("chest"), "pee")>>
    <<elseif $pc.wearing_bra()>>
        Pee soaks into your <<bra $pc>>
        <<run $pc.add_stain($pc.underwear_covering("chest"), "pee")>>
    <<elseif $pc.has_breasts()>>
        Pee runs off your <<breasts $pc bare>>
    <<else>>
        Pee runs off your chest
    <</if>>
    and pools between your thighs.

    <<psc>> <<conj bring>> the stream up and over your face. You close your eyes and keep your lips pressed shut while bringing your hands up to try and block <<pp>> pee. There is little you can do, and you end up just sitting there on the toilet as the stranger drenches you in <<pp>> piss. <<dalterneed Hygiene -600 true>>
    <<if $pc.has_inclination("Golden Showerer")>>
        You can't help but feel a flush of arousal from being covered like this. <<dalterneed Arousal 300 true>>
    <<else>>
        <<dalterneed Humiliation 300 true>>
    <</if>>

    "Fuck, I really needed to let that out," <<pp>> <<conj say>>, shaking the last few drops from <<pp>> cock before tucking it away and leaving the stall. You grab some toilet paper to try and clean up with, then hurry out of the stall before someone else decides to use you as a toilet.
<<else>>
    "Okay, okay, I'm leaving," <<ps>> <<conj say>>, backing out.

    Fucking drunk people. You manage to complete your task without further incident.<<alterneed Bladder 1000>>
<</if>>

<<unset $eventnpc>>
<<link "Continue" HelleborineHall>><</link>>

/*WS passage for PC ignoring the NPC if they started out standing.*/

:: EventQuadPartyBathroomDoorlessWatersportsIgnore [event nobr] {"position":"850,10600","size":"100,100"}
<<set _p to $eventnpc>>
You turn your head away, ignoring the <<boygirl _p>> as <<ps>> <<conj piss>> into the bowl right next to you. It doesn't take long until your bladder finally empties. You fix your clothing and exit the stall before anything awkward can happen.<<alterneed Bladder 1000>>

<<unset $eventnpc>>
<<link "Continue" HelleborineHall>><</link>>

repo_name: narcolepsy/CoT

license: unknown


code:
/*Addition/adjustment to the Chill hangout spooning event. Moves the existing escate option from Disinhibition 1 to Disinhibition 2, and adds a new Disinhibition 1 option.*/

/*Changes the existing link to Disinhibition 2, and adds the new link.*/

 :: EventHangoutChillWatchSpoon2 [event hangout nobr] {"position":"850,24350","size":"100,100"}
You're about halfway through the movie.
<<if setup.people.has_any_inclination($eventnpc, "active") and setup.people.willing_sex($eventnpc) and setup.people.allow_free_interaction($eventnpc)>>
    You feel <<firstname $eventnpc>> shifting against you a little, then <<pp>> hips give a slow roll, <<if $hangout.littlespoon>>rubbing <<pr>> against your rear<<else>>rubbing <<pp>> rear back against you<</if>>.

    <<skillgate Disinhibition 2 "Go with it" EventHangoutChillWatchSpoonEscalate>><</skillgate>>
    <<skillgate Disinhibition 1 "Let <<po>> continue" EventHangoutChillWatchSpoonPassive>><</skillgate>>
    <<set _linkname to "Put " + setup.people.pronouns($eventnpc).po + " off">>
    <<link _linkname EventHangoutChillWatchSpoonEscalateReject>><</link>>
<<else>>
    You're conscious of <<firstname $eventnpc>> <<if $hangout.littlespoon>>against your back<<else>>against your chest<</if>>. You could probably start something with a little grinding...

    <<skillgate Disinhibition 3 "Make a move" EventHangoutChillWatchSpoonEscalateOffer>><</skillgate>>
    <<link "Just watch the movie" EventHangoutChillWatchSpoonDontEscalate>><</link>>
<</if>>

/*New event where NPC and PC grind against each other, but don't contine to sex. Big spoon position ends up cumming.*/

:: EventHangoutChillWatchSpoonPassive [event hangout nobr] {"position":"100,24475","size":"100,100"}
You lay against <<pp>> <<if $hangout.littlespoon>>chest <<else>>back <</if>> as <<ps>> continues to grind against you, occasionally rolling your hips against <<po>>. The movie is mere background noise as you both continue to enjoy the feeling of each other's bodies.

It doesn't take long before <<if $hangout.littlespoon>>you can feel <<po>> shuddering behind you before going still. <<dalterneed Arousal 300 true>><<else>>you are brought to the edge and your <<cum $pc>> <<if $pc.has_part("penis")>>sprays into your <<if $pc.wearing_underwear()>><<shortunderwear $pc>><<else>><<shortpants $pc>><</if>><<else>><<if $pc.has_inclination("Squirter")>>squirts<<else>>trickles<</if>> <<if $pc.wearing_underwear()>>into your <<shortunderwear $pc>><<elseif $pc.under_access()>>down your thigh from underneath your <<shortpants $pc>><<else>>into your <<shortpants $pc>><</if>><</if>><<run $pc.add_stain($pc.innermost_covering("crotch"), "cum")>><</if>>

It may not exactly be what <<ps>> wanted, but <<ps>> doesn't seem to mind.
<<set $hangout.heat += 2>>

<<link "Next">><<gotonexthangoutevent>><<advtime 60>><</link>>


sourceType: module

semi-spacing: warn

semi-style: warn

eqeqeq: error

no-fallthrough: error

camelcase: warn

comma-spacing: error

comma-style: error

object-curly-newline: off

object-curly-spacing: error

no-var: error

spaced-comment: warn

no-undef: off

no-unused-vars: error

block-scoped-var: warn

dot-notation: off

linebreak-style: error unix

eol-last: warn

unicode-bom: error never

repo_name: Fekenol/fc


code:
*.bat	text eol=crlf
*.js	text eol=lf
*.tw	text eol=lf
*.sh	text eol=lf
*.py	text eol=lf
*.txt	text eol=lf

path: .gitattributes


code:
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.swp
*.swo

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
bin/*

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# Start.tw
src/config/start.tw

# eslint
node_modules
package-lock.json
package.json

# misc
fc-pregmod
*.outline
*.todo
TODO.txt
.vscode/settings.json
*.code-workspace

path: .gitignore


code:
Common problems:

How do I start the game?
-Run the compile file, go to folder "bin", click the "FC_Pregmod" and play. (Recommendation: Drag it into incognito mode)

I get an error on gamestart reading "Apologies! A fatal error has occurred. Aborting. Error: Unexpected token @ in JSON at position 0. Stack Trace: SyntaxError: Unexpected token @ in JSON at position 0 at JSON.parse (<anonymous>) at JSON.value" or some variant
-clear cookies

Everything is broken!
-Do not copy over your existing download as it may leave old files behind, replace it entirely

I can't save more than once or twice.
-Known issue caused by sugarcube level changes. Save to file doesn't have this problem and will likely avoid the first problem as well.
-It is possible to increase the memory utilized by your browser to delay this

I wish to report a sanityCheck issue.
-Great, however a large majority of the results are false positives coming from those specific sections being split over several lines in the name of readability and git grep's intentionally (http://git.661346.n2.nabble.com/bug-git-grep-P-and-multiline-mode-td7613900.html ) lacking support for multiline. An Attempt to add -Pzl (https://gitgud.io/pregmodfan/fc-pregmod/merge_requests/2108 ) created a sub condition black hole. What follows are examples of common false positives that can safely be ignored;

	[MissingClosingAngleBracket]src/art/vector/Generate_Stylesheet.tw:11:<<print "<style>."+_art_display_class+" {
		<<print "<style>."+_art_display_class+" {
	position: absolute;
	height: 100%;
	margin-left: auto;
	margin-right: auto;
	left: 0;
	right: 0;
	}

How to mod (basic doc):

1. All sources now in the src subdir, in separate files. 1 passage = 1 file.

2. Special files and dir's:
	- src/config		- configuration of the story is here.
	- src/config/start.tw	- contains list of .tw passage files, regenerated automatic, by building scripts. Do not change by hands. (original passage Start from pregmod renamed and moved to src/events/intro/introSummary.tw)
	- src/js/storyJS.tw		- special passage with [script] tag - contain all native JavaScript from pregmod.
	- devTools/tweeGo/targets/sugarcube-2/userlib.js - on original FC JS moved here (I deleted it after moving JS to storyJS.tw). Compare to storyJS.tw but do not copy file here. May conflict with src/js/storyJS.tw if copied.
	- src/pregmod		- I put all pregmod-only passages here.
	- .gitignore		- special file for git - to ignore some files. For example - compilation results.

3. Compilation:

	Windows:
	Run compile.bat - result will be file bin/FC_pregmod.html
	Second run of compile.but will overwrite bin/FC_pregmod.html without prompt.

	Linux:
	Ensure executable permission on file "devTools/tweeGo/tweego" (not tweego.exe!)
	Ensure executable permission on file "compile.sh"
	In the root dir of sources (where you see src, devTools, bin...) run command "./compile.sh" from console

	Mac:
	Not supported directly (I don't have access to Mac for testing).
	But you can use linux compilation script if you download tweego for mac from here: https://bitbucket.org/tmedwards/tweego/downloads/ and replace linux executable with mac executable in ./devTools/tweeGo/ folder. This is not tested though, so be warned.

4. Simple comparing and merging with original FC:

	Use meld tool. Place folder FreeCities (original FC sources tree) near FreeCitiesPregmod (this sources tree) and use command:
	meld FreeCities FreeCitiesPregmod
	or just select these folders in meld's GUI.

5. All modders will be very grateful if anyone who makes some changes to game with .html file also post their resulting src folder tree.

6. For contributors to pregmod: if you don't use git, then you need to post your version of src folder tree, not just produced FC_pregmod.html file!!! This html file can't be reverted to proper sources, and useless as contribution!

7. Git workflow:
	- Master branch is pregmod-master. Only Pregmodder can add something to it directly. Always contain his last public changes.
	- pregmod-dev - branch with experimental code mainly by pregmodfan.
	- Any contributions will be placed in separate branches like pregmod-mod-<something> (if it's ready to merge with master complete feature/mod) or pregmod-contrib-<something> if it's partial work until contributions is reviewed.

	Typical cycle with git:
	1. Make account on gitgud if you don't have usable one.
	2. Fork main repository through gitgud interface. (Or pull changes from main repo if you already have fork.)
	3. Clone your fork to local machine with git client (Or pull changes if already cloned.)
	4. Make you changes as you like, commit, and push result into your forked repository (with git client).
	5. Make merge request through gitgud interface.


code:
Further Development:
- specialized slave schools
- fortifications
- more levels for militia edict (further militarize society)
- conquering other arcologies?

Events:
- famous criminal escapes to the arcology, followed by another arcology's police force

Bugs:
- sometimes troop counts breaks
- sometimes rebel numbers have fractionary parts

Rules Assistant:
- find a way for the new intense drugs to fit in
- everything mentioned in https://gitgud.io/pregmodfan/fc-pregmod/issues/81

main.tw porting:
- slaveart
- createsimpletabs
- displaybuilding
- optionssortasappearsonmain
- resetassignmentfilter
- mainlinks
- arcology description
- office description
- slave summary
- use guard
- toychest
- walk past


code:
# How to vector art

Please read the whole document before starting to work.
Some aspects are not obvious right away.

Note: This does not actually describe how to be an artist.

## TL;DR

	killall inkscape
	artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
	compile
	python3 artTools/normalize_svg.py artTools/vector_source.svg
	git commit -a

## 1. Be an artist

Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.

## 2. Respect the structure

While editing, keep the Layers in mind.

* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-defined IDs.
* All Layers should have an ID that is globally unique
  (not just within their subtree).

* Please use anonymous groups only for the ease of editing. Remove all "helper" groups before finally saving the file.
* Anonymous groups can be used for continuous scaling (of e.g. boobs).

* Every asset that should go into a separate file needs to be in a labelled group
  (even if that is a group with only one shape).
* There are some globally available styles defined as CSS classes (e.g. skin, hair).
  Use them if your asset should be changed upon display.
  Do not set the style directly in the object.

## 3. Normalize the document (before committing)

**THIS IS IMPORTANT**

The various editors out there (Inkscape, Illustrator) may use different variants of formatting the SVG XML.
Use

	python3 normalize_svg.py vector_source.svg

before committing to normalize the format so git will not freak out about the changed indentation.

If you use Inkscape, please use in Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
In case your Editor uses another path data style which cannot be changed, please contact the other artists and developers via the issue tracker to find a new common ground.

What it does:
* Formats the SVG XML according to Pythons lxml module, regardless of editor.
* Adobe Illustrator uses group IDs as layer labels.
  Inkscape however uses layer labels and a separate, auto-generated group ID.
  normalize_svg.py overwrites the group ID with the Inkscape layer label
  so they are synchronized with Inkscape layer labels.
* Inkscape copies the global style definition into the object *every time*
  the object is edited. If an object references a CSS class AND at the same time
  has a local style, the local style is removed
  so global dynamic styling is possible later on.

Note: Behavior of Adobe Illustrator is untested.

## 4. Split the layers

For NoX original, deepmurk extensions, execute

	python3 vector_layer_split.py vector_deepmurk_primary.svg tw ../src/art/vector/layers/

For faraen revamped art (based on NoX original)

	python3 vector_revamp_layer_split.py vector_revamp_source.svg tw ../src/art/vector_revamp/layers/

. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
The group ID sets the file name. Therefore, the group ID **must not** contain spaces or any other weird characters.

Also consider:
* A group with ID ending in _ is ignored. Use Layers ending in _ to keep overview.
* A group with ID starting with "g" (Inkscape) or "XMLID" (Illustrator) is also ignored.
* Existing files are overwritten **without enquiry**.
* The target directory is not emptied. If a file is no longer needed, you should remove it manually.
* This procedure removes global definitions. This means, SVG filters are currently not supported.

Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
`tw` embeds the SVG data into Twine files, but removes the global style definitions so they can be set during display.

## 5. Edit the code

`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
The code also generates the previously removed global style definitions on the fly and per display.

## 6. Compile the story

Use the provided compile script (Windows batch or shell) for compiling.

## 7. Advanced usage

You can define multiple CSS classes to one element, e.g. "skin torso". When procedurally generating CSS, you can then set a global "skin" style, and a different one for "torso".

You can put variable text into the image using single quotes, e.g. "'+_tattooText+'". The single quotes *break* the quoting in Twine so the content of the Twine variable `_tattooText` is shown upon display. You can even align text on paths.

An anonymous group can be used for dynamic transformation. However, finding the correct parameters for the affine transformation matrix can be cumbersome. Use any method you see fit. See `src/art/vector/Boob.tw` for one example of the result.


code:
Apron
AttractiveLingerie
AttractiveLingerieForAPregnantWoman
BallGown
Battledress
Blouse
BodyOil
Bunny
Chains
ChattelHabit
Cheerleader
ClubslutNetting
ComfortableBodysuit
Conservative
Cutoffs
Cybersuit
FallenNunsHabit
FuckdollSuit
HalterTopDress
HaremGauze
Hijab
Huipil
Kimono
LatexCatsuit
Leotard
LongQipao
MaternityDress
MilitaryUniform
MiniDress
Monokini
NiceBusinessAttire
NiceMaid
NiceNurse
No
PenitentNunsHabit
RedArmyUniform
RestrictiveLatex
ScalemailBikini
SchoolgirlUniform
SchutzstaffelUniform
ShibariRopes
SlaveGown
Slutty
SluttyBusinessAttire
SluttyJewelry
SluttyMaid
SluttyNurse
SluttyQipao
SluttySchutzstaffelUniform
Spats
StretchPants
StringBikini
Succubus
Toga
UncomfortableStraps
Western


code:
#!/bin/bash

# This script reads all possible values of $slave.clothes as mentioned by the documentation.
# This script uses the actual implementation of the JS clothing2artSuffix function as defined in src/art/vector/Helper_Functions.tw
# This script outputs suffixes to be used in the SVG naming scheme
# This script is meant to be executed at the project root directory.
# This script depends on bash, grep, sed, paste and nodejs (so best executed on Linux, I guess)

# grep -Porh '(?<=\.clothes = )"[^"]+"' src/ | sort | uniq # searches sources for all clothes strings actually used in the game, even undocumented ones

(
  echo 'var window = {};'
  grep -v '^:' src/art/vector/Helper_Functions.tw
  echo -n 'Array('
  sed '/^clothes:/,/:/!d' "slave variables documentation - Pregmod.txt" | grep '"' | paste -sd,
  echo ').forEach(v => {console.log(window.clothing2artSuffix(v));});'
) | nodejs | sort


code:
#!/usr/bin/env python3

'''
Application for "normalizing" SVGs

These problems are addressed:

* Inkscape notoriously copies class styles into the object definitions.
https://bugs.launchpad.net/inkscape/+bug/167937

* Inkscape uses labels on layers. Layers are basically named groups.
  Inkscape does not sync the group id with the layer label.

Usage Example:
python3 inkscape_svg_fixup.py vector_source.svg
'''

import lxml.etree as etree
import sys

def fix(tree):
  # know namespaces
  ns = {
	'svg' : 'http://www.w3.org/2000/svg',
	'inkscape' : 'http://www.inkscape.org/namespaces/inkscape'
  }

  # find document global style definition
  # mangle it and interpret as python dictionary
  style_element = tree.find('./svg:style',namespaces=ns)
  style_definitions = style_element.text
  pythonic_style_definitions = '{'+style_definitions.\
  replace('\t.','"').replace('{','":"').replace('}','",').\
  replace('/*','#')+'}'
  styles = eval(pythonic_style_definitions)

  # go through all SVG elements
  for elem in tree.iter():
	if (elem.tag == etree.QName(ns['svg'], 'g')):
	  # compare inkscape label with group element ID
	  l = elem.get(etree.QName(ns['inkscape'], 'label'))
	  if l:
		i = elem.get('id')
		if (i != l):
		  print("Overwriting ID %s with Label %s..."%(i, l))
		  elem.set('id', l)

	# clean styles (for easier manual merging)
	style_string = elem.get('style')
	if style_string:
	  split_styles = style_string.strip('; ').split(';')
	  styles_pairs = [s.strip('; ').split(':') for s in split_styles]
	  filtered_pairs = [ (k,v) for k,v in styles_pairs if not (
		k.startswith('font-') or
		k.startswith('text-') or
		k.endswith('-spacing') or
		k in ["line-height", " direction", " writing", " baseline-shift", " white-space", " writing-mode"]
	  )]
	  split_styles = [':'.join(p) for p in filtered_pairs]
	  style_string = ';'.join(sorted(split_styles))
	  elem.attrib["style"] = style_string

	# remove all style attributes offending class styles
	s = elem.get('style')
	c = elem.get('class')
	if (c and s):
	  s = s.lower()
	  c = c.split(' ')[0] # regard main style only
	  cs = ''
	  if c in styles:
		cs = styles[c].strip('; ').lower()
	  if (c not in styles):
		print("Object id %s references unknown style class %s."%(i,c))
	  else:
		if (cs != s.strip('; ')):
		  print("Style %s removed from object id %s differed from class %s style %s."%(s,i,c,cs))
		del elem.attrib["style"]

if __name__ == "__main__":
  input_file = sys.argv[1]
  tree = etree.parse(input_file)
  fix(tree)
  # store SVG into file (input file is overwritten)
  svg = etree.tostring(tree, pretty_print=True)
  with open(input_file, 'wb') as f:
	f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
	f.write(svg)


code:
#!/usr/bin/env python3

'''
Application for procedural content adaption
*THIS IS VERY EXPERIMENTAL*
Contains a very poor man's implementation of spline mesh warping.

This application parses SVG path data of an outfit sample aligned to a body part.
The outfit is then replicated for other shapes of the same body part.

Example data is geared towards generating a strap outfit for boobs and torso
for all sizes of boobs and all shapes of torsos based on a single outfit for
boobs of size 2 and a hourglass torso respectively.

Limitations:
* handles paths only
* only svg.path Line and CubicBezier are tested
* only the aforementioned examples are tested

Usage:
python3 vector_clothing_replicator.py infile clothing bodypart destinationfile

Usage Example:
python3 vector_clothing_replicator.py vector_source.svg Straps Boob vector_destination.svg
python3 vector_clothing_replicator.py vector_source.svg Straps Torso vector_destination.svg
'''

from svg.path import parse_path
import copy
import lxml.etree as etree
import sys

REFERENCE_PATH_SAMPLES = 200
EMBED_REPLICATIONS = True # whether to embed all replications into the input file or output separate files

input_file = sys.argv[1]
clothing = sys.argv[2]
bodypart = sys.argv[3]
output_file_embed = sys.argv[4]

# TODO: make these configurable
output_file_pattern = '%s_%s_%s.svg' #bodypart, target_id, clothing

if ('Torso' == bodypart):
  xpath_shape = './svg:g[@id="Torso_"]/svg:g[@id="Torso_%s"]/svg:path[@class="skin torso"]/@d' # TODO: formulate more general, independent of style
  xpath_outfit_container = '//svg:g[@id="Torso_Outfit_%s_"]'%(clothing)
  xpath_outfit = '//svg:g[@id="Torso_Outfit_%s_%s"]'%(clothing,'%s')
  target_ids = "Unnatural,Hourglass,Normal".split(",")
  reference_id = "Hourglass"
else:
  raise RuntimeError("Please specify a bodypart for clothing to replicate.")

tree = etree.parse(input_file)
ns = {'svg' : 'http://www.w3.org/2000/svg'}

canvas = copy.deepcopy(tree)
for e in canvas.xpath('./svg:g',namespaces=ns)+canvas.xpath('./svg:path',namespaces=ns):
  # TODO: this should be "remove all objects, preserve document properties"
  e.getparent().remove(e)

def get_points(xpath_shape):
  '''
  This function extracts reference paths by the given xpath selector.
  Each path is used to sample a fixed number of points.
  '''
  paths_data = tree.xpath(xpath_shape,namespaces=ns)
  points = []
  path_length = None
  for path_data in paths_data:
	p = parse_path(path_data)
	points += [
	  p.point(1.0/float(REFERENCE_PATH_SAMPLES)*i)
	  for i in range(REFERENCE_PATH_SAMPLES)
	]
  if (not points):
	raise RuntimeError(
	  'No paths for reference points found by selector "%s".'%(xpath_shape)
	)
  return points

def point_movement(point, reference_points, target_points):
  '''
  For a given point, finds the nearest point in the reference path.
  Gives distance vector from the nearest reference point to the
  respective target reference point.
  '''
  distances = [abs(point-reference_point) for reference_point in reference_points]
  min_ref_dist_idx = min(enumerate(distances), key=lambda x:x[1])[0]
  movement = target_points[min_ref_dist_idx] - reference_points[min_ref_dist_idx]
  return movement

reference_points = get_points(xpath_shape%(reference_id))
container = tree.xpath(xpath_outfit_container,namespaces=ns)
if (len(container) != 1):
  raise RuntimeError('Outfit container selector "%s" does not yield exactly one layer.'%(xpath_outfit_container))
container = container[0]
outfit_source = container.xpath(xpath_outfit%(reference_id),namespaces=ns)
if (len(outfit_source) != 1):
  raise RuntimeError('Outfit source selector "%s" does not yield exactly one outfit layer in container selected by "%s".'%(xpath_outfit%(reference_id), xpath_outfit_container))
outfit_source = outfit_source[0]

for target_id in target_ids:
  print(
	'Generating variant "%s" of clothing "%s" for bodypart "%s"...'%
	(target_id, clothing, bodypart)
  )
  outfit = copy.deepcopy(outfit_source)
  paths = outfit.xpath('./svg:path',namespaces=ns)
  if target_id == reference_id:
	print("This is the source variant. Skipping...")
  else:
	layerid = outfit.get('id').replace('_%s'%(reference_id),'_%s'%(target_id))
	outfit.set('id', layerid)
	outfit.set(etree.QName('http://www.inkscape.org/namespaces/inkscape', 'label'), layerid) # for the Inkscape-users
	target_points = get_points(xpath_shape%(target_id))
	if (len(reference_points) != len(target_points)):
	  raise RuntimeError(
		('Different amounts of sampled points in reference "%s" and target "%s" paths. '+
		'Selector "%s" probably matches different number of paths in the two layers.')%
		(reference_id, target_id, xpath_shape)
	  )
	for path in paths:
	  path_data = path.get("d")
	  p = parse_path(path_data)
	  for segment in p:
		original_distance = abs(segment.end-segment.start)
		start_movement = point_movement(segment.start, reference_points, target_points)
		segment.start += start_movement
		end_movement = point_movement(segment.end, reference_points, target_points)
		segment.end += end_movement
		distance = abs(segment.end-segment.start)
		try:
		  # enhance position of CubicBezier control points
		  # amplification is relative to the distance gained by movement
		  segment.control1 += start_movement
		  segment.control1 += (segment.control1-segment.start)*(distance/original_distance-1.0)
		  segment.control2 += end_movement
		  segment.control2 += (segment.control2-segment.end)*(distance/original_distance-1.0)
		except AttributeError as ae:
		  # segment is not a CubicBezier
		  pass
	  path.set("d", p.d())
	if EMBED_REPLICATIONS:
	  container.append(outfit)
  if not EMBED_REPLICATIONS:
	container = copy.deepcopy(canvas).xpath('.',namespaces=ns)[0]
	container.append(outfit)

  if not EMBED_REPLICATIONS:
	svg = etree.tostring(container, pretty_print=True)
	with open((output_file_pattern%(bodypart, target_id, clothing)).lower(), 'wb') as f:
	  f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
	  f.write(svg)

if EMBED_REPLICATIONS:
  svg = etree.tostring(tree, pretty_print=True)
  with open(output_file_embed, 'wb') as f:
	f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
	f.write(svg)


code:
#!/usr/bin/env python3

'''
Application for splitting groups from one SVG file into separate files

Usage:
python3 vector_layer_split.py infile format outdir

Usage Example:
python3 vector_layer_split.py vector_source.svg tw ../src/art/vector/layers/
'''

import lxml.etree as etree
import sys
import os
import copy
import re
import normalize_svg

input_file = sys.argv[1]
output_format = sys.argv[2]
output_directory = sys.argv[3]
if not os.path.exists(output_directory):
	os.makedirs(output_directory)

ns = {
  'svg' : 'http://www.w3.org/2000/svg',
  'inkscape' : 'http://www.inkscape.org/namespaces/inkscape',
  'sodipodi':"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
}

tree = etree.parse(input_file)
normalize_svg.fix(tree)

# prepare output template
template = copy.deepcopy(tree)
root = template.getroot()

# remove all svg root attributes except document size
for a in root.attrib:
  if (a != "viewBox"):
	del root.attrib[a]

# add placeholder for CSS class (needed for workaround for non HTML 5.1 compliant browser)
if output_format == 'tw':
  root.attrib["class"] = "'+_art_display_class+'"

# remove all content, including metadata
# for twine output, style definitions are removed, too
defs = None
for e in root:
  if (e.tag == etree.QName(ns['svg'], 'defs')):
	defs = e
  if (e.tag == etree.QName(ns['svg'], 'g') or
	  e.tag == etree.QName(ns['svg'], 'metadata') or
	  e.tag == etree.QName(ns['svg'], 'defs') or
	  e.tag == etree.QName(ns['sodipodi'], 'namedview') or
	  (output_format == 'tw' and e.tag == etree.QName(ns['svg'], 'style'))
	  ):
	root.remove(e)
# template preparation finished

# prepare regex for later use
regex_xmlns = re.compile(' xmlns[^ ]+',)
regex_space = re.compile('[>][ ]+[<]',)

# find all groups
layers = tree.xpath('//svg:g',namespaces=ns)
for layer in layers:
  i = layer.get('id')
  if ( # disregard non-content groups
	i.endswith("_") or # manually suppressed with underscore
	i.startswith("XMLID") or # Illustrator generated group
	i.startswith("g") # Inkscape generated group
  ):
	continue
  # create new canvas
  output = copy.deepcopy(template)
  # copy all shapes into template
  canvas = output.getroot()
  for e in layer:
	canvas.append(e)
  # represent template as SVG (binary string)
  svg = etree.tostring(output, pretty_print=False)
  # poor man's conditional defs insertion
  # TODO: extract only referenced defs (filters, gradients, ...)
  # TODO: detect necessity by traversing the elements. do not stupidly search in the string representation
  if ("filter:" in svg.decode('utf-8')):
	# it seems there is a filter referenced in the generated SVG, re-insert defs from main document
	canvas.insert(0,defs)
	# re-generate output
	svg = etree.tostring(output, pretty_print=False)

  if (output_format == 'tw'):
	# remove unnecessary attributes
	# TODO: never generate unnecessary attributes in the first place
	svg = svg.decode('utf-8')
	svg = regex_xmlns.sub('',svg)
	svg = svg.replace(' inkscape:connector-curvature="0"','') # this just saves space
	svg = svg.replace('\n','').replace('\r','') # print cannot be multi-line
	svg = regex_space.sub('><',svg) # remove indentation
	svg = svg.replace('svg:','') # svg namespace was removed
	if ("Boob" in i): # internal groups are used for scaling
	  svg = svg.replace('<g ','<g transform="\'+_artTransformBoob+\'"') # boob art uses the boob scaling
	elif ("Belly" in i):
	  svg = svg.replace('<g ','<g transform="\'+_artTransformBelly+\'"') # belly art uses the belly scaling
	else:
	  svg = svg.replace('<g ','<g transform="\'+_art_transform+\'"') # otherwise use default scaling
	svg = svg.encode('utf-8')

  # save SVG string to file
  i = layer.get('id')
  output_path = os.path.join(output_directory,i+'.'+output_format)
  with open(output_path, 'wb') as f:
	if (output_format == 'svg'):
	  # Header for normal SVG (XML)
	  f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
	  f.write(svg)
	elif (output_format == 'tw'):
	  # Header for SVG in Twine file (SugarCube print statement)
	  f.write((':: Art_Vector_%s [nobr]\n\n'%(i)).encode("utf-8"))
	  f.write("<<print '<html>".encode("utf-8"))
	  f.write(svg)
	  f.write("</html>' >>".encode("utf-8"))


code:
#!/usr/bin/env python3

'''
Application for splitting groups from one SVG file into separate files

Usage:
python3 vector_layer_split.py infile format outdir

Usage Example:
python3 vector_layer_split.py vector_source.svg tw ../src/art/vector/layers/
'''

import lxml.etree as etree
from lxml.etree import XMLParser, parse

import sys
import os
import copy
import re
import normalize_svg

input_file = sys.argv[1]
output_format = sys.argv[2]
output_directory = sys.argv[3]

if not os.path.exists(output_directory):
	os.makedirs(output_directory)

ns = {
	'svg': 'http://www.w3.org/2000/svg',
	'inkscape': 'http://www.inkscape.org/namespaces/inkscape',
	'sodipodi': "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
}

p = XMLParser(huge_tree=True)
tree = parse(input_file, parser=p)
#tree = etree.parse(input_file)
normalize_svg.fix(tree)

# prepare output template
template = copy.deepcopy(tree)
root = template.getroot()

# remove all svg root attributes except document size
for a in root.attrib:
	if (a != "viewBox"):
		del root.attrib[a]

# add placeholder for CSS class (needed for workaround for non HTML 5.1 compliant browser)
if output_format == 'tw':
	root.attrib["class"] = "'+_art_display_class+'"

# remove all content, including metadata
# for twine output, style definitions are removed, too
defs = None
for e in root:
	if (e.tag == etree.QName(ns['svg'], 'defs')):
		defs = e
	if (e.tag == etree.QName(ns['svg'], 'g') or
			e.tag == etree.QName(ns['svg'], 'metadata') or
			e.tag == etree.QName(ns['svg'], 'defs') or
			e.tag == etree.QName(ns['sodipodi'], 'namedview') or
			(output_format == 'tw' and e.tag == etree.QName(ns['svg'], 'style'))
	):
		root.remove(e)
# template preparation finished

# prepare regex for later use
regex_xmlns = re.compile(' xmlns[^ ]+', )
regex_space = re.compile('[>][ ]+[<]', )
regex_transformValue = re.compile('(?<=transformVariableName=")[^"]*', )
regex_transformVar = re.compile('transformVariableName="[^"]*"', )
regex_transformAttr = re.compile('transform="[^"]*"', )
# find all groups
layers = tree.xpath('//svg:g', namespaces=ns)
for layer in layers:
	i = layer.get('id')
	if (  # disregard non-content groups
			i.endswith("_") or  # manually suppressed with underscore
			i.startswith("XMLID") or  # Illustrator generated group
			i.startswith("g")  # Inkscape generated group
	):
		continue
	# create new canvas
	output = copy.deepcopy(template)
	# copy all shapes into template
	canvas = output.getroot()
	for e in layer:
		canvas.append(e)
	# represent template as SVG (binary string)
	svg = etree.tostring(output, pretty_print=False)
	# poor man's conditional defs insertion
	# TODO: extract only referenced defs (filters, gradients, ...)
	# TODO: detect necessity by traversing the elements. do not stupidly search in the string representation
	if ("filter:" in svg.decode('utf-8')):
		# it seems there is a filter referenced in the generated SVG, re-insert defs from main document
		canvas.insert(0, defs)
		# re-generate output
		svg = etree.tostring(output, pretty_print=False)
	elif ("clip-path=" in svg.decode('utf-8')):
		# it seems there is a clip path referenced in the generated SVG, re-insert defs from main document
		canvas.insert(0, defs)
		# re-generate output
		svg = etree.tostring(output, pretty_print=False)

	if (output_format == 'tw'):
		# remove unnecessary attributes
		# TODO: never generate unnecessary attributes in the first place
		svg = svg.decode('utf-8')
		svg = regex_xmlns.sub('', svg)
		svg = svg.replace(' inkscape:connector-curvature="0"', '')  # this just saves space
		svg = svg.replace('\n', '').replace('\r', '')  # print cannot be multi-line
		svg = regex_space.sub('><', svg)  # remove indentation
		svg = svg.replace('svg:', '')  # svg namespace was removed

		transformGroups = regex_transformVar.findall(svg)

		if (len(transformGroups) > 0):
			svg = regex_transformAttr.sub('', svg)
			for transformGroup in transformGroups:
				transformValue = regex_transformValue.search(transformGroup)
				if (transformValue is not None):
					svg = svg.replace(transformGroup, ' transform="\'+' + transformValue.group() + '+\'" ')  # internal groups are used for scaling

		svg = svg.encode('utf-8')

	# save SVG string to file
	i = layer.get('id')
	output_path = os.path.join(output_directory, i + '.' + output_format)
	with open(output_path, 'wb') as f:
		if (output_format == 'svg'):
			# Header for normal SVG (XML)
			f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
			f.write(svg)
		elif (output_format == 'tw'):
			# Header for SVG in Twine file (SugarCube print statement)
			f.write((':: Art_Vector_Revamp_%s [nobr]\n\n' % (i)).encode("utf-8"))
			f.write("<<print '<html>".encode("utf-8"))
			f.write(svg)
			f.write("</html>' >>".encode("utf-8"))


code:
@echo off
:: Free Cities Basic Compiler - Windows

:: Set working directory
pushd %~dp0

:: See if we can find a git installation
setlocal enabledelayedexpansion

for %%k in (HKCU HKLM) do (
	for %%w in (\ \Wow6432Node\) do (
		for /f "skip=2 delims=: tokens=1*" %%a in ('reg query "%%k\SOFTWARE%%wMicrosoft\Windows\CurrentVersion\Uninstall\Git_is1" /v InstallLocation 2^> nul') do (
			for /f "tokens=3" %%z in ("%%a") do (
				set GIT=%%z:%%b
				set GITFOUND=yes
				goto FOUND
			)
		)
	)
)
:FOUND
if %GITFOUND% == yes (
	set "PATH=%GIT%bin;%PATH%"
	bash --login -c "./sanityCheck.sh java"
)

:: Compile the game
call "%~dp0compile.bat"

if %GITFOUND% == yes (
	:: Make the output prettier, replacing \t with a tab and \n with a newline
	bash -c "sed -i -e '/^.*<div id=\"store-area\".*$/s/\\\t/\t/g' -e '/^.*<div id=\"store-area\".*$/s/\\\n/\n/g' bin/FC_pregmod.html"

	:: Revert ./src/init/storyInit.tw for next compilation
	git checkout -- ./src/init/storyInit.tw
)

popd
PAUSE


code:
@echo off
:: Free Cities Basic Compiler - Windows

:: Set working directory
pushd %~dp0
if not exist "bin\" mkdir bin
:: Run the appropriate compiler for the user's CPU architecture.
if %PROCESSOR_ARCHITECTURE% == AMD64 (
	CALL "%~dp0devTools\tweeGo\tweego_win64.exe" -o "%~dp0bin/FC_pregmod.html" --head devTools/head.html "%~dp0src"
) else (
	CALL "%~dp0devTools\tweeGo\tweego_win86.exe" -o "%~dp0bin/FC_pregmod.html" --head devTools/head.html "%~dp0src"
)

popd
ECHO Done


code:
#!/bin/bash

output=/dev/stdout

# displays help text
function displayHelp {
  cat << HelpText
Usage: compile.sh [OPTION]...

Options:
  -d, --dry      Do not compile
  -g, --git      Add hash of HEAD to filename
  -h, --help     Show this help text
  -j, --java     Run sanityCheck based on java
  -p, --python   Run sanityCheck based on python
  -q, --quiet    Suppress terminal output
HelpText
}

#display an error message
function echoError {
	echo -e "\033[0;31m$@\033[0m"
}

#display message
function echoMessage {
	echo "$1" > "${output}"
}

#compile the HTML file
function compile {

	mkdir -p bin

	export TWEEGO_PATH=devTools/tweeGo/storyFormats
	TWEEGO_EXE="tweego"

	if hash $TWEEGO_EXE 2>/dev/null; then
		echoMessage "system tweego binary"
	else
		case "$(uname -m)" in
		x86_64|amd64)
			echoMessage "x64 arch"
			if [ "$(uname -s)" = "Darwin" ]; then
				TWEEGO_EXE="./devTools/tweeGo/tweego_osx64"
			else
				TWEEGO_EXE="./devTools/tweeGo/tweego_nix64"
			fi
			;;
		x86|i[3-6]86)
			echoMessage "x86 arch"
			if [ "$(uname -s)" = "Darwin" ]; then
				TWEEGO_EXE="./devTools/tweeGo/tweego_osx86"
			else
				TWEEGO_EXE="./devTools/tweeGo/tweego_nix86"
			fi
			;;
		*)
			echoError "No system tweego binary found, and no precompiled binary for your platform available."
			echoError "Please compile tweego and put the executable in PATH."
			exit 2
		esac
	fi

	if [[ "$usehash" ]]; then
		HASH="$(git rev-list -n 1 --abbrev-commit HEAD)"

		$TWEEGO_EXE -o "bin/FC_pregmod_${HASH}_tmp.html" src/ --head devTools/head.html || build_failed="true"

		if [ "$build_failed" = "true" ]
		then
			echoError "Build failed."
			exit 1
		fi

		#Make the output prettier, replacing \t with a tab and \n with a newline
		sed -i -e '/^<div id="store-area".*$/s/\\t/\t/g' -e '/^
 /dev/null

		echoMessage "Saved to bin/FC_pregmod_$HASH.html."

	else
		# Find and insert current commit
		COMMIT=$(git rev-parse --short HEAD)
		sed -Ei "s/build .releaseID/\0 commit $COMMIT/" src/gui/mainMenu/AlphaDisclaimer.tw

		$TWEEGO_EXE -o "bin/FC_pregmod_tmp.html" src/ --head devTools/head.html || build_failed="true"

		# Revert AlphaDisclaimer for next compilation
		git checkout -- src/gui/mainMenu/AlphaDisclaimer.tw

		if [ "$build_failed" = "true" ]
		then
			echoError "Build failed."
			exit 1
		fi

		#Make the output prettier, replacing \t with a tab and \n with a newline
		sed -i -e '/^.*<div id="store-area".*$/s/\\t/\t/g' -e '/^.*
 /dev/null

		echoMessage "Saved to bin/FC_pregmod.html."
	fi
}

if [[ "$1" == "" ]]; then
	#tip if no option
	echoMessage "For more options see compile.sh -h."
else
	#parse options
	while [[ "$1" ]]
	do
		case $1 in
		-d|--dry)
			dry="true"
		;;
		-g|--git)
			usehash="true"
		;;
		-h|--help)
			displayHelp
			exit 0
		;;
		-j|--java)
			java="true"
		;;
		-p|--python)
			python="true"
		;;
		-q|--quiet)
			output=/dev/null
		;;
		*)
			echoError "Unknown argument $1."
			displayHelp
			exit 1
		esac
		shift
	done
fi

# Run sanity check.
[ -n "$java" ] && ./sanityCheck.sh java

[ -n "$python" ] && ./sanityCheck.sh

#compile
if [[ "$dry" ]]; then
	echoMessage "Dry run finished."
else
	compile
	echoMessage "Compilation finished."
fi


code:
@echo off
:: Free Cities Basic Compiler - Windows

:: Set working directory
pushd %~dp0

:: See if we can find a git installation
setlocal enabledelayedexpansion

for %%k in (HKCU HKLM) do (
	for %%w in (\ \Wow6432Node\) do (
		for /f "skip=2 delims=: tokens=1*" %%a in ('reg query "%%k\SOFTWARE%%wMicrosoft\Windows\CurrentVersion\Uninstall\Git_is1" /v InstallLocation 2^> nul') do (
			for /f "tokens=3" %%z in ("%%a") do (
				set GIT=%%z:%%b
				set GITFOUND=yes
				goto FOUND
			)
		)
	)
)
:FOUND
if %GITFOUND% == yes (
	set "PATH=%GIT%bin;%PATH%"
	bash --login -c ./sanityCheck.sh
)

:: Compile the game
call "%~dp0compile.bat"

if %GITFOUND% == yes (
	:: Make the output prettier, replacing \t with a tab and \n with a newline
	bash -c "sed -i -e '/^.*<div id=\"store-area\".*$/s/\\\t/\t/g' -e '/^.*<div id=\"store-area\".*$/s/\\\n/\n/g' bin/FC_pregmod.html"

	:: Revert ./src/init/storyInit.tw for next compilation
	git checkout -- ./src/init/storyInit.tw
)

popd
PAUSE


code:
@echo off
:: Free Cities Basic Compiler - Windows

:: Set working directory
pushd %~dp0

:: Compile the game
call "%~dp0compile.bat"

popd
PAUSE


code:
Anatomy of a FreeCities event

Type
There are several types of events. They all happen during the end-of-week calculation, AFTER the normal changes to the slaves and all the economic effects (including you messing with your corporation) happened.

Scheduled events
Nonrandom events
Random nonindividual events
Random individual events

The differences between them are almost non-existent. If they happen, they happen in this order, so you could say it's a kind of event priority ordering. The last two events pre-select a slave (in $eventSlave), with the "nonindividual" events using all your slaves while the "individual" events use only non-fuckdolls, typically from your penthouse. When writing your event, you're free to ignore this and choose your own slave or create a new one. By convention, scheduled events tend to go back to the "Scheduled Event" passage so that other such events can run; all the others tend to just skip to the next event category. Nothing in code forces you to do it this way for your events.

Preconditions
Most events have some kind of precondition for when they happen. Scheduled events always happen when their preconditions are true. Nonrandom events happen when their preconditions are true and no other nonrandom events get picked first (so writing a too-often-true precondition is a good way to break the game by blocking any further such events). The two other types of events get put in a pool ($events) if they match the precondition, then at most one event gets pulled from the pool per type.

	NonRandomEvent (26-33)
	<<elseif (_effectiveWeek == 14) && $badC != 1>>
	<<set _valid = $slaves.find(function(s) { return s.curatives > 1 || s.inflationType == "curative"; })>>
	<<if def _valid>>
		<<set $badC = 1, $Event = "bad curatives">>
		<<goto "Generic Plot Events">>
	<<else>>
		<<set $badC = 1>>
		<<goto "Nonrandom Event">>
	<</if>>

If it is week fourteen and the player hasn't ready seen the event, a check is then made for slaves that either are on curatives or have their implants filled by curatives. If it was successful then load the "bad curatives" generic event, if unsuccessful set the flag anyway and read from the Nonrandom Event passage.

Immediate effects

Every event can have immediate effects, which happen when the event gets chosen. For most events, those are what should happen if the player ignores the event (by hitting "Continue" or the space bar on the keyboard). Choice effects (see below) can override or roll back those.

	reRecuit (4-19)
	<<if Array.isArray($recruit)>>
		<<if $cheatMode == 1>>
			<<set $nextButton = "Back", $nextLink = "Nonrandom Event", $returnTo = "Nonrandom Event">> /* if user just clicks spacebar */
			''A random recruit event would have been selected from the following:''

			<<for _i = 0; _i < $recruit.length; _i++>>
				<<print "[[$recruit[_i]|RE recruit][$recruit = $recruit[" + _i + "]]]">>

			<</for>>

[[Go Back to Random Nonindividual Event|Random Nonindividual Event][$eventSlave = 0]]
		<<else>>
			<<set $recruit = $recruit.random()>>
			<<goto "RE recruit">>
		<</if>>
	<<else>>

	If cheat mode is enabled and the user presses the space bar go back to the start.

Main event text
The bulk of the writing will be in the main event text. There are quite a few rules to deal with here.
The PC is referred to in the second person singular ("you"), everyone else in third person.
The PC has no direct speech. All the things "you" say are described, not quoted.
A slave can be linked with the macro <<EventNameLink _Slave>>. This allows the player to click on the slave name and view their description, then go back to the event. WARNING: Going back triggers the event passage again, so if your event has immediate effects (see above), make sure to NOT repeat them by setting corresponding flags (this is harder than it sounds in general).

<<SlaveTitle _Slave>> sets $desc (which ends up being a string like "slavegirl", "MILF", "futanari" and so on, depending on slave).

<<SlavePronouns _Slave>> allows you to use the variables $pronoun ("she"/"he"/"it"), $pronounCap ("She"/"He"/"It"), $possessive ("her"/"his"/"its"), $possessiveCap ("Her"/"His"/"Its") and $object ("her"/"him"/"it"). There is NO variable for self-possession ("hers"/"his"/"its") and for "herself", you need to use <<= $object>>self.
One more macro initializes several others and is required when you have a slave speak and want to use direct quotes: <<Enunciate _Slave>> allows you to use <<Master>>, <<WrittenMaster>>, <<says>> (which turns into "lisps" when that's the case) and the lisping replacers <<s>>, <<ss>>, <<S>>, <<c>> and <<z>>.

The text should be about large enough to fit on the screen assuming typical monitor sizes. In terms of visible text, about 1000 words are a fine limit to aim for. There's a lot to keep in mind in terms of different appearances and circumstances, so keep your document with slave variables as a reference nearby.

It's fine — and a part of the normal workflow — to first write an event without any variation, then go through it and vary the text here and there.

Choices
You should keep the amount of choices small, but not too small. About three to five is generally a good number. Choices which can't be taken due to the current situation should be displayed as such ("You lack the funds ...") if they are an obvious choice, hidden when they aren't (for example, in event chains you might want to hide choices if the player didn't do something specific or didn't acquire some specific bit of knowledge). Every choice should be a simple sentence of the form "Do something." followed by a short explanation of the obvious effects.

Choices should also be hidden when they run against the game rules, like for example the setting if the player wants to see "extreme" content.

	diary (313-325)
	<<if $dairyFeedersUpgrade == 1>>
		The milking machines can hold feeders in slaves' mouths and inject drugs into their bodies, ensuring ideal nutrition and production.

    The feeders are
		<<if $dairyFeedersSetting == 2>>
			''industrial.'' [[Moderate|Dairy][$dairyFeedersSetting = 1, $dairyFeedersSettingChanged = -1]]
		<<elseif $dairyFeedersSetting == 1>>
			''active.'' [[Inactive|Dairy][$dairyFeedersSetting = 0]]<<if ($seeExtreme != 0) && ($dairyRestraintsSetting == 2)>> | [[Industrial|Dairy][$dairyFeedersSetting = 2, $dairyFeedersSettingChanged = 1]]<</if>>
		<<else>>
			''inactive.'' [[Active|Dairy][$dairyFeedersSetting = 1]]
		<</if>>
	<<else>>
		$dairyNameCaps is equipped to feed and clean slaves normally. [[Upgrade the milking machines with intubators|Dairy][$cash -= _Tmult1, $dairyFeedersUpgrade = 1]] //Costs ¤_Tmult1 and will increase upkeep costs//
	<</if>>

	In order to enable the industrial feeder option both any of the see extreme content options has be enabled and the restraint's have to be already set to industrial.

Remember that "do nothing" is almost always a choice (it's called "Continue" and can be found on the left side of the screen) so your events don't need this as an extra choice.

Choice text
This should be a short text describing the effects of your choice. Generally shorter than the main text, but all the other things mentioned there apply.
Try to keep surprise buttsex to a minimum.

For example this cut up version of "paternalist encounter" from REFS (l:106-139).

	<<link "Alert your drones and keep walking">>

	<</link>>
	<<if $cash >= 2000>>

<<link "Take the poor slave girl into your custody">>

<<link "Publicly confront the citizen">>

	So here you can either, A) "Alert your drones and keep walking", B) if $cash is above 2000 you can take acquire the slave or C) "Publicly confront the citizen".

Choice effect
A choice doesn't need to have a specific effect. If your event has an immediate effect, remember to take that into account when you decide on the choice's effects.

	<<link "Alert your drones and keep walking">>
	<<replace "#result">>
	You inform $assistantName that you have a slave beater in need of detainment by your security drones, then continue on your way confident in your knowledge that the citizen will soon be in custody.
	<</replace>>
	<</link>>
	<<if $cash >= 2000>>

<<link "Take the poor slave girl into your custody">>
		<<replace "#result">>
		Confronting the citizen is simplicity in itself; he would not dare defy you under threat of arrest by your security drones and is unlikely to garner any sympathy from the public for his degradationist behaviors. As such, you are able to take civil ownership over the poor slave girl and take her into your care with only minimal compensation to the citizen. As you stride away from her former owner with the girl in your arms, she leans over to plant a chaste kiss of thanks on your cheek.
		<<set $cash -= 2000>>
		<<set $activeSlaveOneTimeMaxAge = 22>>
		<<include "Generate New Slave">>
		<<set $activeSlave.origin = "She was taken into your custody from an abusive owner.">>
		<<set $activeSlave.devotion = random(-25,0)>>
		<<set $activeSlave.trust = random(-25,0)>>
		<<set $activeSlave.boobs = random(300,450)>>
		<<set $activeSlave.hips = random(-2,-1)>>
		<<set $activeSlave.butt = random(0,2)>>
		<<set $activeSlave.health = random(10,20)>>
		<<include "New Slave Intro">>
		<<set $nextLink = "AS Dump", $returnTo = "RIE Eligibility Check">>
		<</replace>>
		<</link>> // Taking custody of the girl will cost ¤2000. //
	<</if>>

<<link "Publicly confront the citizen">>
	<<replace "#result">>
	Your walk up to the citizen is not accompanied by shaking ground or tumultuous fanfare, yet the citizen looks as if death itself has come before him. You don't hurt him physically, instead chastising him publicly in front of his fellow peers who begin to cheer their agreement. You end your tirade of verbal abuse with a reminder that although the man is a citizen of your arcology, that does not give him the impunity to shirk the law. To make it clear his next offense will be his last, a brace of your security drones hover behind you threateningly. The crowd that gathered @@.green;approves of your rebuke of the citizen@@.
	<<set $rep += 500>>
	<</replace>>
	<</link>>

	So here the results of the choices are, A) nothing, B) reduce cash by 2000 but acquire the slave C) chastise the citizen for plus 500 rep.