code:
-The transgender thoughts sometimes disappear if you perform a chain of sex operations on the same pawn.
Probably some oversight in algorithm that gives thoughts on transition (some underthought chaining of effects).
Fix somewhere in the future.

-Whores with untended diseases (flu, herpes, warts) will try hookup and may succeed but the job will get cancelled shortly afterwards resulting in message spam.
Currently added different message to notify players of problem.

-Genital helper is shit. It does not account for later added animal dicks. has_<x> methods are fucking runtime regex searches

repo_name: Timura/rjw

license: mit


code:
CORE:

add nymph lord ai, so nymphs can behave as visitors
add sex addiction to demonic parts?
add insects dragging pawns to hives
gene inheritance for insect ovis

workgivers(aka manual control):
-workgiver for sex with humpshrooms, increasing their growth by 1 day, causing addiction, temp? humpshroom-penis futa?
-workgiver(auto, cleaning) to collect cum

add recipe to make food from cum?
add female cum?

add some sort of right clicking sub menu:
-designate breeding pair(through relations?)

rework genital system, so it only have vag,penis,breast,anus (with comp?) storing name, severity(size?) of part
probably also rework surgeries to support those
rework sex satisfaction system(based on parts size/difference? traits/quirks etc)

integrate milkable colonists with sizes support and operations? to change milk type(w gitterworld med?)

BDSM:
-make ropes and other generic gear, that is "locked" but not with hololock unique keys - normal keys made of steel, etc
-piercing?
-blindfolds?
-vibrators?

Multiplayer:
-figure out predictable seeds instead of syncmethod's

maybe:
add support for other pregnancy mods
more sex related incidents?
backstories: cowgirl, etc

whoring:
-add whoring zone( esp for prisoners)
-limit whoring to zone
-whore in zone does most checks but instead of going after client- client comes to whore and they do stuff
or merge quickies with whoring
or idk invent something new
and maybe while at it make toggles for whores to service colonists, oh and defying whores will count toward rape and accepted whores - con sex

never?
-make interactive COC like sex/stories
RatherNot (Code)
We need Pawn Sexualization Request: like Pawn Generation Request, but for generating RJW things
Current approach missing any kind of flexibility
In can be useful for race support to: instead placing checks directly in generator you can place limitations on generator in request

