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: Keagro/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:
files
orig
game-raw.js
game-pretty.js
strive-raw.html
/strive*[0-9].[0-9]*.html
Release

repo_name: spdevel/sfp

path: .gitignore

license: unknown


code:
FILES = strive.html style.css
FILES += README.md engine.js structure.js race.js job.js girl.js enemy.js alchemy.js spells.js mutation.js data.js map.js game.js legacy.js pako.min.js save_load.js unique.js
FILES += README.md engine.js structure.js race.js job.js girl.js enemy.js alchemy.js spells.js mutation.js data.js map.js game.js legacy.js pako.min.js unique.js

BACKGROUNDS = desert.jpg

release:
	@rm -rf Release Release-full
	@mkdir -p Release/files/buttons Release/files/backgrounds Release-full
	cp $(FILES) Release
	for i in $(BACKGROUNDS); do cp files/backgrounds/$$i Release/files/backgrounds; done
	cp files/buttons/*.png Release/files/buttons/
	cp $(FILES) Release-full
	ln -s ../files Release-full
	cd Release && zip -r ../Release.zip .
	cd ..

path: Makefile


code:
Strive for Power - web edition

This is a fork of the original game by Maverik.

He's rewriting it as a standalone game [here](http://strivefopower.blogspot.com/), while I'm continuing the development of the web-based version.

Running:

* git clone https://gitgud.io/spdevel/sfp-web.git
* Grab the original from [Mega](https://mega.nz/#!pVcEVAZD!Itx3HmCmvb_g9cOBOQd_T9N2za11QM_YeBwjXcr-0A0)
* copy the files/ folder from the original to this directory.
* browse to strive.html

The files/ are optional, they provide the images.


code:
"use strict";
    var Item = function(type, name, desc) {
            this.type = type;
            this.name = name;
            this.description = desc;
            this.ingredients = [];
            this.select = undefined;
            Data.addItem(this);
    }

    Item.prototype.apply = function(girl) {
            ToText("Nothing happens to " + girl.name);
    }

    Item.prototype.AddIg = function(rname, count) {
        var name = rname.replace(/ /g, '_').toLowerCase();
        this.ingredients.push({ id: name, count: count});
    }

    var pot;
    pot = new Item("potion", "Hair Dye", 'Allows you to permanently change hair color when applied. For external use only.');
    pot.select = function(girl) {
        ToText("Choose new hair color for " + girl.name + "

");
        var value = girl.body.hair.color;
        if (!value && value != 0)
            value = '';
        var focus = '';
        if (!isTouchDevice)
            focus = ' autofocus';
        var ret = getAsmSys_titlePrev();
        switch (ret) {
            case 'residentitem':
            case 'prisoneritem':
                break;
            default:
                ret = 'main';
        }

        ToText("
<form action='' onClick='if(Trim(this.childNodes[0].value)!=""){Game.applyPotion2(Trim(this.childNodes[0].value));} else {ToText("Hair color must not be empty.

", true);DisplayLocation("" + getAsmSys_titleCur() + "");FlipPage();}event.stopPropagation();'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
    }
    pot.apply = function(girl) {
        if (Game.tmp5 === undefined) Game.tmp5 = "red"; //for testing
        girl.body.hair.color = Game.tmp5;
        ToText("You dye " + girl.name + "'s hair " + Game.tmp5 + ".

");
    }

    pot = new Item("potion",  'Hair Growth Elixir', 'Makes hair grow instantly! A tiny disclaimer says this potion is not a FairyCo. product.');
    pot.AddIg("Nature Essence", 2);
    pot.AddIg("Beastial Essence", 2);
    pot.apply = function(girl) {
        ToText("

As you pour the elixir onto her head, " + girl.name + '' + "’s hair begins to rapidly grow in length. ");
        if (girl.attr.willpower != -1) {
            ToText(" She seems surprised at the sudden change to her body, ");
            if (girl.loyalty >= 35) {
                ToText(" but not upset. When her hair has finished growing, she looks into your eyes and says, "I hope my new look pleases you, master." ");
            } else {
                ToText(" but keeps silent about her feelings. ");
            }
        } else if (girl.attr.willpower == 0) {
            ToText(" She barely reacts to what’s happening and just stares silently as it plays out. ");
        }
        if (girl.body.hair.length < 4) girl.body.hair.length++;
        ToText("

");
        girl.body.toxicity += 10;
        ToText("

");
    }

    pot = new Item("potion",  'Aphrodisiac', 'Increases the drinker’s lust');
    pot.AddIg("Beastial Essence", 2);
    pot.apply = function(girl) {
        ToText("

You make " + girl.name + " swallow a bit of aphrodisiac. She looks at you with a dreamy expression, her eyes tearing up, and a noticeable blush spreading across her body.

");
        girl.lust = Math.min(100, girl.lust + 20);
    }

    pot = new Item("potion",  'Elixir of Maturity', 'Causes a rapid acceleration in user’s physical growth, results may vary');
    pot.AddIg("Majorus Concoction", 1);
    pot.AddIg("Magic Essence", 2);
    pot.AddIg("Nature Essence", 1);
    pot.apply = function(girl) {
        ToText("

You hand an Elixir of Maturity to " + girl.name + '' + ", and tell her to drink it. ");
        if (girl.loyalty >= 60) {
            ToText(" You notice a hint of doubt in her eyes, but she doesn’t hesitate in taking the potion. ");
        } else {
            ToText(" She stares at the potion for a good while, clearly doubting its contents, but carefully takes it so that she doesn’t anger you. ");
        }
        if (girl.body.age < 3) {
            ToText(" After a few moments, her body begins to change. ");
            if (Math.tossCoin() && girl.body.chest.size < 5) {
                ToText(" Her breasts grow bigger." + '' + " ");
                girl.body.chest.size++;
            }
            if (Math.tossCoin() && girl.body.ass.size < 5) {
                ToText(" Her butt grows bigger." + '' + " ");
                girl.body.ass.size++;
            }
            if (Math.tossCoin() && girl.body.hair.length < 4) {
                ToText(" Her hair grows longer. ");
                girl.body.hair.length++;
                ToText(" " + '' + " ");
            }
            ToText(" She looks in bewilderment at her new, more mature looking self in a nearby mirror.

");
            girl.body.age += 1;
            girl.body.face.beauty = girl.body.face.beauty - Math.tossDice(5, 15);
        } else {
            ToText(" The potion has no effect. ");
        };
        girl.body.toxicity += 40;
        ToText("

");
    };

    pot = new Item("potion",  'Elixir of Youth', 'Causes a regression of users′s physical growth, results may vary');
    pot.AddIg("Minorus Concoction", 1);
    pot.AddIg("Magic Essence", 2);
    pot.AddIg("Fluid Substance", 1);

    pot.apply = function(girl) {
        ToText("

You hand an Elixir of Youth over to " + girl.name + '' + " and tell her to drink it. ");
        if (girl.loyalty >= 60) {
            ToText(" She looks doubtful, but takes the potion without hesitation out of respect for you. ");
        } else {
            ToText(" She clearly doesn’t trust what you’re giving her, but quickly takes the potion anyway to avoid angering you. ");
        }
        if (girl.body.age > 0)  {
            ToText(" After a few moments, her body begins to change. ");
            if (Math.tossCoin() && girl.body.chest.size > 2) {
                ToText(" Her breasts shrink in size." + '' + " ");
                girl.body.chest.size--;
            }
            if (Math.tossCoin() && girl.body.ass.size > 1) {
                ToText(" Her butt shrinks in size." + '' + " ");
                girl.body.ass.size = girl.body.ass.size - 1;
            }
            ToText(" She looks in bewilderment at her new, younger looking self in a nearby mirror.

");
            girl.body.age -= 1;
            girl.body.face.beauty = girl.body.face.beauty + Math.tossDice(5, 15);
        } else {
            ToText(" The potion has no effect. ");
        };
        girl.body.toxicity += 40;
        ToText("

");
    };

    pot = new Item("potion",  'Clear Mind Potion', 'Cures sexual perversions');
    pot.AddIg("Nature Essence", 2);
    pot.AddIg("Fluid Substance", 2);
    pot.apply = function(girl) {
        ToText("

As " + girl.name + '' + " drinks the Clear Mind Potion, her sexual compulsions and preferences slowly fade, and she is once again a blank slate for you to sculpt to your liking.

");
        girl.trait = 0;
        girl.body.toxicity += 15;
        ToText("

");
    };

    pot = new Item("potion",  'Stimulant Potion', 'Increases the user’s sensitivity to various stimuli. ');
    pot.AddIg("Nature Essence", 2);
    pot.AddIg("Aphrodisiac", 1);
    pot.apply = function(girl) {
        ToText("

You slowly pour the stimulant onto " + girl.name + '' + ", spreading it evenly across her body, an intense blush following the spread of the viscous liquid. The more complete the coverage, the more she fidgets, and by the end she’s stifling moans because of the increased sensitivity.

");
        girl.body.toxicity += 20;
        girl.addEffect("sensitized");
        ToText("

");
    };

    pot = new Item("potion",  'Deterrence Potion', 'Dulls the user’s sensitivity.');
    pot.AddIg("Nature Essence", 2);
    pot.AddIg("Tainted Essence", 1);
    pot.apply = function(girl) {
        ToText("

You slowly pour the Deterrence Potion over " + girl.name + '' + ", evenly spreading it across her body. You notice her tense and stiffen, eventually relaxing as she becomes less sensitive.

");
        girl.body.toxicity += 20;
        girl.addEffect("desensitized");
    };

    pot = new Item("potion",  'Nursing potion', 'Special mixture causing perpetual lactation');
    pot.AddIg("Beastial Essence", 3);
    pot.AddIg("Nature Essence", 1);
    pot.apply = function(girl) {
        if (girl.body.lactation == 0) {
            girl.body.lactation = 1;
            ToText("

You rub the Nursing Potion over " + girl.name + "'s chest.   After a few moments, a little milk starts to leak out of her nipples.

");
        } else {
            ToText("

You rub the Nursing Potion over " + girl.name + "'s chest, but nothing happens.

");
        }
    };

    pot = new Item("potion",  'Majorus Concoction', 'Apply to various parts of someone’s anatomy for rapid and fantastic results! For external use only.');
    pot.AddIg("Nature Essence", 2);
    pot.AddIg("Magic Essence", 1);

    /* majorus */
    pot.select = function(girl) {
        ToText("Choose where you wish to apply it.

");
        var t = girl.body.chest.size;
        if (t != 0) {
            if (t < 5)
                ToText("Breasts

");
            else
                ToText("

Breasts

");
        }

        if (girl.body.ass.size != 0) {
            if (girl.body.ass.size < 5)
                ToText("Ass

");
            else
                ToText("

Ass

");
        }

        if (girl.body.cock.size > 0) {
            if (girl.body.cock.size < 3)
                ToText("

Penis

");
            else
                ToText("

Penis

");
        }

        if (girl.body.balls.size > 0) {
            if (girl.body.balls.size < 3)
                ToText("

Testicles

");
            else
                ToText("

Testicles

");
        }
    }

    pot.apply = function(girl) { // Majorus
        if (Game.tmp5 === undefined) Game.tmp5 = Math.tossDice(1, 4); //for testing
        if (Game.tmp5 == 1) {
            var tmp = girl.body.chest.size;
            ToText("
You apply a " + this.name + " to " + girl.name + "'s breasts. ");
            if (tmp < 5) {
                girl.body.chest.size++;
                ToText("After some time, they increase in size. ");
                ToText("They are now " + girl.body.chest.size.shortDesc3 + ".");
            } else {
                ToText(" Nothing happens, it seems that her tits are too big for the potion to take effect.");
            }
        } else if (Game.tmp5 == 2) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s ass. ");
            if (girl.body.ass.size < 5 ) {
                ToText(" After some time, it increases in size. ");
                girl.body.ass.size++;
                ToText("It is now " + girl.body.ass.size.shortDesc + ".

");
            } else {
                ToText(" Nothing happens, it seems that her butt is too big for the potion to take effect.");
            }
        } else if (Game.tmp5 == 3) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s cock. ");
            if (girl.body.cock.size < 3) {
                ToText(" After some time, it increases in size. ");
                girl.body.cock.size++;
                ToText("It is now a " + girl.body.cock.size.shortDesc + " "
                        + girl.body.cock.type + " cock.");
            } else {
                ToText(" Nothing happens, it seems that her penis is too big for the potion to take effect.");

            }
        } else if (Game.tmp5 == 4) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s balls. ");
            if (girl.body.balls.size < 3) {
                ToText(" After some time, they increase in size. ");
                girl.body.balls.size++;
                ToText("She now has " + girl.body.balls.size.shortDesc + " balls.

");
            } else {
                ToText(" Nothing happens, it seems that her balls are too big for the potion to take effect.");

            }
        } else {
            ToText("Bad location " + Game.tmp5);
        }
    };

    pot = new Item("potion",  'Minorus Concoction', 'Application of this potion will reduce cumbersome body parts to more manageable sizes. For External use only.');
    pot.AddIg("Magic Essence", 1);
    pot.AddIg("Fluid Substance", 2);

    /* minorus */
    pot.select = function(girl) {
        ToText("Choose where you wish to apply it.

");
        var t = girl.body.chest.size;
        if (t != 0) {
            if (t > 1)
                ToText("Breasts

");
            else
                ToText("

Breasts

");
        }

        if (girl.body.ass.size != 0) {
            if (girl.body.ass.size > 1)
                ToText("Ass

");
            else
                ToText("

Ass

");
        }

        if (girl.body.cock.size > 0) {
            if (girl.body.cock.size > 1)
                ToText("

Penis

");
            else
                ToText("

Penis

");
        }

        if (girl.body.balls.size > 0) {
            if (girl.body.balls.size > 1)
                ToText("

Testicles

");
            else
                ToText("

Testicles

");
        }
    }

    pot.apply = function(girl) { // minorus
        if (Game.tmp5 === undefined) Game.tmp5 = Math.tossDice(1, 4); //for testing
        if (Game.tmp5 == 1) {
            var tmp = girl.body.chest.size;
            ToText("
You apply a " + this.name + " to " + girl.name + "'s breasts. ");
            if (tmp > 1) {
                girl.body.chest.size--;
                ToText("After some time, they decrease in size. ");
                ToText("They are now " + girl.body.chest.size.shortDesc3 + ".");
            } else {
                ToText(" Nothing happens, it seems that her tits are too small for the potion to take effect.");
            }
        } else if (Game.tmp5 == 2) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s ass. ");
            if (girl.body.ass.size > 1 ) {
                ToText(" After some time, it decreases in size. ");
                girl.body.ass.size--;
                ToText("It is now " + girl.body.ass.size.shortDesc + ".

");
            } else {
                ToText(" Nothing happens, it seems that her butt is too small for the potion to take effect.");
            }
        } else if (Game.tmp5 == 3) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s cock. ");
            if (girl.body.cock.size > 0) {
                ToText(" After some time, it decreases in size. ");
                girl.body.cock.size--;
                ToText("It is now a " + girl.body.cock.size.shortDesc + " "
                        + girl.body.cock.type + " cock.");
            } else {
                ToText(" Nothing happens, it seems that her penis is too small for the potion to take effect.");

            }
        } else if (Game.tmp5 == 4) {
            ToText("You apply a " + this.name + " to " + girl.name + "'s balls. ");
            if (girl.body.balls.size > 0) {
                ToText(" After some time, they decrease in size. ");
                girl.body.balls.size--;
                ToText("She now has " + girl.body.balls.size.shortDesc + " balls.

");
            } else {
                ToText(" Nothing happens, it seems that her balls are too small for the potion to take effect.");

            }
        } else {
            ToText("Bad location " + Game.tmp5);
        }
    };

    pot = new Item("potion",  'Amnesia Potion', 'Erases memories of the past');
    pot.AddIg("Tainted Essence", 2);
    pot.AddIg("Clear Mind Potion", 1);
    pot.apply = function(girl) {
        ToText("

After chugging down the Amnesia Potion, " + girl.name + '' + " looks lightheaded and confused. "W-what was that? I feel like I’ve forgotten something..." ");
        girl.backstory = Data.backstory1[1];
        ToText(" She’s lost, unable to recall the memories of her recent time in confinement. ");
        if (girl.loyalty < 50) {
            ToText(" She grows closer to you, having no one else she can rely on. ");
            girl.loyalty += 15;
        }
        ToText("

");
        girl.body.toxicity += 40;
        ToText("

");
    };

    pot = new Item("potion",  'Miscarriage Potion', 'The temporal solution to stupidity.');
    pot.AddIg("Tainted Essence", 3);
    pot.apply = function(girl) {
        if (girl.body.pregnancyID.length < 1)
            return;

        for (var i = 0; i < Game.baby.length; i++) {
            if (Game.baby[i].id == girl.body.pregnancyID[0]) {
                Game.tmp5 = i;
                DisplayLocation('deathchild');
                break;
            }
        }
        girl.body.pregnancy = 0;
        girl.body.pregnancyID = [];
        ToText("

After forcing " + girl.name + '' + " to drink Miscarriage Potion on, you let her go. There seems to be no signs of pregnancy from her anymore.

");
    };

    pot = new Item("potion",  'Oblivion Potion', 'The drinker of this potion experiences a form of targeted amnesia, retaining their personality but losing learned skills.');
    pot.AddIg("Amnesia Potion", 1);
    pot.AddIg("Tainted Essence", 1);
    pot.apply = function(girl) {
        ToText("

Drinking Oblivion potion makes " + girl.name + '' + " to forget all her skills.

");
        girl.skill.combat = 0;
        girl.skill.bodycontrol = 0;
        girl.skill.survival = 0;
        girl.skill.management = 0;
        girl.skill.service = 0;
        girl.skill.allure = 0;
        girl.skill.sex = 0;
        girl.skill.magicarts = 0;
        ToText("  ");
        girl.skillpoints = Math.min(girl.level,15);
        ToText("

");
        girl.body.toxicity += 50;
        ToText("

");
        if(girl.hasEffect("entranced")) {
            girl.removeEffect("entranced");
            ToText("It seems the potion had some unintended side effects.

");
            switch(Math.tossCase(5)) {
            case 0:
                girl.addMentalTrait("Frail");
                break;
            case 1:
                girl.addMentalTrait("Pliable");
                break;
            case 2:
                girl.addMentalTrait("Coward");
                break;
            default:
                girl.trait2 = 0;
            }
        } else if (Game.spells_known.entrancement) ToText("You wonder if the potion effects can be enhanced in some way.

");
    };

    pot = new Item("potion",  'Beauty Mixture', 'Clears the complexion and smoothes unsightly contours.');
    pot.AddIg("Fluid Substance", 1);
    pot.AddIg('Nature Essence', 2);
    pot.apply = function(girl) {
        ToText("

You order " + girl.name + '' + " to apply Beauty Mixture over her face, which will make skin smoother and remove flaws.

");
        girl.body.face.beauty = girl.body.face.beauty + 10;
        ToText("Her face is now " + girl.body.face.beauty.shortDesc + ".

");
        girl.body.toxicity += 35;
        ToText("

");
    };

    pot = new Item("potion", 'Health Potion', 'This is your ordinary, boring health restoration potion');
    pot.AddIg('Nature Essence', 1);
    pot.AddIg('Magic Essence', 1);
    pot.apply = function(girl) {
        ToText("The test alchemy potion worked, " + girl.name + " looks healthier.
");
        girl.health = Math.min(100, girl.health + 25);
    }

    pot = new Item("potion", 'Fertility Potion', "Increases a girl's chance to get pregnant.");
    pot.AddIg('Nature Essence', 1);
    pot.AddIg('Stimulant Potion', 1);
    pot.apply = function(girl) {
        ToText("After drinking the fertility potion " + girl.name + " looks at you with a hunger in her eyes.");
        girl.addEffect("fertility");
    }

    pot = new Item("potion", 'Greater Fertility Potion', "Pregnancy is virtually guaranteed.");
    pot.AddIg('Magic Essence', 1);
    pot.AddIg('Fertility Potion', 1);
    pot.apply = function(girl) {
        ToText("After drinking the fertility potion " + girl.name + " looks at you with an insatiable hunger in her eyes.");
        girl.addEffect("greater fertility");
    }

    new Item("essence", "Fluid Substance", "Acquired by fighting aqueous enemies. Produced by: Slime, Nereid, Scylla.");
    new Item("essence", "Tainted Essence", "Acquired from foul creatures. Produced by: Demon, Arachna, Lamia.");
    new Item("essence", "Magic Essence", "Aquired from magically rich environment. Produced by: Fairy, Drow, Dragonkin.");
    new Item("essence", "Nature Essence", "Aquired from sentient plant life.  Produced by: Dryad.");
    new Item("essence", "Beastial Essence", "Aquired from animals. Produced by: Beastkin/Halfkin, Harpy, Centaur.");

    pot = undefined;

Test.tryItemsOn = function(girl) {
    return function(name, callback, level) {
        var sub = {};

        forEach(Data.item, function(k, v) {
            var func = function () {
                if (v.apply === undefined)
                    return true;

                var old = ToText;
                ToText = function() {};
                try {
                    for (var i = 0; i < 100; i++) {
                        v.apply(girl);
                    }
                } catch (e) {
                    ToText = old;
                    throw e;
                }
                ToText = old;
                return true;
            };
            sub["item"+k] = func;
        });

        return Test.runTestTree(sub, name, callback, level);
    };
}


code:
"use strict";

var BodyType = Properties({
    "type.race": Any,
    race: Data.getRace("human"),

    "type.gender": Enum(["female", "futa", "male"]),
    gender: "female",

    "type.age": ClampedTiers(0, [1, 2, 3], 3, {
        marketValue: [0.75, 1, 0.75, 0.5],
        shortDesc: [ "child", "teenager", "adult", "elder" ],
        longDesc: [
            "She looks like a child, that has barely hit puberty. ",
            "She's a young looking teen. ",
            "She's a full grown adult {{female}}. ",
            "She's an elderly {{female}}. ",
        ],
        raceDesc: [
            "She's a barely pubescent {{race}} child.",
            "She's a young looking {{race}} teen. ",
            "She's a full grown {{race}} adult {{female}}.",
            "She's an elderly {{race}} {{female}}.",
        ],
    }),
    age: 0,

    "type.shape": Properties({
        "type.type": Enum(["human", "beastial", "petite", "jelly", "avian", "finned", "tentacles", "centaur", "spider", "lamia"], {
            longDesc: [
                "Her body is quite normal.",
                "Her body resembles a human's, except for some bestial features in her face and body structure.",
                "Her body is rather petite, about half the size of the average human.",
                "Her body is jelly-like and partly transparent.",
                "Her body has wings for arms and avian legs making everyday tasks difficult.",
                "Her body is scaly and sleek, possessing fins and webbed digits.",
                "The lower portion of her body consists of a number of tentacular appendages, similar to those of an octopus.",
                "While her upper body is human, her lower body is rather equine in nature.",
                "The lower portion of her body ends with a spider's legs and abdomen.",
                "The lower portion of her body ends with a snake’s tail.",
            ],
        }),
        type: "human",
    }),

    "type.skin": Properties({
        "type.color": Enum(["pale", "fair", "olive", "tanned", "brown", "dark", "blue", "purple", "green" ], {
            longDesc: [
                " Her skin is a pale white. ",
                " Her skin is healthy and fair color. ",
                " Her skin is of an unusual olive tone. ",
                " Her skin is a tanned bronze color. ",
                " Her skin is a mixed brown color.",
                " Her skin is dark. ",
                " Her skin is dark blue. ",
                " Her skin is purple. ",
                " Her skin is green. ",
            ],
        }),
        color: "fair",

        "type.coverage": Enum([ "skin", "scales", "marble", "gray", "orange-white", "black-white", // 5
                                "black-gray", "orange-black", "white-black-spots", "fur-leaves", "leaves" ], {
            longDesc: [
                "",
                " Her skin is partly covered with scales. ",
                " marble fur. ",
                " greyish fur. ",
                " orange-white fur of common pattern. ",
                " black and white fur of tuxedo pattern. ",
                " black-grayish fur  ",
                " orange-black striped fur. ",
                " white, black-dotted pattern fur. ",
                " unnatural green fur in places intertwined with various plant matter. ",
                " Various plant matter naturally covers parts of her body. ",
            ],
        }),
        coverage: "skin",

        "proto.describe": function() {
            //FIXME: reimplement this
            //if (this.shape == 3)
            //    out += " Her skin is semi-transparent and jelly-like. ";
            var out = "";
            out += this.color.longDesc;

            if (this.coverage.value == "skin")
                return out;

            if (this.coverage.index >= 2 && this.coverage.index <= 9)
                out += " She's covered in ";

            out += this.coverage.longDesc;
            return out;
        },
    }),

    "type.horns": Properties({
        "type.type": Enum(["none", "tiny", "bull", "curved"], {
            longDesc: [
                "",
                " There is a pair of tiny, pointed horns on top of her head. ",
                " She has a pair of long, bull-like horns. ",
                " There are curved horns coiling around her head. ",
            ],
        }),
        type: "none",
    }),

    "type.hair": Properties({
        "type.color": Str,
        color: "FIXME",

        "type.length": ClampedTiers(0, [1, 2, 3, 4], 4, {
            longDesc: [
                " Her {{hair_color}} hair is cut short. ",
                " Her {{hair_color}} hair falls down to just below her neck. ",
                " Her wavy {{hair_color}} hair is shoulder length. ",
                " Her gorgeous {{hair_color}} hair sways down to her waist. ",
                " Her {{hair_color}} hair cascades down, covering her hips. ",
            ],
        }),
        length: 0,

        "type.style": Enum(["untied", "ponytail", "twintails", "braid", "two braids", "bun"], {
            longDesc: [
                " untied and straight. ",
                " tied in a high ponytail. ",
                " managed in girly twintails. ",
                " combed into a single braid. ",
                " combed into two braids. ",
                " tied into a bun. ",
            ],
        }),
        style: "untied",

        "proto.describe": function() {
            var out = '';

            out += this.length.longDesc;
            out += " Right now, her hair is ";
            out += this.style.longDesc;
            out = simple_template(out, { hair_color: this.color });
            return out;
        },
    }),

    "type.face": Properties({
        //"type.type": Enum(["feminine", "masculine"]),
        //type: "feminine",

        "type.beauty": ClampedTiers(0, [16, 31, 51, 71, 86], 100, {
            marketValue: [0.5, 1, 1.5, 2, 2.5, 3],
            shortDesc: [
                "hideous",
                "plain looking",
                "average looking",
                "cute",
                "pretty",
                "beautiful",
            ],
            longDesc: [
                "undeniably ugly",
                "boring and plain",
                "fairly normal looking",
                "quite cute",
                "very pretty",
                "exceptionally beautiful",
            ],
        }),
        beauty: 50,

        //"type.makeup": Enum(["no", "light", "heavy"]),
        //makeup: "no",
    }),

    "type.eyes": Properties({
        "type.pairs": Nat,
        pairs: 1,

        "type.type": Enum(["simple", "compound", "cephalopod"], {
            longDesc: [
                "",
                " Her eyes are compound. ",
                " Her eyes are those of cephalopod. ",
            ],
        }, false),
        type: "simple",

        // percent
        "type.size": ClampedTiers(0, [50, 150, 300], 500, {
            longDesc: [
                " Her eyes are tiny dots on her face. ",
                "",
                " She has big eyes. ",
                " She has huge eyes. ",
            ],
        }),
        size: 100,

        // when type == simple
        "type.pupils": Enum(["round", "vertical", "horizontal"], {
            longDesc: [
                "",
                " She has vertical, animalistic pupils. ",
                " She has horizontal, animalistic pupils. ",
            ],
        }, false),
        pupils: "round",

        // tapetum lucidum
        "type.reflectors": Flag({
            longDesc: [
                "",
                " Her eyes shimmer, increasing her perception in poor light conditions. ",
            ],
        }),
        reflectors: false,

        // percent
        "type.acuity": ClampedTiers(0, [10, 80, 120, 200, 500], 1000, {
            longDesc: [
                " She's legally blind. ",
                " She's nearsighted. ",
                "",
                " She has better than average eyesight. ",
                " She has incredible eyesight. ",
                " She has eagle eyes. ",
            ],
        }),
        acuity: 100,

        "type.color": Str,
        color: "FIXME",
    }),

    "type.ears": Properties({
        "type.type": Enum(["human", "elf", "cat", "fox", "wolf", "bat", "fins", "bunny", "raccoon"], {
            shortDesc: [
                "human",
                "long, pointed",
                "cat",
                "fox",
                "wolf",
                "bat",
                "fin-like",
                "bunny",
                "raccoon",
            ],
            longDesc: [
                "",
                " She has unnaturally long, pointed ears. ",
                " She has a pair of fluffy, cat-like ears. ",
                " She has a long pair of perky, fox-like ears. ",
                " On her head, you spot a pair of short, wolf-like ears. ",
                " Her ears look like a pair of webbed, bat-like wings. ",
                " Her ears look like a pair of fins. ",
                " She has two long bunny ears on top of her head. ",
                " On her head, you spot a pair of short, raccoon-like ears. ",
            ],
        }),
        type: "human",

        // percent
        "type.size": ClampedTiers(0, [50, 150, 300], 500, {
            longDesc: [
                " She has tiny ear lobes. ",
                "",
                " She has big ears. ",
                " She has huge ears. ",
            ],
        }),
        size: 100,

        // percent
        "type.acuity": ClampedTiers(0, [20, 80, 120, 200, 500], 1000, {
            longDesc: [
                " She's deaf. ",
                " She's somewhat deaf. ",
                "",
                " She has pretty good hearing. ",
                " Her hearing is incredible. ",
                " She has owl's ears. ",
            ],
        }),
        acuity: 100,
    }),

    //lips: {
    //    size: 0,
    //},

    "type.tongue": Properties({
        "type.type": Enum(["normal", "split"], {
            longDesc: [
                "",
                "She has split tongue.",
            ],
        }, false),
        type: "normal",

        // percent
        "type.size": ClampedTiers(0, [100], 1000, {
            longDesc: [
                "",
                " Her tongue is unnaturally long and flexible. ",
            ],
        }),
        size: 100,
    }),

    //torso

    "type.chest": Properties({
        // FIXME: broke
        //"masculine",
        //"an average masculine chest",
        //"She has an average masculine chest.",

        "type.size": ClampedTiers(0, [1, 2, 3, 4, 5], 5, {
            shortDesc: [ "none", "flat", "small", "medium", "big", "gigantic" ],

            /* for mid-sentence, along the lines of "Her boobs are"  */
            shortDesc2: [
                "completely nonexistant",
                "nearly flat",
                "small and round",
                "firm, perky and inviting",
                "big and soft",
                "gigantic",
            ],
            /* pretty, with .. */
            shortDesc3: [
                "no breasts",
                "barely noticible breasts",
                "small and round boobs",
                "firm and perky tits",
                "big soft tits",
                "giant, mindblowing tits",
            ],
            longDesc: [
                "She has no boobs.",
                "Her chest is barely visible and nearly flat. ",
                "She has small round boobs.",
                "Her nice, perky breasts are firm and inviting.",
                "Her big tits are pleasantly soft, but still have a nice spring to them.",
                "Her giant tits are mindblowing.",
            ],
        }),
        size: 0,

        "type.nipples": Properties({
            "type.elastic": Flag({
                longDesc: [
                    "",
                    "  Her tits have been altered: nipples very elastic and stretchable. The inside are partly hollow, sensitive, and self-lubricating; suitable for a specific kind of sexual activity.",
                ],
            }),
            elastic: false,
        }),

        "type.pairs": Nat,
        pairs: 0, // additional

        "type.ripe": Flag(),
        ripe: false,

        "proto.describe": function(lookclosely) {
            if (this.pairs == 0)
                return '';

            var out = '';
            if (this.ripe) {
                out += " Below those, she possesses ";
                if (this.pairs == 1)
                    out += 'another row';
                else
                    out += this.pairs + ' more rows';
                out += " of slightly smaller ripe tits.";
            } else if (lookclosely) {
                out += " On a closer inspection you spot ";
                if (this.pairs == 1)
                    out += '1 pair';
                else
                    out += this.pairs + ' pairs';
                out += " of nonfunctional nipples located below her chest. ";
            }
            return out;
        },
    }),

    "type.wings": Properties({
        "type.type": Enum(["none", "white", "brown", "black", "demon", "dragon", "fairy"], {
            longDesc: [
                "",
                " Folded on her back, she has a pair of white, feathery wings. ",
                " On her back, she has folded, brown, feathery wings. ",
                " On her back folds a pair of feathered black wings. ",
                " Hidden on her back is a pair of bat-like, demonic wings. ",
                " On her back, she boldly displays a large set of dragon wings. ",
                " On her back rests translucent fairy wings. ",
            ],
        }),
        type: "none",
    }),

    //womb: Properties({
    "type.pregnancy": Nat,
    pregnancy: 0, // this.pregnancy

    "type.pregnancyID": ArrayOf(Nat),
    pregnancyID: [], // this.pregnancyID
    //}),

    // hips

    "type.tail": Properties({
        "type.type": Enum(["none", "cat", "wolf", "fox", "demon", "dragon", // 5
                           "scruffy", "raccoon", "bunny", "fish", "bird", // 10
                           "spider", "horse", "snake", "tentacles" ], {
            longDesc: [
                "",
                " Below her waist, you spot a slim, cat-like tail covered with fur. ",
                " Below her waist there's a short, fluffy, wolf’s tail. ",
                " She has a large, fluffy tail resembling that of a fox. ",
                " She has a long, thin, demonic tail ending in a pointed tip. ",
                " Trailing somewhat behind her is a scaled tail. ",
                " Behind her you notice a long tail covered in a thin layer of fur and ending in a scruffy brush. ",
                " She has a plump, fluffy raccoon tail. ",
                " She has a small ball of fluff behind her rear. ",
                " Her rear ends in long, sleek fish tail. ",
                " She has a feathery bird tail on her rear. ",
                " Her lower body consists of a spider’s abdomen. ",
                " Her lower body is equine. ",
                " Her lower body consists of a snake’s tail.  ",
                " Her lower body has multiple tentacles for legs. ",
            ],
            newbornDesc: [
                "",
                " It has a small cat-like tail inherited from its mother.",
                " It has a small wolf's tail inherited from its mother.",
                " It has a small fox tail inherited from its mother.",
                " It has a small demonic tail inherited from its mother.",
                " It has a small scaled tail inherited from its mother.",
                " It has a small scruffy inherited from its mother.",
                " It has a small racoon inherited from its mother.",
                " It has a small puffball tail inherited from its mother.",
                " Its legs merge together into a fish fail, like its mother.",
                " It has a small feathery birdtail inherited from its mother.",
                " It has a spider's abdomen, like its mother.",
                " It has an equine lower body, like its mother.",
                " Its lower body consists of a snake's tail, like its mother.",
                " Its lower body consists of multiple tentacles, like its mother.",
            ],
        }),
        type: "none",
    }),

    "type.cock": Properties({
        "type.type": Enum([ "human", "canine", "feline", "equine" ]),
        type: "human",

        "type.size": ClampedTiers(0, [1, 2, 3], 3, {
            shortDesc: [
                "",
                "tiny",
                "normal",
                "huge",
            ],
            longDescHuman: [
                "",
                " Between her legs dangles a tiny human dick, small enough that it could be called cute.",
                " She has an ordinary human penis between her legs, more than large enough to make most men proud.",
                " A huge human cock swings heavily from her groin, big enough to give even the most veteran whore pause.",
            ],
            longDescCanine: [
                "",
                " A slender, pointed dog dick hangs between her legs, so small that its knot is barely noticeable.",
                " She has a knobby, red, canine cock of respectable size between her legs, which wouldn’t look out of place on on a large dog.",
                " Growing from her crotch is a massive canine dick, red-skinned and sporting a massive knot near the base.",
            ],
            longDescFeline: [
                "",
                " A tiny feline penis rests between her legs, so small you can barely see the barbs.",
                " She has a barbed cat dick growing from her crotch, big enough to rival an average human.",
                " There is a frighteningly large feline cock hanging between her things, it’s sizable barbs making it somewhat intimidating.",
            ],
            longDescEquine: [
                "",
                " Between her legs hangs a smallish equine cock, which is still respectable compared to the average man.",
                " There is a sizable equine cock growing from her nethers, which, while small on a horse, is still thicker and heavier than the average human tool.",
                " A massive equine cock hangs heavily between her legs, it’s mottled texture not quite matching the rest of her skin. When not erect, it’s flared tip falls past her knees.",
            ],
        }),
        size: 0,

        "proto.describe": function() {
            switch(this.type.value) {
            case "human":
                return this.size.longDescHuman;
            case "canine":
                return this.size.longDescCanine;
            case "feline":
                return this.size.longDescFeline;
            case "equine":
                return this.size.longDescEquine;
            }
        },
    }),

    "type.pussy": Properties({
        "type.size": ClampedTiers(0, [1], 1),
        size: 1,

        "type.virgin": Flag({
            longDesc: [
                " A normal pussy rests between her legs.",
                " Her virgin pussy has not yet been penetrated. ",
            ],
        }),
        virgin: true,

        "proto.describe": function() {
            console.log("describe", this);
            if (this.size.value < 1)
                return "";
            else
                return this.virgin.longDesc;
        },
    }),

    "type.balls": Properties({
        "type.size": ClampedTiers(0, [1, 2, 3], 3, {
            // ... balls are
            shortDesc: [
                "",
                "tiny",
                "average-sized",
                "huge",
            ],
            longDesc: [
                "",
                " She has a pair of tiny balls.",
                " She has an average-sized ballsack.",
                " She has a huge pair of balls weighing down her scrotum.",
            ],
        }),
        size: 0,
        //fertile: true,
    }),

    "type.ass": Properties({
        "type.size": ClampedTiers(0, [1, 2, 3, 4, 5], 5, {
            shortDesc: [
                "",
                " skinny and flat",
                " a small, firm butt",
                " a nice, pert ass",
                " a pleasantly plump, heart-shaped ass",
                " a huge, attention-grabbing ass",
            ],
            longDesc: [
                "",
                " Her butt is skinny and flat.",
                " She has a small, firm butt. ",
                " She has a nice, pert ass you could bounce a coin off of. ",
                " She has a pleasantly plump, heart-shaped ass that jiggles enticingly with each step. ",
                " She has a huge, attention-grabbing ass. ",
            ],
        }),
        size: 0, //TODO: WTF?
    }),

    //anus: {
        //virgin: true,
        //looseness: 0,
    //},

    "type.toxicity": ClampedTiers(0, [], 100),
    toxicity: 0,

    "type.contraception": Flag(),
    contraception: true,

    "type.lactation": Flag(),
    lactation: false,

    "type.clothing": Nat,
    clothing: 1,

    "type.augmentations": Any,
    augmentations: 1111111111,

    "type.piercings": Any,
    piercings: 1111111111,

    "type.brand": Properties({
        "type.type": Enum(["no", "simple", "refined"], {
            longDesc: [
                " Currently she is not branded. ",
                " On her neck you can recognize symbols of brand you left on her. ",
                " On her neck you can spot your refined brand. ",
            ],
        }),
        type: "no",

        //position: "neck",
    }),
}, undefined, function() {
    this.race = Data.getRace(this.race); //TODO convert races type to Properties and remove this
});

var Body = BodyType.construct;

/* augmentations */
Body.prototype.augment = function(p, val) {
    this.augmentations = this.augmentations.toString().substr(0, p) + val.toString() + this.augmentations.toString().substr(p+1);
}

Body.prototype.giveAugmentation = function(name) {
    switch(name) {
        case 'fur':
            return this.augment(3, 2);
        case 'scales':
            return this.augment(3, 3);
        default:
            throw new Error("Unknown augmentation " + name);
    }
}

Body.prototype.removeAugmentation = function(name) {
    switch(name) {
        case 'fur':
        case 'scales':
        case 'skin':
            return this.augment(3, 0);
        default:
            throw new Error("Unknown augmentation " + name);
    }
}

Body.prototype.hasAugmentation = function(name) {
    switch(name) {
        case 'fur':
            return this.augmentations.toString().charAt(3) == 2;
        case 'scales':
            return this.augmentations.toString().charAt(3) == 3;
        case 'skin':
            return this.augmentations.toString().charAt(3) >= 2;
        default:
            throw new Error("Unknown augmentation " + name);
    }
}

Body.prototype.showAugmentations = function() {
    var out = '

';

    if (this.hasAugmentation('fur'))
        out += " Her fur is magically augmented and provides extra resistance to harmful effects.
 ";
    if (this.hasAugmentation('scales'))
        out += " Her scales are thicker than normal and provide extra protection on impacts.
 ";

    return out;
}

/* piercings */
Body.prototype.earPiercing = [
    '',
    '',
    " Her ears are decorated with pair of fancy earrings. ",
    " Her ears have a pair of small studs in them. ",
];

Body.prototype.nosePiercing = [
    '',
    '',
    " Her nose contains a large nose ring in it. ",
    " Her nose has a small stud in it.",
];

Body.prototype.lipPiercing = [
    '',
    '',
    " Her lip is pierced with a small ring. ",
    " Her lip has a  small stud in it.",
];

Body.prototype.tonguePiercing = [
    '',
    '',
    " Her tongue has a notorious stud which can be seen when she talks.",
];

Body.prototype.eyebrowPiercing = [
    '',
    '',
    " Her eyebrow is decorated with a small stud.",
];

Body.prototype.showBreastPiercing = function() {
    var p = this.piercings.toString().charAt(5);
    if (p < 2)
        return '';

    var out = " Her nipples are pierced and ";

    if (p == 2)
        out += "a pair of rings can be seen in them.";
    else if (p == 3)
        out += "decorated with a pair of small studs.";
    else if (p == 4)
        out += "a small degrading chain connects them.";
    return out;
};

Body.prototype.navelPiercing = [
    '',
    '',
    " Her navel is pierced and decorated with a small stud.",
];

Body.prototype.labiaPiercing = [
    '',
    '',
    " Her labia is pierced and decorated with a pair of rings. ",
    " Her labia is pierced and decorated with a small stud. ",
];

Body.prototype.clitPiercing = [
    '',
    '',
    " Her clit is pierced with a ring. ",
    " Her clit has a small stud in it. ",
];

Body.prototype.cockPiercing = [
    '',
    '',
    " Her cock has a considerable ring on the tip. ",
    " Her cock has a stud in it. ",
];

/* pregnancy */
Body.prototype.pregnancyArray = new TierArray({
    0: "",
    5: " The unborn fetus causes her belly to bulge slightly ",
    12: " Her advanced pregnancy is clearly evident by the moderate bulge in her belly ",
    20: " The unborn child forces her belly to protrude massively; she is going to give birth soon. ",
});

Body.prototype.hasFur = function() {
    return (this.skin.coverage.index >= 2 && this.skin.coverage.index <= 8);
}


code:
"use strict";

Data.version = '0.8.1+';

Data.changelog =
    "
Reworked Strive for Power (v1) Web Fork

"

    + "
Links
"
    + "
Source"
    + "
Dowload latest version"
    + "
Feedback thread"
    + "
Feedback thread (tor onion)"

    + "
Credits
"
    + "
Coding: SFPD, dinitro, MetalFan."
    + "
Writing: DarkwoodDragon."

    + "
History
"
    + "
Maverik develops original SfPv1"
    + "
Maverik abandons SfPv1 at v6.4.4 and goes to develop SfPv2 on Godot engine."
    + "
No new versions released for quite a while"
    + "
SfPv1 is picked up by SFPD to fix bugs and keep the game in a playable state while Maverik develops SfPv2"
    + "
dinitro and MetalFan join development"
    + "
DarkwoodDragon writes some new sex scenes"

    + "

" + Data.version + "

"

    + "
Changes
"
    + "
Arachna-specific resident scene added"
    + "
Developer internal changes to Girl.Body()"
    + "
New sex scenes by DarkwoodDragon"

    + "
Fixes
"
    + "
Dragonkin namegen, again"
    + "
Failed saves now have a useful message to the player."

    + "

0.8.1

"

    + "
Changes
"
    + "
Training revamp WIP"
    + "
Added beastkin/halfkin wolves to the forest"
    + "
Jobs reworked"
    + "
Beautify more resident selections."
    + "
Books show up based on library level, and unread books are marked."
    + "
Reworked sex."
    + "
Added detailed sex-scene for resident/companion cunnilingus."
    + "
Lamias now have their own sex-scene."
    + "
Hired a medical team to help with difficult childbirth."
    + "
Orgasm is now chance based"
    + "
So is pregnancy - and there's always a 1% chance if the participants are fertile."
    + "
You can build more than 25 rooms (plus more jails/personal quarters), but it gets expensive."

    + "
Fixes
"
    + "
capitalize names for a few races"
    + "
slaveguild restocks properly"
    + "
Crash bug when the chef had to buy more food."
    + "
Green is green when altering skincolor in the lab."
    + "
Race description popups"
    + "
New player creation fixes"
    + "
Portals from old saved games import properly."
    + "
Lamias generate with augmented tongues, not tits."

    + "

0.8.0

"

    + "
Changes
"
    + "
All girls get appropriate level for their skills"
    + "
Massive intro revamp by MetalFan"
    + "
Job selection clearly shows requirements"
    + "
endofday1 and all nightly jobs rewritten"
    + "
Moved variety of screens into popups and squashed redundancies"
    + "
Added action and status icons for prison screen"
    + "
Increased information density on various screens"
    + "
Streamlined work assignment, now with 80% less tedium"
    + "
Desert zone - full of bandits and hyenas"
    + "
Encounter fixes and balancing, moved portal from Highlands to Meadows"
    + "
Undercity Cellar has new requirements to enter, and a cooldown"
    + "
Alchemy greatly improved."
    + "
Reasons to use Oblivion potions and to kill beasts"
    + "
Majorus/Minorus/beauty potions have better feedback."
    + "
Javascript strict mode enabled.  This will break things!"
    + "
All remaining asm_var crap removed"

    + "
Fixes:
"
    + "
Baby augmentation inheritance was wrong"
    + "
fix peasant encounter again"
    + "
make it clearer you are out of gold when buying spells"
    + "
horny dolin without companion"
    + "
hair dye"

    + "

0.7.9

"

    + "
Changes
"
    + "
Merged in changes and fixes by dinitro"
    + "
Buffed dream"
    + "
Display spell/potion effects"
    + "
Added taurus and bunny/halfkin bunny to meadow"
    + "
Added halfk tanuki/cat/fox to crossroads"
    + "
Actually, not everyone is your sister"
    + "
Changing companion job much easier"

    + "
Fixes
"
    + "
Oblivion potion skillpoint calculation"
    + "
bunny generation was broken"
    + "
Companion no longer bumps confidence to 60 in jail"
    + "
Mind read isn't an option before you learn it"
    + "
Unique girl skillpoint consistancy"
    + "
Improved baby event"
    + "
Added fallback font for checkboxes"
    + "
Youth/maturity potions don't take you beyond the age limits"

    + "

0.7.0

"

    + "
Decompile and code cleanup by SFPD"

    + "

Original Strive for Power (v1) by Maverik

"

    + "
Links
"
    + "
Blog"
    + "
Patreon"

    + "
Credits
"
    + "
Game design and coding: Maverik."
    + "
Writers: Writer Anon, Anti-no, TheCrassDragon, Xero, Kalderza, Kiro."
    + "
Proofreading: Kalderza, TheCrassDragon."
    + "
Special thanks to Patreon Supporters, including:"
    + "Gapho, Tazanik, Anthony, Fred Roybal IV, ratking, Chris A, Shinjai, stephen stricklin, NeoMage, Roman, AF, kasper kaas søderberg, William G Klett, glocknar, Dragon_ANGL, andrew schumann, william potts, michael goode, NC, manshy, Alfredo J Sanchez, Tom Skelton, Nathaniel, Allan Castellon, Tim Chung, Lightness, matt, nightvolt, Michael Kluncker, Shuuu, sarcasm2k1, Gwaxer, Crazybue, supman500, concon361, Earl, Guy Gower, Kranua Xero, Alex McGregor, Colby, EtherLicker, Magetsu88, Massatsu, David, Lys, William Tomaszewski, Diablo8x, Joshua Smith, Bill Barnes, Nikohalus G Ingraham, DUSTIN MATTHEWS, Myzer676, Jeesup, Bill Barnes, Jeremie Jean-Philippe, Cecil Kane, N, Tiberiumkyle, austin nguyen, Tailsjaegar, Joseph E Reid, jack ryan, Bravanger, Michael Dennis Eagles, Matt, Ibrahim Dikko, Bene, Will RunOver, AL, WinterFrailty, Sean Dean, Gregory Melvin, Zack Mickowski."

    + "

6.6.4 Hotfix

"

    + "
Fixed elixirs being unbrewable"
    + "
Fixed sex/pregnancy/mana screens"
    + "
Sebastian provides you ingredients for main quest"

    + "

6.6.2

"
    + "
Possible potion reward from clearing zone decreased from 1-3 to 1-2."
    + "
Alchemy revamp"
    + "
Added Wisp encounter to ancient grove as a source of magic essense"
    + "
Girls in lab shouldn& #39;t act disobediently while there"
    + "
Mutation won't grow balls or penis if related options are turned off"
    + "
Fixed current stats not displaying companion stats correctly"
    + "
Fixed lab assistant not triggering elf's trait"
    + "
Fixed few other pregnancy issues"

    + "

6.6.1

"
    + "
Increased main window size to adjust for longer population screen"
    + "
Added window to check on personal room/your bed occupants"
    + "
Fixed some more threesome pregnancy related bugs"
    + "
 Fixed save related to personal rooms"
    + "
Fixed navel/nipples piercing description"
    + "
Fixed bug preventing you from getting any rare and special encounters at high survival skill"

    + "

6.6.0

"
    + "
Added option to switch population page to no image version and hide icons for specific residents there"
    + "
Added option to add custom description to slaves"
    + "
Added piercing options for slaves"
    + "
Fixed Dolin's quest resolution"
    + "
Fixed undefined races showing in some places"
    + "
Fixed brandings when sent baby to the growth"
    + "
Fixed slaves not actually getting pregnant on the prostitution assignements"

    + "

6.5.3

"
    + "
Fixed prisoners not being allowed as servants."

    + "

6.5.2

"
    + "
Threesome scenes will reflect if girls are relatives"
    + "
Added cheat function to unlock all spells"
    + "
Added servant's willpower stats to the training screen for easier referenceMonster girls can't be released from jail before branding and can't be unbranded"
    + "
Grove plant and Lis encounters moved to the grove screen, so they can be accessed with weak girls"
    + "
Bear s nerfed a bit"
    + "
Fixed slavers with girl encounter not being capturable and not allowing to cast mindread"
    + "
Fixed halfkin custom player images not showing"
    + "
Fixed some incorrect and blank screen e ncounters"
    + "
Added tooltip function"
    + "
Made it impossible to put lab assistant onto modifications"
    + "
Made population screen more user-friendly"

    + "

6.5.1

"
    + "
Added option to buy food in bulks and raised its price"
    + "
Added numerics on the training list"
    + "
Fixed bunch of typos and misspells"
    + "
Repeatable quest reward buffed"
    + "
Chef will pur chase food for mansion during work day if it gets bellow 200"
    + "
Fixed children getting no body shape and some other pregnancy related bugs"
    + "
Fixed save to string not saving known spells"

    + "

6.5.0

"
    + "
Added option to select prisoners for laboratory modification"
    + "
Fixed undercity progression"
    + "
Fixed slavers encounter"
    + "
Fixed afterfight rape (this time for sure)"
    + "
Fixed laborat ory confinements affecting other residents and progressing in various deeds"
    + "
Fixed slaver guild giving bugged quests and added few more races which they can ask for"
    + "
Fixed Dolin's quest alche my part"

    + "

6.4.1

"
    + "Fixed afterfight escapes"
    + "
Fixed afterfight rapes"
    + "
Fixed bug where you'd get stuck on screen after losing energy"

    + "

6.4.0

"
    + "Added new races, body shapes and features"
    + "
Dolin's quest line expanded."
    + "
Changed many old race traits"
    + "
Using portal costs energy. Advancing through zones will take energy o nce for numerous encounters."
    + "
Laboratory subjects can be selected now"
    + "
Removed hunting bows from the game"
    + "
Capturing prisoners now is a chance and based on your companion's body control and willpower of capturee"
    + "
Lab assistant will receive exp from residents stationed there"
    + "
Added 3 new spells and buffed some of the others"
    + "
Nerfed fucktoy job income"
    + "
Fixed couple of threesome p regnancy bugs"

    + "

6.3.3

"
    + "
Added protection for unique characters from renaming"
    + "
Added more descriptions and bit of depth to some normal intimacy scenes."
    + "
Fixed children looks bug"
    + "
Fixed toxicity save/lo ad string bug"
    + "
Fixed alchemy room equipment not saving"

    + "

6.3.2

"
    + "
Fixed loading from string breaking toxicity and looks"
    + "
Fixed Sebastian not fulfilling his deal on rare species"
    + "
Fixed toxicity applying to the wrong resident on lab modification"

    + "

6.3.1

"
    + "Fixed pregnancy not advancing if servant is away
Fixed Builder shop in shaliq not triggering as it should
"

    + "

6.3.0

"
    + "
Redid main quest line to fit more with plot direction"
    + "
Added more starting character options"
    + "
Added help sections on many screens explaining game mechanics"
    + "
Added some dynamic text to generic sexual encounters"
    + "
Added dick shapes to residents"
    + "
Added toxicity effect to the residents. Toxicity builds up on potion and modification usage and may have detrimental effects"
    + "
Added 'Mutate' spell as showcase of mechanic"
    + "
Added descriptions for spells and potions"
    + "
Added option to sell prisoners to the slaver's guild"
    + "
Added option to show/hide resident's description on their personal page"
    + "
Added an option to buy rare race high willpower girls at market's encounter"
    + "
Brothel-related assignments build up stress "
    + "
Highly stressed girls will complain by the end of the day"
    + "
Library upgrades are saved and increase amount of XP received by girls assigned there"
    + "
Doubled exp received from combat"
    + "
Pregnancy cause drop in combat characteristics and additional stress build up"
    + "
Coward trait also makes physical punishments more effective"
    + "
Mindread will display mental traits and mental characteristics for non-captured girls"
    + "
Reduced occurrence of virgins"
    + "
Face beauty is now a dynamic stat instead of static, and can be improved or damaged"
    + "
Fixed servants getting 'pliable' trait when they shouldn't have gotten any"
    + "
Fixed slaves bought from auction not being branded to you"
    + "
Fixed issue with racial traits reducing stats when it was too high"
    + "
Fixed advanced branding not transferring on string load"
    + "
Fixed Dream spell causing instant stress removal instead of lowering it and prevented certain abuse with it"
    + "
Fixed child growing option not costing any gold"
    + "
Fixed some bad logic on the 'end of the day' which ignored some pregnancies and failed with prisoners"

    + "

6.2.4

"
    + "
Fixed starting character creation"

    + "

6.2.3

"
    + "
Fixed crossroads encounter"
    + "
Added pregnancy chance on prostitution, fucktoy and sleeping in your bed and being horny event"
    + "
Added option to cut hair shorter"
    + "
Torture/punishment options build up stress instead of outright reducing willpower stats (very high stress will still cause breakage tho)"
    + "
Fixed repeatable quest not being completable"
    + "
Headgirl assignement gives exp"
    + "
Enabled random sexual traits generation"
    + "
Added unique mental traits to character generation"

    + "

6.2.2

"
    + "
Fixed prisoners escapes"
    + "
Added old starting girl constructor to sandbox mode"
    + "
Increased pregnancy duration to 30 days"
    + "
Fixed farm assignments"
    + "
Cali's stats nerfed"
    + "
Clothes have actual effect on servants."
    + "
Fixed broken health issue with Nurse assignment"
    + "
Trimmed second 'finish day' screen a bit"

    + "

6.2.1

"
    + "
Fixed undercity's last room"
    + "
Fixed sex option not checking for energy"
    + "
Repeatable quest allows to select servant, instead of brining it as companion"
    + "
Fixed some potions not being consumed on use."
    + "
Fixed Reservoirs disapperaing from mage's order if you upgraded your laboratory too much"
    + "
Fixed wrong child races"
    + "
Fixed integer problems with loading from string"
    + "
Added laboratory to things being saved"
    + "
Fixed apiary assigning wrong person"

    + "

6.2.0

"
    + "
Fixed prison bug"
    + "
Fixed leveling up bug"
    + "
Added 'Save to string' and 'load from string' options (use ctrl+c — ctrl+v)."

    + "

6.1.0

"
    + "
Added changelog to the game (you don't say)"
    + "
Added headgirl assignment (can be accessed at 8+ maximum residents)"
    + "
Added sex button in population screen and shortcut with cumming inside\outside"
    + "
Added races, futa gender and surname to player character"
    + "
Added new starting girl generation sequence"
    + "
Added Apiary for passive mana generation (requires lab and assistant)"
    + "
Added option to remove surnames on Rename button"
    + "
Added experience system (exp rewarded for any tasks and fights and allows to learn skills)"
    + "
Added couple more options for cheat menu"
    + "
Renamed Oblivion Potion to Amnesia Potion and added Oblivion Potion to reset resident's skills"

    + "
";


code:
"use strict";

Data.characters.player = function() {
    var g = new Girl();
    g.id = 1;
    g.name = 'John';
    g.lastname = 'Smith';
    g.body.age = 1;
    g.body.balls.size = 2;
    g.body.chest.size = 0;
    g.body.cock.size = 2;
    g.body.eyes.color = 'brown';
    g.body.gender = "male";
    g.body.hair.color = 'black';
    g.body.pussy.size = 0;
    g.father = -1; // special id
    g.mother = -2; // special id
    g.unique = 'player';
    return g;
}

Data.characters.urchin = function() {
    var g = new Girl();
    g.name = 'Emily';
    g.lastname = 'Hale';
    g.attr.charm = 30;
    g.attr.confidence = 35;
    g.attr.courage = 15;
    g.attr.wit = 50;
    g.backstory = Data.backstory1[2];
    g.body.age = 1;
    g.body.ass.size = 2;
    g.body.chest.size = 2;
    g.body.eyes.color = 'gray';
    g.body.face.beauty = 25;
    g.body.hair.color = 'brown';
    g.body.hair.length = 1;
    g.corruption = 0;
    g.father = 0;
    g.health = 85;
    g.level = 0;
    g.lost_skill_314 = 0;
    g.loyalty = 15;
    g.lust = 5;
    g.mother = 0;
    g.obedience = 100;
    g.potion_effect = 0;
    g.setRace("human");
    g.skill.allure = 0;
    g.skill.allure = 0;
    g.skill.bodycontrol = 0;
    g.skill.combat = 0;
    g.skill.magicarts = 0;
    g.skill.management = 0;
    g.skill.service = 0;
    g.skill.sex = 0;
    g.skill.survival = 0;
    g.skillmax = 5;
    g.skillpoints = 0;
    g.spell_effect = 0;
    g.stress = 0;
    g.trait = 0;
    g.trait2 = 0;
    g.trait_known = 0;
    g.unique = 1;
    return g;
}

Data.characters.cali = function() {
    var g = new Girl();
    g.name = 'Cali';
    g.lastname = 'Nuisal';
    g.attr.charm = 10;
    g.attr.confidence = 50;
    g.attr.courage = 65;
    g.attr.wit = 40;
    if (Game.settings.loli == 0) {
        g.body.age = 1;
    } else {
        g.body.age = 0;
    }
    g.backstory = '- Bandits stolen me from my home to sell as slave.';
    g.body.ass.size = 2;
    g.body.chest.size = 1;
    g.body.ears.type = "wolf";
    g.body.eyes.color = 'blue';
    g.body.face.beauty = 36;
    g.body.hair.color = 'gray';
    g.body.hair.length = 2;
    g.body.skin.color = "pale";
    g.body.tail.type = "wolf";
    g.corruption = 0;
    g.father = 0;
    g.health = 100;
    g.level = 1;
    g.lost_skill_314 = 0;
    g.loyalty = 0;
    g.lust = 0;
    g.mother = 0;
    g.obedience = 50;
    g.potion_effect = 0;
    g.setRace("halfkin wolf");
    g.skill.allure = 0; // 6.2.2 nerf
    g.skill.bodycontrol = 0;
    g.skill.combat = 0;
    g.skill.magicarts = 0;
    g.skill.management = 0;
    g.skill.service = 0;
    g.skill.sex = 0;
    g.skill.survival = 1;
    g.skillmax = 9;
    g.skillpoints = 0;
    g.spell_effect = 0;
    g.stress = 10;
    g.trait = 0;
    g.trait2 = 0;
    g.trait_known = 0;
    g.unique = 2;
    return g;
}

Data.characters.dolin = function() {
    var g = new Girl();
    g.name = 'Dolin';
    g.lastname = 'Seyfert';
    g.attr.charm = 45;
    g.attr.confidence = 25;
    g.attr.courage = 45;
    g.attr.wit = 75;
    g.backstory = Data.backstory1[2];
    g.body.age = 2;
    g.body.ass.size = 4;
    g.body.chest.size = 3;
    g.body.eyes.color = 'green';
    g.body.face.beauty = 50;
    g.body.hair.color = 'red';
    g.body.hair.length = 3;
    g.body.hair.style = "ponytail";
    g.body.pussy.virgin = false;
    g.body.shape.type = "petite";
    g.body.toxicity = 20;
    g.corruption = 15;
    g.father = 0;
    g.health = 100;
    g.level = 4;
    g.loyalty = 0;
    g.lust = 0;
    g.mother = 0;
    g.obedience = 100;
    g.potion_effect = 0;
    g.setRace("gnome");
    g.skill.allure = 0;
    g.skill.bodycontrol = 0;
    g.skill.combat = 0;
    g.skill.magicarts = 3;
    g.skill.management = 0;
    g.skill.service = 0;
    g.skill.sex = 0;
    g.skill.survival = 1;
    g.skillmax = 10;
    g.skillpoints = 0;
    g.spell_effect = 0;
    g.stress = 0;
    g.trait = 0;
    g.trait2 = 0;
    g.trait_known = 1;
    g.unique = 3;
    return g;
}

Test.tests.characters = {}
var t = Test.tests.characters;

t.tryAllCharacters = function(name, callback, level) {
    var sub = {};

    forEach(Data.characters, function(k, v) {
        var func = function() {
            var girl = v();
            return Test.tryMutationsOn(girl).apply(this, arguments) && Test.tryItemsOn(girl).apply(this, arguments);
        };
        sub[k] = func;
    });

    return Test.runTestTree(sub, name, callback, level);
}


code:
"use strict";

    /* begin populating data here */

    var Data_ = function() {
        this.characters = {};
        this.mutations = [];
        this.Spell = {}
        this.item = {}
        this._zone = {};
        this.Special = {};
        this.trigger = {};
        this.job_byname = {};

        /* Begin migrating static data here */
        this.obedience_imgname = [, 'obedbad', 'obedmed', 'obedgood'];
        this.health_imgname = [, 'healthlow', 'healthmed', 'healthhigh'];
        this.stress_imgname = [, 'stressgood', 'stressmed', 'stressbad'];
        this.sex_imgname = [, 'sexnone', 'sexlow', 'sexmed', 'sexhigh'];
        this.brand_imgname = [ 'nobrand', 'normalbrand', 'refinedbrand'];
        this.job_imgname = [ 'job0', 'job1'];
        this.inspect_imgname = [ 'inspect0', 'inspect1'];

        this.portal = {
            grove: 'Mystic Grove',
            //highlands: 'Highlands', // seashore is right next to highlands
            undercity: 'Undercity',
            meadows: 'Meadows', // distance consistency
            sea: 'Seashore',
            desert: 'Desert',
        };

        this.race = {}

        this.clothing_name = ['', 'normal clothes', 'sundress', 'maid uniform', 'kimono', 'pet suit', 'ninja suit', 'miko outfit', 'Chainmail Bikini', 'Bedlah'];
        this.skill_name = ['', 'Combat', 'Body Control', 'Survival', 'Management', 'Service', 'Allure', 'Sexual Proficiency', 'Magic Arts', '', ''];

        this.skillMap = {
            combat: {name: 'Combat', desc: 'Combat is a primal fighting skill representing both offensive and defensive capabilities of a person as well as knowledge of battle tools. Main use — figthing.',
                requirements: [ {}, {}, {}, {courage: 40}, {courage: 60, willpower: 4, bodycontrol: 2}, {willpower: 5, bodycontrol: 3, courage: 80} ]},
            bodycontrol: {name: 'Body Control', desc: 'Body Control represents agility and dexterity of a person. It plays major role in both combat and entertainment.',
                requirements: [ {}, {}, {}, {confidence: 40}, {confidence: 60}, {willpower:4, confidence: 80} ], exceptions: [ 'confidence', [7]]}, // exceptions - race 7 skips confidence req
            survival: { name: 'Survival', desc: 'Survival represents how good person can manage in the wild. Affects tracking, forage and hunting.',
                requirements: [ {}, {}, {courage: 20, wit: 20}, {courage: 40, wit: 40}, {courage: 60, wit: 60}, {courage: 60, wit: 60, willpower: 4} ],
                exceptions: [ 'all', [6, 103]]},
            management: { name: 'Management', desc: 'Management relates to the capability of holding administrative roles. Is a must for anyone involved in working with subordinates.',
                requirements: [ {}, {}, {}, {confidence: 40}, {confidence: 60, wit: 20, willpower: 4}, {confidence: 80, wit: 40, willpower: 5} ]},
            service: { name: 'Service', desc: 'Service represents householding skills and etiquette. Affects most house workers.',
                requirements: [ {}, {}, {wit: 20, charm: 20}, {wit: 40, charm: 40}, {wit: 60, charm: 60}, {wit: 60, charm: 60, willpower: 4} ],
                exceptions: ['all' [0]]},
            allure: { name: 'Allure', desc: 'Allure represents ability to attract other people by your apperance and actions.',
                requirements: [ {}, {}, {}, {charm: 40}, {charm:60, face: 51}, {charm:80, face: 71} ],
                exception: [ 'charm', [5, 102]]},
            sex: { name: 'Sexual Proficiency', desc: 'Sexual Proficiency represents how good person can satisfy partner in sex.',
                requirements: [ {}, {}, {courage:20, charm: 20}, {courage:40, charm: 40}, {bodycontrol: 2, courage:60, charm: 60}, {bodycontrol: 3, courage:60, charm: 60} ]},
            magicarts: { name: 'Magic arts', desc: 'Magic arts represents various knowledge about nature and functions of magic. It is highly required to those actively working in such direction.',
                requirements: [ {}, {}, {}, {wit: 40}, {wit: 60}, {wit: 80} ]},
        };

        this.skill_adjective = ['None', 'Novice', 'Apprentice', 'Journeyman', 'Expert', 'Master'];
        this.willpower_adjective = ['Broken', 'Pushover', 'Meek', 'Reserved', 'Willful', 'Headstrong', 'Heroine'];
        this.sex_trait = ['none', 'submissive', 'nymphomaniac', 'deviant', 'frigid', 'frivolous', 'masochistic', 'bisexual', 'monogamous'];
        this.mental_trait = ['none', 'Coward', 'Iron Will', 'Passive', 'Clingy', 'Prude', 'Loner', 'Frail', 'Pliable', 'Devoted', 'Slutty'];
        this.backstory1 = ['- I′ve been living with you, Master, since I can remember. You always been taking care of me and I′m really grateful for everything you′ve done. ', '- I don′t remember anything before I came here... I′m sorry, master. ', '- I was an orphan and never knew my parents, it was pretty horrible living with many other children and begging on streets... ', '- My parents were pretty poor and I had to help here and there as it was pretty rough. ', '- I′m from merchant family. We were not very rich but dad still used to spoil me. ', '- I′m a high-born. Of course you wouldn′t care about it now. Who would expect me to end up like one of servants we used to have... ', '- I′m from pious family. It was pretty strict at that time but my parents took care of me. '];
        this.backstory2 = ['- I was serving in the army. You may think its hard for a girl like me, but it felt nice to be useful. ', '- I used to work as nurse in hospital. ', '- I′ve used to work in field so I′m no stranger to hard work. ', '- I was a herbalist helping our local doctor. ', '- I′ve been making some living by hunting in local grounds. ', '- In truth I used to work as private detective. ', '- I was few months away from arranged marriage. ', '- I was working as a teacher in local school. ', '- I was an actress in small ensemble. '];
        this.combat_action = ['Attack', 'Defend'];
        this.my_action = ['Observe', 'Cast sedation', 'Cast Heal', 'Cast Daze', 'Cast Mind Blast', 'Cast Rejuvenation'];
        this.enemy_action = ['', 'Arousing Mist', 'Poisonous Bite', 'Infectious Bite', 'Corrupting Hands', 'Overwhelm', 'Alluring Touch', 'Entangle'];
    }

    Data_.prototype.AddMutation = function(v) {
            this.mutations.push(v);
    }

    Data_.prototype.RandomMutation = function() {
            return this.mutations.random();
    }

    Data_.prototype.addItem = function(it) {
            var name = unixName(it.name);
            this.item[name] = it;
    }

    Data_.prototype.addSpell = function(sp) {
            var name = unixName(sp.name);
            this.Spell[name] = sp;
    }

    Data_.prototype.AddZone = function(z) {
        var name = unixName(z.name);
        this._zone[name] = z;
    }

    Data_.prototype.Zone = function(z) {
        var name = unixName(z);
        if (name in this._zone)
            return this._zone[name];

        console.log("Failed to lookup zone " + z);
        throw new Error("Failed to lookup zone " + z);
    }

    // returns an array of Race() objects.
    Data_.prototype.listRaces = function() {
        var ret = [];
        var keys = Object.keys(this.race);
        for (var i in keys) {
            if (this.race.hasOwnProperty(keys[i]) && (Game.settings.furry == 1 || this.race[keys[i]].tags.indexOf("furry") == -1))
                ret.push(this.race[keys[i]]);
        }
        return ret;
    }

    Data_.prototype.addTrigger = function(name, fn) {
        if (!(name in Data.trigger))
            Data.trigger[name]=[];

        Data.trigger[name].push(fn);
    }

    Data_.prototype.getRace = function(race) {
        if (parseInt(race) == race)
            throw new Error("Numeric races are not supported");

        // allow copying Girl without looking up race again
        if (race instanceof Race)
            return race;

        var k = unixName(race);

        if (this.race.hasOwnProperty(k))
            return this.race[k];
        throw new Error("Unknown race " + race);
    }

    Data_.prototype.addJob = function(job) {
        var n = unixName(job.name);
        this.job_byname[n] = job;
    }

    // change the name to be in-line with the rest of the functions
    Data_.prototype.getJob = function(job) {
        if (job === undefined || Array.isArray(job)) {
            throw new Error("Job " + job + " not found.");
        }
        if (Number.isInteger(job))
            throw new Error("Numeric jobIDs no longer exist.");
        if (job instanceof Job)
            return job.name;

        var n = unixName(job);
        if (this.job_byname.hasOwnProperty(n))
            return this.job_byname[n]
        throw new Error("Job " + job + " not found.");
    }

    Data_.prototype.addRace = function(race) {
        var key = unixName(race.name);
        this.race[key] = race;
    }

    var Data = new Data_();


code:
# Getting started

## Customize your starting girl

  Easiest start is a combat build, as follows:

* Race*, looks, backstory: to taste.
* Hobby: Fighting
* Strength: Bravery
* Sexual traits: to taste.
* Mental trait: Iron will
* Buy some wit so you can max her potential easier, or leave at the default.

*cat races have a slightly higher chance to dodge, dragonkin can break holds easier, but it's really preference what you like the best.

Click around your mansion and get familiar with it's (sparse) offerings.  There's some backstory and info in the library, and you can inspect your servant under residents.  Assign your starting girl as a companion.

First day:  Head into town.  Visit all the locations there and don't be a complete shit and you'll get your second servant.  You have your first step on the main quest now!  You don't need to buy anything at first, especially on easy start.  Don't worry about taking the slaveguild quest, there's no penalty for not completing it so if you get lucky it's easy money.

Go back home, and play dress-up.  Inspect, Rules and regulations, Maid Uniform to encourage obedience.  If you have a second girl, give her a job to do.

## Your first foray into the world

Now it's time to go through the portal.  You only have one choice, so it's off to Shaliq.  Go into the wilds and attack the first girl you find.  You should be able to fight 3 before your companion is too beat up to continue.  If you catch one, you can choose to rape her for mana, sell her to the slave guild, or take her to your jail to try to break in.

Head home, have your companion rest, and finish the day.

# Jobs

### In house roles

These mostly gain XP based on the number of residents you have.

* Rest: Gain no XP, but heal and reduce stress faster.
* Library: gains XP at a fixed rate per day, but contributes nothing else.
* Chef: reduces the amount of food your residents consume.  Critical when your household gets full
* Nurse: provides extra healing to your residents
* Jailer: Tends to prisoners, increases their conversion rate and reduces stress.
* Head girl: Can either enforce obedience or encourage loyalty.  Nearly always want the latter, since obedience is a quick trip to the punishment room away.

### Outside jobs

* Forage/hunt: Bring in food.  Can get hurt doing it.
* Store/Entertainer/Mage Guild: Bring in money.  Some have requirements, and all have skills that increase the amount you gain.
* Prostitution: Mentally damaging to most girls, some of the best money though.