older stuff
TO DO:
Add new body part groups for the bondage gear (specifically a genitals group for the CB and a jaw or mouth group for the gag, the mouth group that's already in the game won't work I'm pretty sure)
Once that (^^^) is done make it so that the groups are automatically blocked by the gear, like a more robust version of the blocks_genitals flag
Add more thoughts after sex about e.g. partner's dick size and sex skill
Add chance for sex/fap with a peg dick to produce splinters

TO DO EVENTUALLY (AKA NEVER):
Improve detection of genital harvest vs amputation
  - (Maybe) Make it so that downgrading a pawn is considered harvesting

OTHER RANDOM/HALF-BAKED IDEAS:
Add some random dialogues for rapes, lovins, etc.
Strap prisoners to their beds
Sex addiction
  - Reduces satisfaction from sex
  - Should only affect nymphomaniacs?

Traits?:
Low Self Esteem (just another pessimist trait)

Dom/Sub

Mindbroken (raped so much that they are at a permanent max happiness, but are worth nothing more than just hauling and cleaning)

Branded, this pawn is branded a cow/animal fucker/prostitute/rapist, whatever his/her extra curricular is comes with a negative opinions so they are pretty much never liked?

path: todo or not todo.txt


code:
root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
trim_trailing_whitespace = true

repo_name: Carrabina/the

path: .editorconfig


code:
/node_modules
/*.zip
/releases/*.html
/source/script-compiled.js

path: .gitignore


theChildTrainer

cSpell.words: baaack bumhole camelcase Carrabina coprime cumshot cumshots cunny cyrusfirheir Debuff deeppink esbenp Fairmath fauxhawkian herms hmnn Hungryyy iiit Kyaa ldquo newmeter nobr passageinit passagestart pepee pervy precum rdquo showmeter sowwyyy squishiness storyformat sugarcube techer textbox ticed Trib Waaaah Wooow Yesh

twee3LanguageTools.passage.group: Folder


code:
{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Tweego",
      "type": "shell",
      "isBackground": true,
      "command": "npm run build;tweego/tweego -o releases/${config:name}${config:version}.html source;echo 'done compiling'",
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}


code:
The basic idea:
It starts with the Player moving into his new home with a basement. His intentions are to lock up young slaves in the basement and make use of them for its own personal profit (both sexually and monetary). Shortly after moving in he has to go to his work that he already acquired before moving in.

On his free time he starts to look for his favorite preys around town trying to avoid drawing too much suspicion. When the player finds an interesting target he would stalk his victim. Learning details about his victim (what the kid likes, name, locations where it's alone, etc...). Then looking for the best opportunity to kidnap, either by using chloroform or just convince the kid to visit the player by luring the kid with something of interest. If it's kidnapped the player would need to do it while avoiding the risk of being seen.

Once the kid is locked in the basement the kid would most likely have different questions/demands to the player depending of the kid's age ("where am I", "I want to go home", "Let me out", "I want my mama", etc...) the first time the kid is locked in the basement. In the basement the player can train the kids sexually using their asshole, pussy or mouth (blowjob). The player can choose to go gentle and slow or harsh and fast training, harsher will make the kids whimper making it riskier to be heard by the neighbors.

The player can also train their obedience using treats and/or punishments and starvation can be used to enforce it. Training would make the kid make the player more money if prostituited or sold to the black market. Having too many kids with low obedience can make the kids break free from your basement (game ends) when you go to work unless the lock is upgraded.

Treats, praises and gentleness make the kid's love for the player increase. If love is high the kid can be selected to access the rest of the house, temporarily or permanently. Even with high love if the kid has low obedience it can present a risk of being caught when accessing the rest of the house when a delivery (of something the player bought) comes at your door.

Only when obedience is maxed out the player can use the kid to lure and kidnap other kids.

path: Basic idea.txt


code:
# The Child Trainer

## How to compile (In the way I do it)

I'm pretty sure these indications work on Linux and MacOS but I only tested it on Windows.

### You need to install on your OS (minimum requirements):

- [node.js](https://nodejs.org/) - Needed to transpile the TypeScript in the **ts** folder into Javascript so that Tweego can then compile it into the game.
- [VSCode](https://code.visualstudio.com/) - IDE I use to develop the game. This project is configured to work with this one and it will only compile out of the box with VSCode.

No need to install Tweego. The repository comes with the right version to compile the game.

Once all that is installed and the project is cloned or downloaded you can proceed to the first compilation:

1. Open the file **theChildTrainer.code-workspace** with VSCode.
2. Open a terminal (Menu: Terminal -> New Terminal).
3. While connected to the internet, enter the command `npm ci` into the terminal to download and install project dependencies (specially [Babel js](https://babeljs.io/) transcompiler for the TypeScript).
4. Build the project (by default Ctrl+Shift+B or Cmd+Shift+B).

Once done compiling the terminal will show the phrase "done compiling" and the compiled html will be in the **releases** folder.

## VSCode extensions I use for this project

- **Twee 3 Language Tools** - Extremely useful when working with twee (.tw extension) files easier and faster.
- **open in browser** - With this one, to test the compiled game just right click on it (html) and select "Open in Default Browser".
- **Prettier - Code formatter** - This one is for the Typescript. Press Shift+Alt+F to automatically format it nicely.
- **file-icons** - Adds icons to most file types to identify them on VSCode file viewer at a single glance.
- **indent-rainbow** - Adds a different color to the indentation depending on how many tabs or spaces have. It makes it faster to analyze it. In my opinion.
- **Noctis** - Just a collection of color themes to please my eyes. I use the *Noctis Minimus* theme.

## Extra recommendations

Due the nature of this game. If you don't like Microsoft obtaining usage information from you, you can disable telemetry in VSCode, clicking File -> Preferences -> Settings and then go to the search/filter box at the top, type telemetry, under that select the User tab, and disable all telemetry.

***

If you have any question don't hesitate to ask me 😁


the-child-trainer

type: opencollective

type: tidelift

type: individual

jsesc: bin/jsesc

semver: bin/semver


build: babel --extensions .ts ./ts --out-file source/script-compiled.js


code: Final releases in html will be compiled to this folder


code:
:: StoryCaption {"position":"250,0"}
----
!<<- Now.getWeekDay()>>
!!!<<- Now.getTime()>>
----
$player.name <<link "[Inventory]">><<dialog "Player Inventory">>\
    <<set _items = Player.getInventory().items>>\
    <<for _item range _items>>\
        <<-_item.count?_item.count+" ":"">>_item.name
    <</for>><<unset _items>>\
<</dialog>><</link>>
<<if $cheat>>\
    <<link "+10k">>\
        <<set $player.cash += 10000;Engine.show()>>
    <</link>> \
    <<link "+100k">>\
        <<set $player.cash += 100000;Engine.show()>>
    <</link>> \
<</if>>\
Cash: ¤<<- $player.cash.toFixed(2)>>
<<if $cheat>>\
    <<link "+">>\
        <<set $player.energy = 100;Engine.show()>>
    <</link>> \
<</if>>\
Energy:<<if $cheat>>\
<<set _infiniteEnergyText = "Make " + ($settings.infiniteEnergy ? "Finite" : "Infinite")>>\
 <<link _infiniteEnergyText>><<set
    $settings.infiniteEnergy = ~$settings.infiniteEnergy;
    $('#infiniteEnergy a').html("Make " + ($settings.infiniteEnergy ? "Finite" : "Infinite"));
>><</link>><</if>>
<<showmeter playerEnergy `$player.energy / 100`>>
:: StoryMenu {"position":"1025,125"}
<<link "👨👩👧👦 Character stats">>
    <<dialog "Character stats">>
        <<set _dialogWidth = 'width:' + Math.min(1022, window.innerWidth - 84) + 'px'>>\

\

\

\
                <<for _slave range $slaves>>\
                    <<set _action to "<<replace '#stats'>><<personProperties " + _slave.uid + ">><</replace>>">>\
                    <<printPersonLink _slave _action>><<unset _action>>
                <</for>>\

\

\
    <</dialog>>
<</link>>
<<link "⚙ Settings">>
    <<dialog Settings>>\

\
            <input type="radio" name="configTabs" id="tab-childGen" checked>\
            Child generator\

\

\

\
                        Female percentage:\

\

<input type="range" min="0" max="100" value="50" class="slider" id="femalePercentSlider">
\

\
                            <input id="femalePercentDisplay" @value="$settings.childGeneration.femalePercentage" style="width: 1.7em;border: 0;pointer-events: none;" readonly> %
\

\

\

\
                        Herm percentage (100 is all characters replaced with herms):\

\

<input type="range" min="0" max="100" value="50" class="slider" id="hermPercentSlider">
\

\
                            <input id="hermPercentDisplay" @value="$settings.childGeneration.hermPercentage" style="width: 1.7em;border: 0;pointer-events: none;" readonly> %
\

\

\

\

\

\
                        Females:
                        From<input type="number" min="0" id="femaleAgeFrom" style="width:3em">years old
                        To  <input type="number" id="femaleAgeTo" style="width:3em">years old

\

\
                        Males:
                        From<input type="number" min="0" id="maleAgeFrom" style="width:3em">years old
                        To  <input type="number" id="maleAgeTo" style="width:3em">years old

\

\
                        Herms:
                        From<input type="number" min="0" id="hermAgeFrom" style="width:3em">years old
                        To  <input type="number" id="hermAgeTo" style="width:3em">years old

\

\
                <<button "Hair colors">>\
                    <<userEditListVar "$settings.childGeneration.hairColors" "Edit hair colors" PersonGeneration.hairColors>>
                <</button>>\
                <<button "Hair styles">>\
                    <<userEditListVar "$settings.childGeneration.hairStyles" "Edit hair styles" PersonGeneration.hairStyles>>
                <</button>>\
                <<button "Eye colors">>\
                    <<userEditListVar "$settings.childGeneration.eyeColors" "Edit eye colors" PersonGeneration.eyeColors>>
                <</button>>\
                <<button "Skins">>\
                    <<userEditListVar "$settings.childGeneration.skins" "Edit skins" PersonGeneration.skins>>
                <</button>>\

\
            <input type="radio" name="configTabs" id="tab-tweaks">\
            Tweaks\

\

\

\

Change job
\
                        Current job: <<- $player.job ? $player.job.name.toUpperFirst() : "Unemployed">>
                        <<for _jobKey, _job range Jobs>>\
                            <<set _jobDesc = _job.name.toUpperFirst() + ": Pay:¤" + _job.pay.toFixed(2) + "/day, Go time:" + _job.enterTime>>\
                            <<print '<<button _jobDesc>><<run $player.job = ' + JSON.stringify(_job) + ';Engine.show()>><<dialog "Job changed">>Your job is now: ' + _job.name.toUpperFirst() + '.<</dialog>><</button>>'>>
                        <</for>>\
                        <<button '<<emoji 🚫>>None'>><<run $player.job = null;Engine.show()>><<dialog "Job changed">>You're now unemployed.<</dialog>><</button>>\

\

Lust drop
\
                        Amount of lust drop after NPC ejaculating: <input type="number" id="lustDecCum" style="width:3em">

\

\

Addressing
\
                How should your kids call you? (leaving them blank reverts them to default value)
                Slaves: <input type="text" class="addressing" data-target="slave">
                Servants: <input type="text" class="addressing" data-target="servant">
                Lovers: <input type="text" class="addressing" data-target="lover">
                Your Sons/Daughters: <input type="text" class="addressing" data-target="offspring">

\
            <input type="radio" name="configTabs" id="tab-switches">\
            Switches\

\
                \
                    <input id="toggleEmoji" type="checkbox" class="macro-checkbox">Show emojis\
                \
                \
                    <input id="toggleAnal" type="checkbox" class="macro-checkbox">Show anal content\
                \
                \
                    <input id="toggleSlaveSell" type="checkbox" class="macro-checkbox">Slave selling\
                \
                <<set _cheatButtonText = ($cheat ? "Disable" : "Enable") + " Cheats">>\
                <<button _cheatButtonText>>\
                    <<run
                        $cheat = ~$cheat;/*Inverse $cheat value. Anything but true becomes true and true becomes false*/
                        Engine.show();
                        $('#cheatsButton button').text(($cheat ? "Disable" : "Enable") + " Cheats");
                    >>
                <</button>>\

\

\
        \
    <</dialog>>\
<</link>>


code:
:: StoryData {"position":"500,0"}
{
    "ifid": "A1AB4B27-D446-4DA4-9B25-47E8F99367E8",
    "format":"SugarCube",
    "format-version":"2.36.1"
}
:: StoryTitle {"position":"125,0"}
The Child Trainer
:: StoryInit
<<set
    $now = Now;
    $player = Player;
    $settings = {
        emoji: true,
        anal: true,
        childGeneration: PersonGeneration,
        slaveSelling: true,
        lustDecCum: 20
    };
    $onlineStore = OnlineStore;
    $basement = Basement;
    $slaves = [];
    $achievements = [];
>>
<<newmeter playerEnergy>>
    <<sizing 100% 20px>>
    <<label '$player.energy'>>
<</newmeter>>


code:
:: Start {"position":"1025,825"}
<<set _sexOptions = ["man", "woman", "futanari"]>>\
<<set _genderOptions = ["him", "her", "them"]>>
Your name is <<textbox "$player.name" "David">>, a young <<listbox '_sex' autoselect>><<optionsfrom _sexOptions>><</listbox>>
 living by <<listbox '_gender' autoselect>><<optionsfrom _genderOptions>><</listbox>>self in a small home. To the outside world, you’re the definition of an average person, but behind closed doors, you're anything but normal.

If anyone where to know what type of person you really were, you’d probably find yourself under heavy surveillance in a prison cell. Let’s just say that your tastes in life, are a bit too exotic for most people.

It's currently a nice Monday morning and the start of a new life. For too long have your desires been only satisfied by dreams. Today was the day that you started to make those dreams a reality. Today, you would go down the path of enslaving and training children to sate your sexual depravity.

<<keyAction Start ▶ btn>>
  <<set
    Player.setSex(_sex == "man" ? "male" : _sex == "woman" ? "female" : "herm");
    Player.setGender(_gender == "him" ? "male" : _gender == "her" ? "female" : "nb");
    $player.name to $('#textbox-playername').val();
    >>
  <<goto IntroBed>>/*[[IntroBed]]*/
<</keyAction>>
<<done>>
  <<run
    $('#listbox--gender').on('change', function() {
      var playerName = $('#textbox-playername');
      var currentName = playerName.val();
      if(this.value == 1 && currentName == "David")
        playerName.val("Emma");
      else if(currentName == "Emma")
        playerName.val("David");
      $('#pronoun').html(this.value == 1 ? "her" : "him");
    });
  >>
<</done>>
:: IntroBed {"position":"1025,700"}
<<scenery bed>>
As the sun reveals the world to a nice Monday morning, you climb out of bed, ready to start a new day. <<print either("The birds chip happily in the morning sun and the trees rustle alive in the early breeze.", "The morning sun shines through your room, illuminating it in a nice warm glow.")>>

Today is the day that you begin your debauchery.

<<keyOption [[Leave your room|IntroMain]] 🚪>>
<<keyOption [[Skip intro|main]] ⏭>>
:: IntroMain {"position":"1025,575"}
<<scenery mainRoom>>
As you leave your room, you walk into your living room. It’s not the most ideal layout, but at least you have a house with the terrible paychecks you get from work.

Glancing at the clock, you note that you have a good hour and a half before you need to leave for work. It starts at 8 o’clock, but it’s also a 30-minute drive away.

To pass the time you decide to head on down to the basement. It would probably be best if you plan out how you were going to keep a child down there.

<<keyOption [[The Basement|IntroBasement]] 🚪>>
<<keyOption [[Skip intro|main]] ⏭>>
:: IntroBasement {"position":"1025,450"}
<<addMinutes 60>>\
<<scenery basement>>
Walking down creaky wooden steps, you arrive at the basement. It's quite barren with a concrete layout and exposed piping visible. But as much as it sucks, it is the most preferable option to keep a slave held in.

You already have a couple things set up. An old mattress has been laid on the ground for future ‘guests’, and a simple lock was installed on the door to help prevent any escapes. You determine that you’ll need a couple more items before you’re ready to hold someone here. At least some restraints and optionally some sound padding, would all be the necessary components. Technically you could also use your own bedroom too, but the sound muffling the basement provides is so much better.

After a bit of daydreaming about your hopeful future, you check the time to see that you have about 30 minutes left until you need to leave for work. You could hang around the house for the time being, otherwise you could leave now.

<<keyOption [[Go outside|IntroHouse]] 🏡>>
<<keyOption [[Skip intro|main]] ⏭>>
:: IntroHouse {"position":"825,375"}
<<scenery home>>
Your home doesn’t contain much, but it’s a home that you’ve grown accustomed to. Along with the living space, you also have a job working as a $player.job.name. It doesn’t pay much, but something is better than nothing.

<<keyOption [[Start game|main]] ▶ btn>>


code:
.container {
  position: relative;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}

.container:after,
.row:after,
.u-cf {
  content: "";
  display: table;
  clear: both; }

.column,
.columns {
  width: 100%;
  float: left;
  box-sizing: border-box;
}

/* For devices larger than 400px */
@media (min-width: 400px) {
  .container {
    width: 85%;
    padding: 0;
  }
}

/* For devices larger than 550px */
@media (min-width: 550px) {
  .container {
    width: 80%;
  }
  .column,
  .columns {
    margin-left: 4%;
  }
  .column:first-child,
  .columns:first-child {
    margin-left: 0;
  }

  .one.column,
  .one.columns {
    width: 4.66666666667%;
  }
  .two.columns {
    width: 13.3333333333%;
  }
  .three.columns {
    width: 22%;
  }
  .four.columns {
    width: 30.6666666667%;
  }
  .five.columns {
    width: 39.3333333333%;
  }
  .six.columns {
    width: 48%;
  }
  .seven.columns {
    width: 56.6666666667%;
  }
  .eight.columns {
    width: 65.3333333333%;
  }
  .nine.columns {
    width: 74%;
  }
  .ten.columns {
    width: 82.6666666667%;
  }
  .eleven.columns {
    width: 91.3333333333%;
  }
  .twelve.columns {
    width: 100%;
    margin-left: 0;
  }

  .one-third.column {
    width: 30.6666666667%;
  }
  .two-thirds.column {
    width: 65.3333333333%;
  }

  .one-half.column {
    width: 48%;
  }

  /* Offsets */
  .offset-by-one.column,
  .offset-by-one.columns {
    margin-left: 8.66666666667%;
  }
  .offset-by-two.column,
  .offset-by-two.columns {
    margin-left: 17.3333333333%;
  }
  .offset-by-three.column,
  .offset-by-three.columns {
    margin-left: 26%;
  }
  .offset-by-four.column,
  .offset-by-four.columns {
    margin-left: 34.6666666667%;
  }
  .offset-by-five.column,
  .offset-by-five.columns {
    margin-left: 43.3333333333%;
  }
  .offset-by-six.column,
  .offset-by-six.columns {
    margin-left: 52%;
  }
  .offset-by-seven.column,
  .offset-by-seven.columns {
    margin-left: 60.6666666667%;
  }
  .offset-by-eight.column,
  .offset-by-eight.columns {
    margin-left: 69.3333333333%;
  }
  .offset-by-nine.column,
  .offset-by-nine.columns {
    margin-left: 78%;
  }
  .offset-by-ten.column,
  .offset-by-ten.columns {
    margin-left: 86.6666666667%;
  }
  .offset-by-eleven.column,
  .offset-by-eleven.columns {
    margin-left: 95.3333333333%;
  }

  .offset-by-one-third.column,
  .offset-by-one-third.columns {
    margin-left: 34.6666666667%;
  }
  .offset-by-two-thirds.column,
  .offset-by-two-thirds.columns {
    margin-left: 69.3333333333%;
  }

  .offset-by-one-half.column,
  .offset-by-one-half.columns {
    margin-left: 52%;
  }
}


code:
.centerHouse {
  display: block;
  margin-left: auto;
  margin-right: auto;
  max-width: 500px;
  height: auto;
  border-radius: 8px;
}
label.toggle-button input[type="checkbox"] {
  display: none;
}

label.toggle-button input[type="checkbox"]:not(:checked) + span::after {
  font-family: tme-fa-icons;
  font-style: normal;
  font-weight: 400;
  font-variant: normal;
  text-transform: none;
  content: "\00a0\00a0\e830";
}

label.toggle-button input[type="checkbox"]:checked + span::after {
  font-family: tme-fa-icons;
  font-style: normal;
  font-weight: 400;
  font-variant: normal;
  text-transform: none;
  content: "\00a0\00a0\e831";
}

label.toggle-button span:hover {
  background-color: #57c;
  border-color: #79e;
}

label.toggle-button span {
  display: inline-block;
  cursor: pointer;
  color: #eee;
  background-color: #35a;
  border: 1px solid #57c;
  line-height: normal;
  padding: 0.4em;
  margin-top: 1px;
  margin-bottom: 1px;
  -webkit-transition-duration: 0.2s;
  -o-transition-duration: 0.2s;
  transition-duration: 0.2s;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.shopProduct {
  position: relative;
  height: 0;
  padding-bottom: 30.6666666667%;
  background: #2f4f4f;
  vertical-align: middle;
  margin-right: 2%;
  border: 1px solid #999;
  overflow: hidden;
}
.shopProduct div {
  position: absolute;
  bottom: 0;
  width: 100%;
  color: #69e;
  text-shadow: 2px 0px 1px #2f4f4f, 2px 2px 1px #2f4f4f, 0px 2px 1px #2f4f4f,
    -2px 2px 1px #2f4f4f, -2px 0px 1px #2f4f4f, -2px -2px 1px #2f4f4f,
    0px -2px 1px #2f4f4f, 2px -2px 1px #2f4f4f, 4px 0px 3px #2f4f4f,
    4px 4px 3px #2f4f4f, 0px 4px 3px #2f4f4f, -4px 4px 3px #2f4f4f,
    -4px 0px 3px #2f4f4f, -4px -4px 3px #2f4f4f, 0px -4px 3px #2f4f4f,
    4px -4px 3px #2f4f4f;
}
.shopProduct:hover div {
  color: #8af;
}
.shopProduct img {
  margin-top: 0.5em;
  max-width: 95%;
  max-height: 178px;
}
.emoji {
  margin: 0 5px;
}


code:
.tabs {
  display: flex;
  flex-wrap: wrap;
}
.tabs > label {
  order: 1;
  display: block;
  padding: 1rem 2rem;
  margin-right: 0.2rem;
  cursor: pointer;
  font-weight: bold;
  transition: background ease 0.2s;
  background-color: #222;
  color: gray;
}
.tabs > div {
  order: 99;
  flex-grow: 1;
  width: 100%;
  display: none;
  padding: 0.3rem 1rem;
  background: #2e2f36;
}
.tabs > div h3 {
  color: yellow;
  margin: 0.3rem 0;
}
.tabs > input[type=radio] {
  display: none;
}
.tabs > input[type=radio]:checked + label {
  background: #2e2f36;
  color: gainsboro;
}
.tabs > input[type=radio]:checked + label + div {
  display: block;
}

@media (max-width: 45em) {
  .tabs > div,
.tabs > label {
    order: initial;
  }
  .tabs > label {
    width: 100%;
    margin-right: 0;
    margin-top: 0.2rem;
  }
}


code:
.tabs {
  display: flex;
  flex-wrap: wrap;
  > label {
    order: 1;
    display: block;
    padding: 1rem 2rem;
    margin-right: 0.2rem;
    cursor: pointer;
    font-weight: bold;
    transition: background ease 0.2s;
    background-color: #222;
    color: gray;
  }
  > div {
    order: 99;
    flex-grow: 1;
    width: 100%;
    display: none;
    padding: 0.3rem 1rem;
    background: #2e2f36;
    h3 {
      color: yellow;
      margin: 0.3rem 0;
    }
  }
  > input[type="radio"] {
    display: none;
    &:checked + label {
      background: #2e2f36;
      color: gainsboro;
      + div {
        display: block;
      }
    }
  }
}

@media (max-width: 45em) {
  .tabs {
    > div,
    > label {
      order: initial;
    }
    > label {
      width: 100%;
      margin-right: 0;
      margin-top: 0.2rem;
    }
  }
}


code:
:: basement {"position":"250,625"}
<<scenery basement>>
<<if !Player.hasAchievement("capturedSlave") && $slaves.length>>\
    <<set $achievements.push("capturedSlave")>>\
    You actually did it!! Now you have a personal $found_kid.age year old slave locked on your basement and no one knows about it!!
    You could do anything you wanted to $found_kid.pronoun and no one would stop you<<emoji 🤤>>.
    <<unset $found_kid>>
<<else>>\
    You are currently in your basement.
<</if>>\
<<unlessEmergency>>\
    <<slaveEvents Basement>>

\

\
            <<set _slaves = Basement.getSlaves()>>\
            <<if _slaves.length > 0>>\
                Approach slave:
                <<set _charcode = 97>>\
                <<for _slave range _slaves>><<if _slave.location == 'basement'>>\
                    <<set
                        if(_charcode == 104) _charcode += 2;
                        _char = String.fromCharCode(_charcode++);
                        _keyId = _char + "KeyAct";
                        _action = "<<openNpcInteraction slave " + _slave.uid + ">>";
                        _prefix = "(" + _char.toUpperCase() + ")"
                    >>\
                    \
                        <<printPersonLink _slave _action _prefix>>
                    \
                <</if>><</for>>\
            <</if>>\

\

\
            <<set
                _availableBeds to Basement.availableBeds();
            >>\
            <<if _availableBeds <= 0>>\
                There's no more room for more slaves. You need to buy Mattresses if you want more.
            <<else>>\
                There's room for _availableBeds more slave<<if _availableBeds gt 1>>s<</if>>.
            <</if>>\

\

    <<keyOption [[Go back upstairs|main]] 🚪>>
    <<if TortRafters.getSlaves().length>>\
        <<keyOption [[Check on suspended slaves|tortRafters]] ⛓>>
    <</if>>\
    /*<<keyOption [[The torture room|tort]] ⛓>>*/\
    <<if Player.has("pizza") && Basement.getHungrySlaves().length>>\
        <<keyOption [[Give pizza to all hungry slaves|giveSlavesPizza]] 🍕>>
    <</if>>\
    <<include workOption>>\
<</unlessEmergency>>\

:: giveSlavesPizza {"position":"250,775"}
<<scenery basement>>
You lay down the pizza<<if Player.has("pizza", 2)>>s<</if>> and all the slaves get around it to eat.
<<set
    var hungrySlaves = Basement.getHungrySlaves();
    var inventory = Player.getInventory();
    var pizzaCount = inventory.get("pizza").count
    var hungerDepletedPerSlave = (pizzaCount * (8/3) * 90) / hungrySlaves.length;
    hungrySlaves.forEach(function(slave) {
        slave.hunger = Math.max(0, slave.hunger - hungerDepletedPerSlave);
        slave.freedomWish = Math.max(0, slave.freedomWish - 5);
    });
    inventory.removeByName("pizza", pizzaCount);
>>\
/*<<keyOption [[The torture room|tort]] ⛓>>*/
<<keyOption [[Return|basement]] 🔙>>


code:
:: bathroom {"position":"775,700"}
<<scenery bathroom>>
You are currently standing in your bathroom.
<<unlessEmergency>>\
    <<wanderingSlaves>>\
    <<slaveEvents BathRoom>>
    <<keyOption [[Leave your bathroom|main]] 🚪>>
    <<keyAction 'Take a shower' 🛀>><<addHours 1>><<run Engine.show()>><</keyAction>>: 1 hour
    <<sleepOption>>
<</unlessEmergency>>\


code:
:: garden {"position":"800,500"}
<<scenery garden>>
You are currently standing in your garden.
<<unlessEmergency>>\
    <<wanderingSlaves>>\
    <<keyOption [[Leave your garden|main]] 🚪>>
    <<keyAction 'gardening' 🌺>><<addMinutes 30>><<run Engine.show()>><</keyAction>>: 30min
    <<sleepOption>>
<</unlessEmergency>>\


code:
:: bed {"position":"675,825"}
<<scenery bed>>
<<if $player.sleeping>>\
    <<set _sleepSlave = $slaves.firstOrDefault(function(slave){return Person.hasAchievement('okSleepWithPlayer', slave)})>>\
    <<if _sleepSlave>><<done>>/*Without using the done macro the bed passage was executed twice for some reason*/
        <<run State.history.splice(State.history.length - 1, 1)>>/*Let's forget that we entered the bed scene so that the player can still go back in time. Otherwise they would be redirected back to the interaction.*/
        <<openNpcInteraction wakeUpAfterNightTogether _sleepSlave.uid>>
    <</done>><</if>>
	As the sun reveals the world to a nice <<- Now.getWeekDay()>> morning, you climb out of bed, ready to start a new day. <<- either("The birds chip happily in the morning sun and the trees rustle alive in the early breeze.", "The morning sun shines through your room, illuminating it in a nice warm glow.")>>
<<else>>\
	You currently stand in the middle of your cozy small bedroom.
<</if>>\
<<if $player.sleeping && OnlineStore.pendingOrder()>>
    Someone is knocking at the front door!!
    Who might that be?
    <<keyOption [[Go check|receivePacket]] 🚪>>
<<else>>
    <<unlessEmergency>>\
        <<wanderingSlaves>>\
        <<slaveEvents BedRoom>>
        <<keyOption [[Leave your room|main]] 🚪>>
        <<keyOption [[go to your computer|pc]] 💻>>
        <<keyOption [[Your bathroom|bathroom]] 🛀>>
        <<sleepOption>>
    <</unlessEmergency>>\
<</if>>
<<set $player.sleeping = false>>
<<unset $personWokeUp>>
:: main {"position":"600,625"}
<<scenery mainRoom>>
You are currently standing in the middle of the main room of your house.

''Is there something you want to do in here?''

<<unlessEmergency>>\
    <<wanderingSlaves>>\
    <<slaveEvents MainRoom>>
    <<keyOption [[Watch TV|watchTv]] 📺>>: 30min
    <<include workOption>>\
    <<keyOption [[Go outside|home]] 🚪>>
    <<keyOption [[The Basement|basement]] 🔒>>
    <<keyOption [[Your Bedroom|bed]] 🛌>>
    <<keyOption [[Your garden|garden]] 🌺>>
    <<keyOption [[Your kitchen|kitchen]] ☕>>
    <<keyOption [[Your toilet|wc]] 🚽>>
    <<keyOption [[Your bathroom|bathroom]] 🛀>>
<</unlessEmergency>>\
:: watchTv {"position":"875,825"}
<<addMinutes 30>>\
@@color:yellow;You spend the next half hour skimming through channels and shows.@@
<<include main>>\
:: slaveEvent {"position":"375,0"}
<<if _eventSlaves[_eventIndex]>>\
    \
        <<set _slave to _eventSlaves[_eventIndex]>>
        <<if _slave.eventType == "demand">>\
            _slave.name <<if _slave.fear gte 30>>cautiously<</if>> approaches, looking at you with _slave.possessive _slave.eyeColor eyes, and says:
        "<</if>>\
/*     */<<switch _slave.event>>\
/*        */<<case "hunger">>\
/*          */<<if _slave.hunger gte 75>>\
/*            */<<if _slave.age gte 5>>Please!!! I'm so hungry!!! <<emoji 🙁>><<else>>I'm hungryyyyyy!!! <<emoji 😢>><</if>>\
/*          */<<else>>\
/*            */I'm hungry... <<emoji 🙁>>\
/*          */<</if>>\
/*        */<<case 'sleepWithPlayer'>>\
/*          */Hey <<npcAddressPlayer _slave>>, can I sleep in the same bed as you tonight?\
/*        */<<case 'cook'>>\
/*          */_slave.name is currently cooking<<if !_slave.haveClothes>> while naked<</if>>.\
/*      */<</switch>><<if _slave.eventType == "demand">>"<</if>>
        <<set _eventInteraction to "slaveEvent" + _slave.event.toUpperFirst()>>
        \
            <<button '✅ (H) Handle'>>
                <<set $npc to _eventSlaves[_eventIndex]>>
                <<openNpcInteraction _eventInteraction>>
            <</button>>\
        \
            <<button '❌ (I) Ignore'>>\
                <<set _eventIndex++>>
                <<replace #slaveEvent>><<include slaveEvent>><</replace>>
            <</button>>\
        \
    \
<</if>>\
:: cookFeedExceptions [nobr] {"position":"125,125"}
<<if $settings.cook.exceptions.length>>
    <<for _exception range $settings.cook.exceptions>>
        <<set _exceptionPerson = Person.get(_exception.npc)>>
        <<printPersonLink _exceptionPerson ''>>

    <</for>>
    <<done>><<run $('.exceptionPerson').on('click',function(){
        var exceptionNpc = $(this).data('exception-npc');
        var exceptionEditor = $('#exceptionEditor').html('
Edit exception
\
            Slave:' + Person.getShortDescription(Person.get(exceptionNpc), true) + '
\
            <input type="checkbox" id="exceptionFeedEnabled">Feed this the slave when they have at least <input type="number" id="exceptionFeedAtHunger" style="width:3em" min="1" max="100">hunger.\

<button id="deleteExceptionBtn">Delete</button>');
        var exceptionIndex = $settings.cook.exceptions.findIndex(function(ex){return ex.npc == exceptionNpc});
        Player.bindSettingDom('exceptionFeedEnabled',$settings.cook.exceptions[exceptionIndex],'feedEnabled');
        Player.bindSettingDom('exceptionFeedAtHunger',$settings.cook.exceptions[exceptionIndex],'feedAtHunger');
        $('#deleteExceptionBtn').on('click',function(){
            exceptionEditor.html('');
            $settings.cook.exceptions.splice(exceptionIndex, 1);
            $('#exceptionList').html('').wiki('<<include cookFeedExceptions>>');
        });
    })>><</done>>
<<else>>
    (none)

<</if>>


code:
:: kitchen {"position":"900,625"}
<<scenery kitchen>>
<<if $settings.cook && $settings.cook.npc>><<set _cook=Person.get($settings.cook.npc)>><</if>>\
You are currently standing in your kitchen.<<if _cook>> _cook.name is in charge of cooking.<</if>>
<<unlessEmergency>>\
    <<wanderingSlaves>>\
    <<slaveEvents Kitchen>>
    <<keyOption [[Leave your kitchen|main]] 🚪>>
    <<keyAction 'Have a meal' ☕>>
        <<addMinutes 45>>
        <<set $player.energy = ($player.energy + 18).clamp(0, 100)>>
        <<run Engine.show()>>
    <</keyAction>>: 45min + 6 Energy
    <<if _cook>>\
        <<keyAction 'Set feeding rules' ⚙>>
            <<showCookFeedingRules>>
        <</keyAction>>
    <</if>>\
    <<sleepOption>>
<</unlessEmergency>>\


code:
:: pc {"position":"825,950"}
<<scenery pc>>
welcome to your computer.
<<unlessEmergency>>\
    <<slaveEvents BedRoom>>
    <<keyOption [[Leave your computer|bed]] 💻>>
    <<keyOption [[Watch porn|watchPorn]] 💗>>: 30min
    <<keyOption [[Shop online|onlineStore]] 🛒>>
    <<keyOption [[Tor Browser|DarkStore]] 🌐>>
    <<keyOption [[change your name|changenames]]  💬>>
<</unlessEmergency>>\

:: changenames {"position":"950,950"}
<<set _dialogTitle = 'Change ' + $player.name + " name to:";>>\
<<dialog _dialogTitle>>\
    <<textbox '_newName' $player.name>>
    \
        <<button "✅ (Enter) your">>\
            <<run
            if(!$player.originalName)
                $player.originalName = $player.name;
            _mewName = _newName.trim();
            $player.name = _newName ? _newName : $player.originalName;
            Engine.show();
            Dialog.close();
            >>\
        <</button>>\
    \
    \
        <<button "❌ (Esc) Cancel">>\
            <<run Dialog.close()>>\
        <</button>>\
    \
    <<run $(document).ready(function(){$('#textbox--newname').focus()})>>\
<</dialog>>
<<include pc>>
:: watchPorn {"position":"950,1075"}
<<addMinutes 30>>\
@@color:yellow;You spend the next half hour skimming through porn and <<if $player.gender == 'girl'>>buffing the muffin<<else>>jerking off<</if>>.@@
<<include pc>>\


code:
:: tortRafters
<<scenery tortRafters>>
You are currently in the corner of your basement where you suspend slaves from the rafters.
<<unlessEmergency>>\
    <<slaveEvents TortRafters>>

\

\
            <<set _slaves = TortRafters.getSlaves()>>\
            <<if _slaves.length > 0>>\
                Approach slave:
                <<set _charcode = 97>>\
                <<for _slave range _slaves>><<if _slave.location == 'tortRafters'>>\
                    <<set
                        if(_charcode == 104) _charcode += 2;
                        _char = String.fromCharCode(_charcode++);
                        _keyId = _char + "KeyAct";
                        _action = "<<openNpcInteraction slave " + _slave.uid + ">>";
                        _prefix = "(" + _char.toUpperCase() + ")"
                    >>\
                    \
                        <<printPersonLink _slave _action _prefix>>
                    \
                <</if>><</for>>\
            <</if>>\

\

    <<keyOption [[Return to main basement|basement]] 🚪>>\
    <<include workOption>>\
<</unlessEmergency>>\


code:
:: porn1 {"position":"525,950"}
<<addMinutes 30>>\
<<scenery porn1>>
    <<unlessEmergency>>\
        <<keyOption [[next video|porna]] 🔞>>
        <<keyOption [[mastubate|cumstop]] 💦>>
        <<addMinutes 30>>\
        <<sleepOption>>
    <</unlessEmergency>>\
:: porna {"position":"425,1100"}
<<addMinutes 30>>\
<<scenery porna>>
    <<unlessEmergency>>\
        <<keyOption [[mastubate|cumstop]] 💦>>
        <<addMinutes 30>>\
        <<sleepOption>>
    <</unlessEmergency>>\
:: cumstop {"position":"300,950"}
<<addMinutes 30>>\
<<scenery cumstop>>
    <<unlessEmergency>>\
        <<sleepOption>>
    <</unlessEmergency>>\


code:
:: wc {"position":"400,700"}
<<scenery wc>>
You are currently standing in your wc.
<<unlessEmergency>>\
    <<wanderingSlaves>>\
    <<slaveEvents Wc>>
    <<keyOption [[Leave your wc|main]] 🚪>>
    <<keyAction 'Go to the toilet' 🚽>><<addMinutes 5>><<run Engine.show()>><</keyAction>>: 5min
    <<if Player.getInventory().items.firstOrDefault(function(item){return item.name.startsWith('Used clothes')}) != null>>\
        <<keyOption [[Masturbate using stolen slave underwear|pantiescum]] 💦>>: 30min
    <</if>>\
    <<sleepOption>>
<</unlessEmergency>>\
:: pantiescum {"position":"400,825"}
<<addMinutes 30>>\
<<if $player.hasPenis>>\
    @@color:yellow;You wrap the underwear on your penis and fap until you cum in them.@@
    @@color:yellow;(Cum filled underwear added to your inventory)@@
<<else>>\
    @@color:yellow;You rub the underwear on your pussy till you cum.@@
    @@color:yellow;(Love juice stained underwear added to your inventory))@@
<</if>>\
<<include wc>>\
<<run
    if($player.hasPenis)
        Player.getInventory().add({name:'Cum filled underwear',description:'Underwear that was filled with your cum',count:1,tags:['slave','wearable','clothes','playerPresent']});
    else
        Player.getInventory().add({name:'Love juice stained underwear',description:'Underwear that was stained with your love juice',count:1,tags:['slave','wearable','clothes','playerPresent']});
>>\


code:
:: playground {"position":"550,500"}
<<scenery playground>>
<<personHunting>>
<<keyOption [[Keep searching|playground]] 🔍>>
<<keyOption [[Return home|home]] 🏠>>
:: kidnap {"position":"425,500"}
<<scenery home>>
<<addMinutes 30>>\
You start stalking the $found_kid.age year old kid waiting for the right moment.
<<run Player.removeItem("chloroform")>>
A while later you see the kid standing all alone in a corner. You look around making sure no one sees you and quickly use the chloroform \
on $found_kid.possessive mouth. (<<- Player.has('chloroform') ? Player.getInventory().get('chloroform').count : 'No'>> doses left)

Surprised, the kid takes a deep breath on the chloroform soaked handkerchief. You feel $found_kid.possessive strength quickly running out and \
$found_kid.possessive cute limbs falling right in your arms.

You cover $found_kid.possessive body in a blanket and sneak up back to your home with your new toy.
<<run Person.capture($found_kid)>>
<<keyOption [[Enter the basement|basement]] 🚪>>

:: CandyLure {"position":"250,450"}
<<scenery playground>>
<<addMinutes 3>>\
You sit on the park bench with a candy bar and wait for the $found_kid.age year old $found_kid.title to approach you. It takes a few minutes. But $found_kid.genPronoun finally approaches you.

<<run Player.removeItem("Candy")>>\
"Hi, my name's $found_kid.name and I was wondering if perhaps I can have that candy bar?"

You smile and say, "Unfortunately $found_kid.name, this is all I have on me. BUT, you see that house down the road?" You turn and point to your house.

"Yeah?" says the $found_kid.age yo $found_kid.title.

"Well, that's my house and I have a 30 lb. bag of candy downstairs. If you follow me back. You can help yourself." (<<- Player.has('Candy') ? Player.getInventory().get('Candy').count : 'No'>> pieces left)

"OK!" says the $found_kid.title with SURPRISING Enthusiasm.

The $found_kid.age yo $found_kid.title follows you closely. It takes 6 minutes to reach your house.<<addMinutes 6>>

You and the $found_kid.age yo enter your house. You and $found_kid.pronoun then head into the basement.
As soon as $found_kid.name realizes what's going on $found_kid.genPronoun tries to escape but you quickly grab and chain $found_kid.pronoun to the wall.
<<run Person.capture($found_kid)>>
<<keyOption [[Continue|basement]] ▶>>


code:
:: shool {"position":"550,375"}
<<scenery shool>>
<<personHunting>>
<<keyOption [[Keep searching|shool]] 🔍>>
<<keyOption [[Return home|home]] 🏠>>


code:
:: DarkStore {"position":"675,950"}
<<scenery deepweb>>
<<unlessEmergency>>\
    /*<<keyOption [[Buy a child|DarkStore]] 🧑💻>>*/\
    /*<<keyOption [[Buy torture object|DarkStore]] ⛓>>*/\
    /*<<keyOption [[Buy a vehicle|DarkStore]] 🚗>>*/\
    <<keyOption [[Sell a child|sellchild]] 💸>>
    <<keyOption [[Sell a dirty clothes|sellclothes]] 👙>>
    <<keyOption [[Leave your dark web|pc]] 💻>>
<</unlessEmergency>>\

:: sellchild {"position":"575,1100"}
<<scenery deepweb>>
Choose slave to sell:
<<set
    _charcode = 97;
    _slaves = $slaves.filter((slave) => slave.status!="servant");
>>\
<<for _slave range _slaves>>\
    <<set _value = Person.getValue(_slave)>>\
    <<capture _slave _value>>\
        <<set
            if(_charcode == 104) _charcode += 2;
            _char = String.fromCharCode(_charcode++);
            _linkText = '(' + _char.toUpperCase() + ') ' + Person.getShortDescription(_slave) + ' for ¤' + _value.total.toFixed(2);
            _keyId = _char + 'KeyAct';
        >>\
        <<link _linkText>>
                <<set
                    _dialogTitle = "Sell "+_slave.name;
                >>
                <<set _dialogTitle = _slave.name + "'s value">>
                <<dialog _dialogTitle>>
                    Obedience_value.obedienceRatio: ¤<<- _value.obedience.toFixed(2)>>
                    Lust_value.lustRatio: ¤<<- _value.lust.toFixed(2)>>
                    <<if _slave.hasPussy>>\
                        Pussy Training_value.pussyRatio: ¤<<- _value.pussy.toFixed(2)>>
                    <</if>>\
                    Anal Training_value.anusRatio: ¤<<- _value.anus.toFixed(2)>>
                    Oral Training_value.mouthRatio: ¤<<- _value.mouth.toFixed(2)>>
                    <<if _slave.freedomWish>>\
                        Freedom Wish Penalty: @@color:red;¤<<- Math.abs(_value.freedomWish).toFixed(2)>>@@
                    <</if>>\
                    <<if _value.virginType>>\
                        @@color:deeppink;Bonus _value.virginType: ¤<<- _value.virginBonus.toFixed(2)>>@@
                    <</if>>\
                    Total: ¤<<- _value.total.toFixed(2)>>

                    \
                        <<button "💵 (Space) Sell _slave.name">>\
                            <<set _dialogTitle = "Sell "+_slave.name>>\
                            <<dialog _dialogTitle>>\
                                Are you ''REALLY'' sure you want to sell out _slave.name for ¤<<- _value.total.toFixed(2)>>??
                                You won't see _slave.pronoun ever again!!
                                <<button "✅ Yes, sell _slave.name">>\
                                    <<cash _value.total>>\
                                    <<run Basement.deleteSlave(_slave);Dialog.close()>>\
                                    <<goto DarkStore>>\
                                <</button>>\
                                \
                                    <<button "🔙 (Esc) Go back">>\
                                        <<run Dialog.close()>>\
                                    <</button>>\
                                \
                            <</dialog>>\
                        <</button>>\
                    \
                    \
                        <<button "🔙 (Esc) Go back">>\
                            <<run Dialog.close()>>
                        <</button>>\
                    \
                <</dialog>><</link>>
    <</capture>>
<</for>>
<<keyOption [[Go back|DarkStore]]>>

:: sellclothes {"position":"700,1100"}
<<scenery deepweb>>
Choose item to sell:
<<set
    _charcode = 97;
    _clothes = Player.getInventory().items.filter((item) => item.tags.includes != undefined && item.tags.includes("clothes")  && !item.tags.includes("unsellable") && item.name != "Cooking apron");
>>\
<<for _item range _clothes>>\
    <<set _value = 20>>\
    <<capture _item _value>>\
        <<set
            if(_charcode == 104) _charcode += 2;
            _char = String.fromCharCode(_charcode++);
            _linkText = '(' + _char.toUpperCase() + ') ' + _item.name + ' for ¤' + _value;
            _keyId = _char + 'KeyAct';
        >>\
        <<link _linkText>>
            <<set
                _dialogTitle = "Sell "+_item.name;
            >>\
            <<dialog _dialogTitle>>\
                Do you want to sell _item.name for ¤<<- _value>>?
                <<button "✅ Yes, sell _item.name">>\
                    <<cash _value>>\
                    <<run Player.removeItem(_item.name)>>\
                    <<goto DarkStore>>\
                <</button>>\
                \
                    <<button "🔙 (Esc) Go back">>\
                        <<run Dialog.close()>>\
                    <</button>>\
                \
            <</dialog>><</link>>\

    <</capture>>
<</for>>
<<keyOption [[Go back|DarkStore]]>>


code:
:: npcInteraction [nobr] {"position":"625,0"}
<<scenery $scenery>>
<<unlessEmergency>>
    <<npcInteraction>>
<</unlessEmergency>>
:: slaveApproach {"position":"1000,0"}
$npc.name is a <<- Person.getLongDescription()>>.
You approach $npc.pronoun.
<<npcStats>>
<<if $npc.fear gt 90>>\  /*&& $npc.location != "tortRafters">>\*/
    $npc.GenPronoun <<thirdPerson "walks" "walk">> back scared and wodering what you will do to $npc.pronoun.
<<elseif $npc.hasPenis && ($npc.lust gte 80 || ($npc.uniqueness.naughty && $npc.lust gte 30)) && $npc.fear lt 25>>\
    $npc.GenPronoun <<thirdPerson "looks" "look">> at you with expectancy. <<if $npc.haveClothes>>@@color:deeppink;You can see a bump in $npc.possessive pants@@.<<else>>@@color:deeppink;$npc.genPronoun<<thirdPerson "'s" "'re">> already erect@@. <<emoji ❤>><</if>>
    (High lust made $npc.pronoun "happy" to see you)<<npcStimulated>>\
<<elseif ($npc.lust gte 80 || ($npc.uniqueness.naughty && $npc.lust gte 50)) && $npc.fear lt 25>>
    $npc.GenPronoun <<thirdPerson "looks" "look">> at you with expectancy. <<if !$npc.haveClothes>>@@color:deeppink;$npc.Possessive pussy is wet and leaking@@. <<emoji ❤>><</if>>
    (High lust made $npc.pronoun "happy" to see you)<<npcStimulated>>\
<<else>>\
    $npc.GenPronoun nervously <<thirdPerson "looks" "look">> at you with $npc.possessive $npc.eyeColor eyes.
<</if>>


code:
:: buyProduct {"position":"0,125"}
$buyProduct.description

<<set
    _own to OnlineStore.destination($buyProduct).get($buyProduct.name);
    _max to (!$buyProduct.available || ($buyProduct.available * $buyProduct.price gt $player.cash)) ?
        Math.floor($player.cash / $buyProduct.price) :
        $buyProduct.available;
    _countName to $buyProduct.itemName ? $buyProduct.itemName.replace(SugarCube.State.variables.buyProduct.name.toLowerCase(), "") : "";
>>\
<<if _own>>\
    You currently own _own.count<<- _countName>>.
<<else>>\
    You don't have any right now.
<</if>>\

<<if $buyProduct.available is not 1>>\
    How many? <input type="number" min="1" @max="_max" value="1" id="buyProductQuantity">
<</if>>\

\
    <<button "✅ (Space) Order">>\
        <<run
            var input = document.getElementById("buyProductQuantity");
            var quantity = input ? input.value : 1;
            if(quantity > 0) {
                var bought = OnlineStore.buy($buyProductIndex, quantity);
                if(bought) {
                    Dialog.close();
                    Engine.show();
                }
            }
        >>\
    <</button>>\
\
\
    <<button "❌ (Esc) Cancel">>\
        <<run Dialog.close()>>\
    <</button>>\
\
:: onlineStore [nobr] {"position":"825,1075"}

Anazom

Next day delivery

        <<for _productIndex to 0; _productIndex lt $onlineStore.products.length; _productIndex++>>
            <<set
                _product = $onlineStore.products[_productIndex];
                _priceStyle = "color:" + (_product.price > $player.cash ? "red" : "#8af");
            >>
            <<if !_product.soldOut>>

                        <<itemImage _product.name>>

<<- OnlineStore.productText(_product)>><<-  OnlineStore.priceText(_product)>>

            <</if>>
        <</for>>

            <<if State.peek(1).title != State.passage>>
                <<set $returnPassage to State.peek(1).title>>
            <</if>>
            <<keyOption [[Return|$returnPassage]] 🔙>>

            You currently own ¤<<- $player.cash.toFixed(2)>>

:: receivePacket {"position":"525,775"}
<<scenery home>>
''Delivery man'': "I have a delivery for you"

You received: <<- $onlineStore.bought.items.map(function(item){return item.count + 'x ' + item.name.charAt(0).toUpperCase() + item.name.slice(1)}).join(', ').replace(/, ((?:.(?!, ))+)$/, ' and $1')>>

<<if !Player.hasAchievement("boughtMattress") && OnlineStore.isBought("mattress")>>\
    <<set $achievements.push("boughtMattress")>>\
    ''Delivery man'': "Do you need help you carrying the Mattress?<<emoji 🙂>>"
    ''$player.name'': "NO!!.. No thanks, I can do it myself.<<emoji 😅>>"

<</if>>\
<<unlessEmergency>>\
    <<keyOption [[Go back inside|main]] 🔙>>
    <<include workOption>>\
<</unlessEmergency>>
<<run OnlineStore.receiveBought()>>\


code:
:: widgets [widget nobr] {"position":"425,300"}
<<widget emoji>><<if $settings.emoji === false>><<set _style to "display:none">><</if>>_args[0]<</widget>>
<<widget addMinutes>>
    <<run Now.addMinutes(_args[0])>>
<</widget>>
<<widget addHours>>
    <<run Now.addHours(_args[0])>>
<</widget>>
<<widget cash>>
    <<run $player.cash = Math.round(($player.cash + _args[0]) * 100) / 100
        ;_cashAction = _args[0] < 1 ? 'lost' : 'received'
        ;_cashColor = _args[0] < 1 ? 'red' : 'green'>>
    @@color:_cashColor;You _cashAction ¤<<- Math.abs(_args[0]).toFixed(2)>>@@
<</widget>>
<<widget workOption>>
    <<set _workEmoji = $player.gender != "boy" ? "👷♀" : "👷♂"; _workText = "Go to work" + (Now.isWeekend()?" (Optional)":"")>>
    <<keyOption [[_workText|work]] _workEmoji>>
<</widget>>
<<widget sleep>>
    <<set $player.sleeping = true;
        Now.skipTo("7:00 AM")>>
    <<goto bed>>
<</widget>>
<<widget unlessEmergency container>>
    <<if $player.energy lt 1>>
        @@color:red;You REALLY need to sleep@@

        <<keyAction 'Go to sleep' 😴>><<sleep>><</keyAction>>
    <<else>>
        <<if $player.job && !Now.isWeekend() && !$player.workedToday && Now.isEqualOrLaterThan($player.job.enterTime)>>
            @@color:red;You need to go to work@@

            <<workOption>>
        <<else>>
            _contents
        <</if>>
    <</if>>
<</widget>>
<<widget checkNpcVirgin>>
    <<set _fieldName to _args[0] + 'Virgin'>>
    <<if $npc[_fieldName]>>
        <<set
            $npc[_fieldName] = false;
            Person.setAchievement("playerTook" + _args[0].toUpperFirst() + "Virginity");
        >>

@@color:red;$npc.name just lost $npc.possessive _args[0] virginity!!!@@

    <<elseif _args[0] != 'mouth' && !Person.hasAchievement("playerTook" + _args[0].toUpperFirst() + "Virginity") && !Person.hasAchievement("playerRealizedNonVirgin")>>

@@color:yellow;You realize that $npc.name was not a virgin!!@@

        <<run Person.setAchievement("playerRealizedNonVirgin")>>
    <</if>>
    <<run Person.setAchievement('had' + _args[0].toUpperFirst() + 'SexWithPlayer')>>
<</widget>>
<<widget userEditListVar>>
    <<set _list = eval(_args[0]).join('\n')>>
    <<dialog _args[1]>>
        <<textarea "_newList" _list>>

        <<button "✅ Save">>
            <<run eval(_args[0] + '=' + JSON.stringify(_newList.split(/\r?\n/)));Dialog.close()>>
        <</button>>
        <<if _args[2]>>
            <<button "♻ Reset to default">><<set
                _newList = eval(_args[2]).join('\n');
                $('#textarea--newlist').val(_newList);
            >><</button>>
        <</if>>
        <<button "❌ (Esc) Cancel">>
            <<run Dialog.close()>>
        <</button>>
    <</dialog>>
<</widget>>
<<widget npcAddressPlayer>><<- Player.getAddressing(_args[0] || $npc)>><</widget>>
<<widget npcStats>>
    <<set _npc to _args[0] || $npc>>
    <<if typeof _npc == 'number'>>
        <<set _npc to Person.get(_npc)>>
    <</if>>
    <<run
        ['love', 'obedience', 'lust', 'pussyTraining', 'anusTraining', 'mouthTraining'].forEach(function(name) {
            Meter.del(name);
            Meter.add(name, {
                label: name.beautifyStat() + ':' + _npc[name],
                height: '20px'
            }, _npc[name] / 100);
        });
        ['freedomWish', 'hunger', 'fear'].forEach(function(name) {
            Meter.del(name);
            Meter.add(name, {
                label: name.beautifyStat() + ':' + _npc[name],
                height: '20px',
                full: '#FF4136',
                empty: '#2ECC40'
            }, _npc[name] / 100);
        });
    >>

        <<if _npc.fear>>

                <<showmeter fear>>

        <</if>>
        <<if _npc.love>>

                <<showmeter love>>

        <</if>>
        <<if _npc.obedience>>

                <<showmeter obedience>>

        <</if>>

        <<if _npc.lust>>

                <<showmeter lust>>

        <</if>>
        <<if _npc.freedomWish>>

                <<showmeter freedomWish>>

        <</if>>
        <<if _npc.hunger>>

                <<showmeter hunger>>

        <</if>>

        <<if _npc.pussyTraining>>

                <<showmeter pussyTraining>>

        <</if>>
        <<if _npc.anusTraining>>

                <<showmeter anusTraining>>

        <</if>>
        <<if _npc.mouthTraining>>

                <<showmeter mouthTraining>>

        <</if>>

    <<unset _npc>>
<</widget>>
<<widget personProperties>>
    <<npcStats _args[0]>>
    <<set _npc to _args[0] || $npc>>
    <<if typeof _npc == 'number'>>
        <<set _npc to Person.get(_npc)>>
    <</if>>
    location: _npc.location <<button "Go there" _npc.location>><<run Dialog.close()>><</button>>

    <<if $cheat>>\
        Personality preset: $npc.uniqueness.name <<button change>><<set _dialogTitle = "Personality for " + _npc.name>><<dialog _dialogTitle>>
            <<for _presetIndex = 0; _presetIndex lt PersonUniquenessPresets.length; _presetIndex++>>
                <<set _preset = PersonUniquenessPresets[_presetIndex]>>
                <<print '<<button "_preset.name (' + Person.getPersonalityDescription(_preset) + ')">><<set _npc.uniqueness = PersonUniquenessPresets[' + _presetIndex + '];Dialog.close()>><</button>>'>>
                <<unset _preset>>
            <</for>>
        <</dialog>><</button>>

        Characteristics: <<- Person.getPersonalityDescription($npc.uniqueness)>>
    <</if>>
<</widget>>
<<widget npcGenderSymbol>>
    <<set _genderSymbol = _args[0].sex == 'male' ? '♂' : _args[0].sex == 'female' ? '♀' : '⚤'>>
    <<emoji _genderSymbol>><<unset _genderSymbol>>
<</widget>>
<<widget printPersonLink>>
    <<npcGenderSymbol _args[0]>>
    <<print "<<link '" + (_args[2] ? _args[2] + ' ' : '') +  Person.getShortDescription(_args[0]) + "'>>" + _args[1] + "<</link>>">>
<</widget>>
<<widget wanderingSlaves>>
    <<set _wandering = Person.getWandering()>>
    <<if _wandering.length>>
        <<if _wandering.length == 1>>
            <<set _linkText = "(A) " + _wandering[0].name>>
            <<link _linkText>><<openNpcInteraction houseSlave _wandering[0].uid>><</link>> is here.

        <<else>>
            <<set
                _last = _wandering.pop();
                _charcode = 97;
            >>
            <<for _slave range _wandering>>
                <<set
                    if(_charcode == 104) _charcode += 2;
                    _char = String.fromCharCode(_charcode++);
                    _linkText = '(' + _char.toUpperCase() + ') ' + _slave.name;
                    _keyId = _char + 'KeyAct';
                >><<print '<<link _linkText>><<openNpcInteraction houseSlave ' + _slave.uid + '>><</link>>'>>,
            <</for>>
            <<set
                _char = String.fromCharCode(_charcode);
                _linkText = '(' + _char.toUpperCase() + ') ' + _last.name;
                _keyId = _char + 'KeyAct';
            >>
            and
                <<link _linkText>><<openNpcInteraction houseSlave _last.uid>><</link>>
             are here.

        <</if>>
    <</if>>
<</widget>>
<<widget playerSay>>''$player.name'': "_args[0]"<</widget>>
<<widget npcSay>>
    <<set _npc = _args[1] ? _args[1] : $npc>>
    ''_npc.name'': "_args[0]"
    <<unset _npc>>
<</widget>>
<<widget personHunting>>
    <<set $found_kid = Person.generate($settings.childGeneration)>>
    You found a <<- Person.getLongDescription($found_kid)>> named $found_kid.name

    <<if Player.has('magic sunglasses')>>
        <<checkbox '$settings.usingMagicSunglasses' false true autocheck>> Put on magic sunglasses.<<emoji 😎>>

        <<set _npcStatsStyle = $settings.usingMagicSunglasses ? '' : 'display:none'>>

            <<npcStats $found_kid>>
            Traits: <<- Person.getPersonalityDescription($found_kid.uniqueness)>>

    <</if>>
    <<set
        _hasChloroform = Player.has('chloroform');
        _hasCandy = Player.has('Candy');
    >>
    <<if !_hasChloroform and !_hasCandy>>
        (You don't have anything at your disposal to obtain kids)

    <<elseif Basement.availableBeds() lte 0>>
        (Not enough space for a new kid back at home)

    <<else>>
        <<if _hasCandy>>
            <<keyOption [[Offer $found_kid.name some Candy.|CandyLure]] 🍬>>: 9min

        <</if>>
        <<if _hasChloroform>>
            <<keyOption [[Kidnap $found_kid.pronoun|kidnap]] 🖐>>: 30min

        <</if>>
    <</if>>
<</widget>>
<<widget sleepOption>>
    <<if !$player.job or $player.workedToday or Now.isWeekend() or Now.isBetween('12:00 AM', '06:59 AM')>>
        <<set
            _sleepSlave = $slaves.firstOrDefault(function(slave){return Person.hasAchievement('okSleepWithPlayer', slave)});
            _sleepSentence = $scenery != 'bed' ? "Go sleep" : 'Sleep';
        >>
        <<if _sleepSlave>>
            <<set _sleepSentence+=' with ' + _sleepSlave.name>>
            <<keyAction _sleepSentence 🥰>>
                <<set $scenery = 'bed'>>
                <<openNpcInteraction 'okSleepWithPlayerDemand' _sleepSlave.uid>>
            <</keyAction>>
        <<else>>
            <<keyAction _sleepSentence 😴>><<sleep>><</keyAction>>
        <</if>>
    <</if>>
<</widget>>
<<widget thirdPerson>><<if $npc.gender != 'nb'>>$args[0]<<else>>$args[1]<</if>><</widget>>
<<widget slaveEvents>>
    <<set _eventSlaves = window[_args[0]].getEventSlaves()>>
    <<set _eventIndex to 0>>
    <<include slaveEvent>><<if _eventSlaves.length>>
<</if>>
<</widget>>
<<widget showCookFeedingRules>>
    <<dialog "Cook feeding rules">>
        <input type="checkbox" id="feedEnabled">Feed all the slaves that have at least <input type="number" id="feedAtHunger" style="width:3em" min="1" max="100">hunger.

        Those slaves will be fed at 7AM, 1PM and 7PM

                Special cases:

                <<include cookFeedExceptions>>

<button id="addExceptionBtn">Add exception</button>

        <<done>><<run
            Player.bindSettingDom('feedEnabled',$settings.cook,'feedEnabled');
            Player.bindSettingDom('feedAtHunger',$settings.cook,'feedAtHunger');
            $('#addExceptionBtn').on('click',function(){
                var exceptionEditor = $('#exceptionEditor').html('
New exception
\
                    Slave:\
                    <select id="exceptionSlaveSelect" style="width:100%"></select>\
                    <input type="checkbox" id="exceptionFeedEnabled" checked>Feed this the slave when they have at least <input type="number" id="exceptionFeedAtHunger" style="width:3em" min="1" max="100" value="20">hunger.\

<button id="saveExceptionBtn">Save</button>');
                var exceptionSlaveSelect = $('#exceptionSlaveSelect').select2({
                    placeholder:"Select slave...",
                    dropdownParent:exceptionEditor,
                    data:$slaves.filter(function(slave) {
                        return !$settings.cook.exceptions.find(function(ex){ return ex.npc == slave.uid; });
                    }).map(function(slave){return {
                        id: slave.uid,
                        text: Person.getShortDescription(slave, true)
                    }})
                }).val(null).trigger('change');
                $('#saveExceptionBtn').on('click',function(){
                    var slaveId=exceptionSlaveSelect.val();
                    if(!slaveId)return;
                    $settings.cook.exceptions.push({
                        npc:slaveId,
                        feedEnabled:$('#exceptionFeedEnabled').is(':checked'),
                        feedAtHunger:$('#exceptionFeedAtHunger').val()
                    });
                    exceptionEditor.html('');
                    $('#exceptionList').html('').wiki('<<include cookFeedExceptions>>');
                });
            });
        >><</done>>
    <</dialog>>
<</widget>>
<<widget takeClothes>>
    <<set _item = {
        name:'Used clothes(' + $npc.age + ' y.o.)',
        description:'Used clothes from a ' + $npc.age + ' year old',
        count:1,
        tags:["person","clothes","used"]
        };
        Player.getInventory().add(_item);>>
    (_item.description added to your inventory)
<</widget>>
:: workOption {"position":"875,0"}
<<if $player.job && !$player.workedToday && Now.isBetween("7:00 AM", "2:00 PM")>>\
    <<workOption>>
<</if>>\


code:
:: work {"position":"700,250"}
<<scenery dump>>
<<set if(!$player.job.hours)$player.job.hours=8>>\
<<addHours $player.job.hours>>\
<<set $player.workedToday = true>>\
You spend $player.job.hours hours working on your job as $player.job.name
<<cash $player.job.pay>>
You currently own ¤<<- $player.cash.toFixed(2)>>

<<keyOption [[Return home|home]] 🏠>>
<<if $slaves.length gt 1 >>
    <<set
        _hungryBasementSlaveCount = Basement.getHungrySlaves().length;
        _neededPizzas = Math.ceil((_hungryBasementSlaveCount * 3) / 8);
        _pizzasPrice = _neededPizzas * 9;
    >>\
    <<if _hungryBasementSlaveCount && !Player.has("pizza", _neededPizzas) && $player.cash gte _pizzasPrice>>\
        <<keyAction "Go buy pizza for all the hungry slaves in the basement (¤_pizzasPrice) and return home" 🍕>>\
            <<run
                $player.cash -= _pizzasPrice;
                Player.getInventory().add({
                    name:'Pizza',
                    description:'Median basic pizza',
                    count:_neededPizzas,
                    tags: ["player", "food", "consumable"]
                })
            >>
            <<addMinutes 10>>
            <<goto home>>
        <</keyAction>>: 10min detour\
    <</if>>\
<</if>>\
:: home {"position":"700,375"}
<<scenery home>>
You are currently in front of your home.
<<unlessEmergency>>
    <<keyOption [[Go inside|main]] 🚪>>
    <<include workOption>>\
    <<keyOption [[Go to the park|playground]] ⚽>>
    <<keyOption [[Go to the school|shool]] 🏫>>
<</unlessEmergency>>\


code:
sugarcube-2:
  macros:
    keyOption:
      name: keyOption
    keyAction:
      name: keyAction
      container: true
    addMinutes:
      name: addMinutes
    addHours:
      name: addHours
    cash:
      name: cash
    unlessEmergency:
      name: unlessEmergency
      container: true
    workOption:
      name: workOption
    updateVariables:
      name: updateVariables
    scenery:
      name: scenery
    newmeter:
      name: newmeter
      container: true
    label:
      name: label
    colors:
      name: colors
    showmeter:
      name: showmeter
    popup:
      name: popup
    dialog:
      name: dialog
      container: true
    npcInteraction:
      name: npcInteraction
    sizing:
      name: sizing
    itemImage:
      name: itemImage
    openNpcInteraction:
      name: openNpcInteraction
    emoji:
      name: emoji
    sleep:
      name: sleep
    userEditListVar:
      name: userEditListVar
    npcGenderSymbol:
      name: npcGenderSymbol
    wanderingSlaves:
      name: wanderingSlaves
    personHunting:
      name: personHunting
    npcAddressPlayer:
      name: npcAddressPlayer
    sleepOption:
      name: sleepOption
    npcStimulated:
      name: npcStimulated
    slaveEvents:
      name: slaveEvents
    printPersonLink:
      name: printPersonLink
    npcStats:
      name: npcStats
    showCookFeedingRules:
      name: showCookFeedingRules
    thirdPerson:
      name: thirdPerson
    playerSay:
      name: playerSay
    npcSay:
      name: npcSay
    takeClothes:
      name: takeClothes


code:
let Homes: Record<string, Home> = {
  smallUrban: {
    name: "small urban house",
    rent: 400, //Unused right now, will be the amount to pay each month
    spaces: [
      "basement",
      "tortRafters",
      "mainRoom",
      "bed",
      "bathroom",
      "wc",
      "garden",
      "kitchen",
    ], //The spaces are where the wandering slaves will move into excluding the basement and everything starting with "tort"
  },
};
//Planned to be events involving NPC that can happen when the player enters the same scenery (found slave sleeping, playing with the computer, taking a piss, etc...)
//In the eyes of the player it should appear in the same way as the demands.
interface NpcEvent {
  description: string;
  canBeShown?: (npc: Npc) => boolean;
}
abstract class HomeSpace {
  contents: Inventory = new Inventory();
  //The higher the muffle the lower the chance of neighbors/passerby hearing loud noises from this room (planned)
  //This indicates the base value, items could be bought to increase this value.
  muffleBase: number = 25;
  //How secure is this space (The higher is, the harder is for slaves to escape this space, or the house if it has an exit)
  securityBase: number = 25;
  npcEvents: NpcEvent[] = [];
  //The main passage associated with this room
  abstract passageName: string;
  getEventSlaves(): Person[] {
    const variables = Variables();
    const slaves = variables.slaves as Person[];
    let candidates: Person[] = [];
    for (let slaveIndex = 0; slaveIndex < slaves.length; slaveIndex++) {
      const slave = slaves[slaveIndex];
      if (slave.location != this.passageName || slave.age < 1) continue;
      if (
        this.passageName == "kitchen" &&
        variables.settings.cook &&
        slave.uid == variables.settings.cook.npc
      ) {
        slave.eventType = "ordinary";
        slave.event = "cook";
        candidates.push(slave);
        continue;
      }
      if (slave.hunger >= 25) {
        slave.eventType = "demand";
        slave.event = "hunger";
        candidates.push(slave);
        continue;
      }
      if (
        slave.status != "slave" && //The status slave means locked in the basement
        slave.age > 1 &&
        slave.love > 49 &&
        !slaves.firstOrDefault((slave: Person) =>
          window.Person.hasAchievement("okSleepWithPlayer", slave)
        )
      ) {
        var seed = PseudoRandom.getSeed(slave.name, slave.age, turns());
        if (PseudoRandom.either(seed, [true, false])) {
          slave.eventType = "demand";
          slave.event = "sleepWithPlayer";
          candidates.push(slave);
          continue;
        }
      }
    }
    return candidates.sort(() => PseudoRandom.getFloat(turns()) - 0.5);
  }
}
class Basement extends HomeSpace {
  muffleBase: number = 90;
  passageName: string = "basement";
  constructor() {
    super();
    window.OnlineStore.get("Mattress").transferTo(this.contents);
  }
  has(itemName: string, count: number = 1): boolean {
    return new Inventory(Variables().basement.contents).has(itemName, count);
  }
  availableBeds(): number {
    let variables = Variables();
    var contents = new Inventory(variables.basement.contents);
    var oldItem = contents.get("Matress");
    if (oldItem) oldItem.name = "Mattress";
    return contents.get("Mattress").count - variables.slaves.length;
  } //TODO: do not count slaves in player's bed
  getHungrySlaves(): Person[] {
    return Variables().slaves.filter((slave: Person) => slave.hunger > 25);
  }
  getSlaves(): Person[] {
    return Variables().slaves.filter(
      (slave: Person) => (slave.location == "basement")
    );
  }
  deleteSlave(slave: Person) {
    let slaves: Person[] = Variables().slaves;
    let index = slaves.indexOf(slave);
    if (index != -1) slaves.splice(index, 1);
  }
}
class TortRafters extends HomeSpace {
  muffleBase: number = 90;
  passageName: string = "tortRafters";
  getSlaves(): Person[] {
    return Variables().slaves.filter(
      (slave: Person) => (slave.location == "tortRafters")
    );
  }
}
class MainRoom extends HomeSpace {
  passageName: string = "mainRoom";
}
class BedRoom extends HomeSpace {
  bedAssignedNpc: Uid[] = [];
  passageName: string = "bed";
}
class Bathroom extends HomeSpace {
  passageName: string = "bathroom";
}
class Wc extends HomeSpace {
  passageName: string = "wc";
}
class Kitchen extends HomeSpace {
  passageName: string = "kitchen";
}
class Garden extends HomeSpace {
  muffleBase: number = 0;
  passageName: string = "garden";
}
//class TortureRoom extends HomeSpace {
//  muffleBase: number = 90;
//  passageName: string = "tort";
//  cages: [];
//}


code:
interface Array<T> {
  firstOrDefault<T>(predicate: Function): T;
}
//Array extension to return the first item that makes the predicate return true or null
Array.prototype.firstOrDefault = function <T>(predicate: Function) {
  return this.reduce((accumulator: T, currentValue: T) => {
    if (!accumulator && predicate(currentValue)) accumulator = currentValue;
    return accumulator;
  }, null);
};
//A product does not represent a physical item but something that can be bought and transfer a corresponding item to an inventory.
class Product {
  name: string; //Product name displayed. If itemName is not specified it will also be the item's name.
  itemName?: string; //The item's name (once purchased)
  price: number;
  description: string;
  packQuantity?: number = 1; //How many items are transferred to the inventory for each product bought
  available?: number; //How many times this product can be purchased. This value will decrease after each purchase and set soldOut to true when it reaches zero.
  soldOut?: boolean = false; //If set to true it will not appear available for purchasing.
  tags: Set<string>; //Keywords related to the product. The first keyword indicates where the item go after receiving it. It will be used to filter the products in the future.
  constructor(init?: Product) {
    Object.assign(this, init);
  }
  transferTo?(inventory: Inventory, count: number = 1) {
    inventory.add({
      name: this.itemName ? this.itemName : this.name,
      description: this.description,
      count: count * this.packQuantity,
      tags: this.tags,
    });
  }
}
//Represents a physical item.
class Item {
  name: string;
  description: string;
  count?: number = 1;
  tags: Set<string>; //Keywords related to this item. It will be used to filter the items in a inventory in the future.
  constructor(init?: Item) {
    Object.assign(this, init);
  }
}
class Inventory {
  items: Item[] = [];
  add(item: Item): void {
    let existing: Item = this.items.firstOrDefault(
      (i: Item) => i.name == item.name
    );
    if (existing) existing.count = (existing.count || 1) + (item.count || 1); // Add to existing item, taking into account that not all items have a count
    else this.items.push(new Item(item));
  }
  remove(item: Item, count: number = 1): void {
    if (!item) return;
    if (!count || count >= item.count) this.items.delete(item);
    else item.count -= count;
  }
  get(name: string): Item {
    let found: Item = this.items.firstOrDefault(
      (i: Item) => i.name.toLowerCase() == name.toLowerCase()
    );
    if (!found)
      found = this.items.firstOrDefault(
        (i: Item) => i.name.split(" ")[0].toLowerCase() == name.toLowerCase()
      );
    return found;
  }
  removeByName(name: string, count: number = 1): void {
    this.remove(this.get(name), count);
  }
  has(itemName: string, count: number = 0): boolean {
    let item: Item = this.get(itemName);
    if (item === null) return false;
    if (!count) return true;
    return item.count >= count;
  }
  hasAll(itemNames: string[]): boolean {
    return itemNames.countWith((n) => this.has(n)) == itemNames.length;
  }
  //Moves an item from this inventory to another (including all quantity of that item)
  move(itemIndex: number, destination: Inventory): void {
    destination.add(this.items[itemIndex]);
    this.items.deleteAt(itemIndex);
  }
  //Moves an item from this inventory to another (including all quantity of that item)
  moveByName(itemName: string, destination: Inventory): void {
    let item = this.get(itemName);
    if (item === null) return;
  }
  //Moves some of an item from this inventory to another, optionally leaving some behind
  moveByNameAndCount(itemName: string, count: number, destination: Inventory): void {
    let item = this.get(itemName);
    if (item === null) return;
    if (count >= item.count) {
      //Moving all items
      destination.add(item);
      this.items.delete(item);
    } else {
      let newItem = {...item};
      newItem.count = count;
      item.count -= count;
      destination.add(item);
    }
  }
  //Remove all items in this inventory
  clear(): void {
    this.items = [];
  }
  constructor(init?: Partial<Inventory>) {
    Object.assign(this, init);
  }
}
//The only store available right now, if more are created maybe a parent abstract class should be created.
class OnlineStore {
  //Updating this class does not automatically updates the $onlineStore story variable unless a new product is added, the version number is used to know if the story variable object should be updated when loading an old save.
  version: number = 3;
  products: Product[] = [
    new Product({
      name: "Chloroform",
      itemName: "chloroform doses",
      description:
        "Helps getting a good sleep, and in your life in general. Can be used on others.",
      price: 33,
      tags: new Set(["player", "consumable", "capture"]),
      packQuantity: 10,
    }),
    new Product({
      name: "Mattress",
      description:
        "Adds an additional bed to your basement so another slave can move in.",
      price: 70,
      tags: new Set(["basement", "home", "furniture"]),
    }),
    new Product({
      name: "Lube",
      description:
        "Slippery lotion. Can be used for easier screwing, among other things<<emoji 😏>>",
      price: 15,
      tags: new Set(["player", "sex", "unsellable"]),
      available: 1,
    }),
    new Product({
      name: "Candy",
      description: "Most people like these. Kids, especially<<emoji 🤭>>.",
      price: 5,
      tags: new Set(["player", "lure", "bribery", "capture", "food"]),
    }),
    new Product({
      name: "Sleeping pills",
      description: "Sweet dreams are made of these.<<emoji 💤>>.",
      price: 12,
      tags: new Set(["player", "capture", "medicine"]),
      available: 20,
      soldOut: true, //Hidden product, for now
    }),
    new Product({
      name: "Lactation pills",
      description: "Got milk? Induces lactation (breasts reqiured)<<emoji 🥛>>.",
      price: 30,
      tags: new Set(["player", "consumable", "medicine"]),
    }),
    new Product({
      name: "Dildo",
      description: "Stimulating toy to play until complete satisfaction.",
      price: 20,
      tags: new Set(["player", "sex", "toy"]),
      available: 1,
    }),
    new Product({
      name: "Condom",
      description:
        "The condom or condom is a thin and flexible case, impermeable to blood",
      price: 8,
      tags: new Set(["player", "sex", "toy"]),
    }),
    new Product({
      name: "Magic sunglasses",
      description:
        "These hi-tec sunglasses detect subtle variations on people behavior and allows you to see more details about them.",
      price: 500,
      available: 1,
      tags: new Set(["player", "wearable", "eyes", "tech", "cheat", "unsellable"]),
    }),
    new Product({
      name: "Cooking apron",
      description:
        "Cute apron to give to a slave that you assigned as a cook. They will surely use it when cooking to protect their cute body, even if no other clothes are present<<emoji 👀>>",
      price: 20,
      available: 1,
      tags: new Set(["player", "wearable", "cooking", "clothes", "unsellable"]),
    }),
    new Product({
      name: "Rope",
      description: "A stong sturdy rope",
      price: 20,
      tags: new Set(["player", "sex", "toy"]),
    }),
  ];
  bought: Inventory = new Inventory(); //Bought products are transferred to this inventory until delivered (where they are transferred to their destination)
  //Get a product from the store
  get(name: string): Product {
    let store: OnlineStore = Variables().onlineStore as OnlineStore;
    if (!store) store = this;
    return store.products.firstOrDefault(
      (p: Product) => p.name.toLowerCase() == name.toLowerCase()
    );
  }
  //Check if a product on the store can be bought with the current available player's money.
  canBuy(productIndex: number, count: number = 1): boolean {
    let variables = Variables();
    let player = variables.player as Player;
    let store = variables.onlineStore as OnlineStore;
    let product = store.products[productIndex];
    return player.cash >= product.price * count;
  }
  //Performs a purchase on a product
  buy(productIndex: number, count: number = 1): boolean {
    let variables = Variables();
    let player = variables.player as Player;
    let store = variables.onlineStore as OnlineStore;
    let product = new Product(store.products[productIndex]);
    let total = product.price * count;
    if (total > player.cash) return false;
    store.bought = new Inventory(store.bought);
    product.transferTo(store.bought, count);
    player.cash = Math.round((player.cash - total) * 100) / 100;
    if (product.available > 0) {
      if (product.available <= count)
        variables.onlineStore.products[productIndex].soldOut = true;
      else variables.onlineStore.products[productIndex].available -= count;
    }
    return true;
  }
  //Gets the product or item final destination
  destination(product: Product | Item): Inventory {
    switch ([...product.tags][0]) {
      case "basement":
        let basement = Variables().basement as Basement;
        basement.contents = new Inventory(basement.contents);
        return basement.contents;
      default:
        let player = Variables().player as Player;
        player.inventory = new Inventory(player.inventory);
        return player.inventory;
    }
  }
  //Check if an item it's bought and has a pending delivery
  isBought(itemName: string): boolean {
    return new Inventory(Variables().onlineStore.bought).has(itemName);
  }
  //Transfer all bought products to the respective destinations.
  receiveBought(): void {
    let variables = Variables();
    let store = variables.onlineStore as OnlineStore;
    let player = variables.player as Player;
    let basement = variables.basement as Basement;
    for (let index = 0; index < store.bought.items.length; index++) {
      const item = store.bought.items[index];
      switch ([...item.tags][0]) {
        case "basement":
          basement.contents = new Inventory(basement.contents);
          basement.contents.add(item);
          break;
        default:
          player.inventory = new Inventory(player.inventory);
          player.inventory.add(item);
          break;
      }
    }
    store.bought = new Inventory(store.bought);
    store.bought.clear();
  }
  //Checks if there are items pending delivery
  pendingOrder(): boolean {
    let store = Variables().onlineStore as OnlineStore;
    return store.bought.items.length > 0;
  }
  //Builds a price string to be displayed
  priceText(product: Product): string {
    return "¤" + product.price;
  }
  //Builds a text to display as a product name
  productText(product: Product): string {
    let text = product.name;
    if (product.packQuantity > 1) text += " x " + product.packQuantity;
    return text + ": ";
  }
}


code:
interface Job {
  name: string;
  pay: number;
  enterTime: string; //Not exactly the enter time but the time that the player absolutely needs to go. Forced by unlessEmergency widget.
  workHours: number; //How many hours this job will take. More hours means more energy depleted too.
}
let Jobs: Record<string, Job> = {
  garbageCollector: {
    name: "garbage collector",
    pay: 37.38, //Math.round((800 / 21.74) * 100) / 100, //800 a month divided by the average days per month with two decimal places
    enterTime: "8:30 AM",
    workHours: 8,
  },
  //Teacher: {
  //  name: "Teacher shool",
  //  pay: Math.round((1000 / 11.14) * 100) / 100,
  //  enterTime: "8:00 AM",
  //},
};


code:
const keyToId: Record<string, string> = {
  "1": "one",
  "2": "two",
  "3": "three",
  "4": "four",
  "5": "five",
  "6": "six",
  "7": "seven",
  "8": "eight",
  "9": "nine",
  "0": "zero",
  " ": "space",
  Escape: "esc",
}; //These are the keys that do not have the same text as the names of the key trigger classes.
let keyOptionNumber: number;
$(document).on(":passagestart", () => (keyOptionNumber = 1));
//Adds an option that leads to a passage that can be selected by pressing a number. The number is assigned automatically.
//Parameters:
//1. SugarCube link. Example: [[text|passageName]]
//2. (optional) emoji or emojis that will appear at the start if emojis are enabled in the settings.
//3. (optional) widget used. By default <<link>> is used but "btn" can be specified to show a button instead.
//Example: <<keyOption [[My text|myPassage]] 🎃 btn>>
Macro.add("keyOption", {
  handler: function () {
    let $wrapper = $(document.createElement("span"));
    let emoji =
      this.args.length > 1 ? this.args[1] : "'    '";
    let widget =
      this.args.length > 2 && this.args[2] == "btn" ? "button" : "link";
    $wrapper
      .wiki(
        `<<${widget} "<<emoji ${emoji}>>(${keyOptionNumber}) ${this.args[0].text}" "${this.args[0].link}">><</${widget}>>`
      )
      .attr("id", keyToId[keyOptionNumber] + "Act")
      .appendTo(this.output);
    keyOptionNumber = (keyOptionNumber + 1) % 10;
  },
});
//Adds an option that executes a sugarcube markup when selected and can be selected by pressing a number. The number is assigned automatically.
//Parameters:
//1. Text to show in the option
//2. (optional) emoji or emojis that will appear at the start if emojis are enabled in the settings.
//3. (optional) widget used. By default <<link>> is used but "btn" can be specified to show a button instead.
//Body: The sugarcube markup to be executed when the option is selected.
//Example <<keyAction 'myText' 🎃 btn>><<run alert('action executed')>><</keyAction>>
Macro.add("keyAction", {
  tags: null,
  handler: function () {
    let $wrapper = $(document.createElement("span"));
    let emoji =
      this.args.length > 1 ? this.args[1] : "'    '";
    let widget =
      this.args.length > 2 && this.args[2] == "btn" ? "button" : "link";
    $wrapper
      .wiki(
        `<<${widget} "<<emoji ${emoji}>>(${keyOptionNumber}) ${this.args[0]}">>${this.payload[0].contents}<</${widget}>>`
      )
      .attr("id", keyToId[keyOptionNumber] + "Act")
      .appendTo(this.output);
    keyOptionNumber = (keyOptionNumber + 1) % 10;
  },
});
//Adds a text similar imitating a keyOption that cannot be selected. A non working key number is assigned automatically.
//Parameters:
//1. Text to show
//2. (optional) emoji or emojis that will appear at the start if emojis are enabled in the settings.
//Example: <<keyDisabled 'My text' 🎃>>
Macro.add("keyDisabled", {
  handler: function () {
    let $wrapper = $(document.createElement("span"));
    let emoji =
      this.args.length > 1 ? this.args[1] : "'    '";
    $wrapper
      .wiki(`<<emoji ${emoji}>>(${keyOptionNumber}) ${this.args[0]}`)
      .appendTo(this.output);
    keyOptionNumber = (keyOptionNumber + 1) % 10;
  },
});
$(document).on("keyup", (evt) => {
  if (
    evt.key != "Enter" &&
    evt.key != "Escape" &&
    evt.key != " " &&
    SugarCube.Dialog.isOpen()
  )
    //To avoid executing options while writing into a dialog, only enter, esc and space are triggered when a dialog is opened.
    return;
  let className = keyToId[evt.key] || evt.key.toLowerCase();
  if (className.length == 1) className = className + "Key";
  $(`#${className}Act a`).trigger("click");
  $(`#${className}Act button`).trigger("click");
});