code:
"use strict";

    var Zone = function(name, image, description) {
        this.name = name;
        this.image = image;
        this.description = description;
        this.encounter = [];
        this.advencounter = [];
        this.exit = [];   // maybe-available exits at start of zone
        this.border = []; // endzone borders
        this.spooky = 0;
        this.size = 1;
    }

    Zone.prototype.getEncounter = function() {
        var be = []; // better encounter
        for (var i = 0; i < this.advencounter.length; i++)
            if (Data.encounter[this.advencounter[i]].check())
                be.push(this.advencounter[i]);

        var ne = []; // normal encounter
        for (var i = 0; i < this.encounter.length; i++)
            if (Data.encounter[this.encounter[i]].check())
                ne.push(this.encounter[i]);

        if (Game.Girls[Game.companion] !== undefined)
            var better_encounter = 25 + 5 * Game.Girls[Game.companion].skill.survival;
        else {
            console.log("Should not be able to get encounters without a companion!");
            var better_encounter = 0;
        }

        console.log("Better encounter chance: " + better_encounter);
        if (be.length > 0 && Math.tossCoin(Math.withChance(better_encounter))) {
            console.log(" got better");
            return be.random();
        } else {
            console.log(" got normal");
            return ne.random();
        }
    }

    Zone.prototype.addEncounter = function(adv, e, weight) {
        if (typeof(e) === 'object')
            throw new Error('Expect an encounter name');

        if (Data.encounter[e] === undefined)
            throw new Error("Encounter undefined: " + e);

        if (adv)
            do this.advencounter.push(e); while (weight--);
        else
            do this.encounter.push(e); while (weight--);
    }

    Data.addTrigger("travel", function() {
        if (Game.companion != -1 && Game.Girls[Game.companion].isRace("centaur"))
            return true;
        else if (Game.energy >= 1) {
            Game.energy--;
            return true;
        } else {
            console.log("Out of energy and not riding a centaur");
            return false;
        }
    });

    Data.addTrigger("portal", function() {
        if (Game.companion == -1 || !Game.Girls[Game.companion].isRace("centaur"))
            Game.energy--;
        return true;
    });

    Game_.prototype.mapTravel = function(dest) {
        this.zone_travelled = 0;
        this.mappos = dest;
        PrintLocation('map');
    }

    Game_.prototype.mapPortal = function(dest) {
        this.trigger("portal", dest);
        this.mapTravel(dest);
    }

    var Border = function(name, button) {
        if (name === undefined || button === undefined)
            throw "Border must be defined";

        this.name = name;
        this.button = button;
    }

    Border.prototype.display = function() {
        if (this.name.toLowerCase() in Data._zone) {
            if (Game.energy > 0)
                ToText("" + this.button + "
");
            else
                ToText("" + this.button + " - not enough energy
");
        } else
            throw new Error("Zone " + this.name + "Not found.");
    }

    Border.prototype.check = function() { return true; }

    var Exit = function(etype, name, arg) {
        this.etype = etype;
        this.name = name;
        this.arg = arg;
    }

    Exit.prototype.display = function() {
        if (this.etype == 'location')
            ToText("" + this.name + "

");
        else
            ToText("etype " + this.etype + " not finished for " + this.name + " / " + this.arg + "

");
    }

    var z = new Zone("Forest", "files/backgrounds/forest.jpg", "You stand deep within this ancient forest. Giant trees tower above you, reaching into the skies and casting deep shadows on the ground below. As the wind whispers past, you can hear the movement of small creature in the undergrowth and birds singing from their perches above.");
    z.spooky = 2;
    z.size = 4;
    z.exit.push(new Exit("location", "Return to the Shaliq Village", "shaliq"));
    z.border.push(new Border("Elvenforest", "Go North, to the elven parts of the forest"));
    z.border.push(new Border("Crossroads", "Go West, to the crossroads"));

    z.addEncounter(false, 'wolf');
    z.addEncounter(false, 'bandit');
    z.addEncounter(false, 'peasantgirl', 2);

    Data.encounter.forestgnome = new Encounter({
        etype: 'location',
        description: "You meet a lone person.
SPECIAL",
        dest: 'forestgnome',
        check: function() { return Game.mission.dolin <= 1; },
    });
    z.addEncounter(true, 'forestgnome');
    z.addEncounter(true, 'banditgirl'); // the rich one
    Data.AddZone(z); // forest

    z = new Zone('Elvenforest', 'files/backgrounds/forest.jpg', "

This portion of the forest is located dangerously close to eleven lands. They take poorly to intruders in their part of the woods so you should remain on your guard.

");
    z.spooky = 2;
    z.size = 4;
    z.border.push(new Border('grove', 'Go East to the eerie looking woods'));
    z.border.push(new Border('Forest', 'Go South to the forest nearby Shaliq'));
    z.addEncounter(false, 'bear');
    z.addEncounter(false, 'bandit');
    z.addEncounter(false, 'peasantgirl', 2);
    z.addEncounter(false, 'elfwarrior');

    z.addEncounter(true, 'fairy');
    z.addEncounter(true, 'elfmaiden', 2);
    Data.AddZone(z);

    z = new Zone('Crossroads', 'files/backgrounds/crossroads.jpg', 'This part of the forest seems to be well used by people as you can see a well-worn road below your feet.');
    z.border.push(new Border('forest', 'Go east to the Shaliq village outskirts'));
    z.border.push(new Border('meadows', 'Go west to the Meadows'));
    z.spooky = 3;
    z.size = 4;

    z.addEncounter(false, 'peasantgirl', 3);
    z.addEncounter(false, 'bear');

    z.addEncounter(true, 'banditswithgirl');
    z.addEncounter(true, 'tanuki');
    Data.AddZone(z);

    z = new Zone("Meadows", 'meadows.jpg', 'You are in the middle of grass sea. Nice breeze goes through your hair as you look around. In the far you can spot some living settlements.');
    z.spooky = 2;
    z.size = 5;
    z.border.push(new Border('Crossroads', 'Go east, to the crossroads'));
    z.border.push(new Border('Highlands', 'Go south, to the highlands'));

    z.addEncounter(false, 'bandit');
    z.addEncounter(false, 'peasantgirl', 5);

    z.addEncounter(true, 'bunny');
    z.addEncounter(true, 'banditswithgirl');
    z.addEncounter(true, 'centaur');
    z.addEncounter(true, 'tanuki');
    Data.AddZone(z);

    z = new Zone("Highlands", 'highlands.jpg', 'These elevated areas lead to moutains and cave systems. You can feel sun getting hotter over your head.');
    z.spooky = 2;
    z.size = 5;
    z.border.push(new Border('Meadows', 'Go north, to the meadows'));
    z.border.push(new Border('Sea', 'Go south, to the sea'));
    z.border.push(new Border('Desert', 'Go west, to the desert'));

    z.addEncounter(false, 'peasantgirl');
    z.addEncounter(false, 'cougar', 3);
    z.addEncounter(false, 'banditswithgirl');

    z.addEncounter(true, 'banditswithharpy');
    z.addEncounter(true, 'harpy');
    z.addEncounter(true, 'centaur');
    Data.AddZone(z);

    z = new Zone("Desert", 'desert.jpg', 'Rocky desert. You can feel sun getting hotter over your head.');
    z.spooky = 2;
    z.size = 5;
    z.border.push(new Border('Highlands', 'Go east, to the highlands'));

    z.addEncounter(false, 'bandit', 6);
    z.addEncounter(false, 'hyena', 2);

    z.addEncounter(true, 'banditswithgirl');
    z.addEncounter(true, 'harpy');
    z.addEncounter(true, 'lamia', 2);
    Data.AddZone(z);

    z = new Zone("Sea", 'sea.jpg', 'You are at the beach of a Big Sea. Air smells of salt and you can spot some sea caves formed by plateau and incoming waves.');
    z.spooky = 2;
    z.size = 5;
    z.border.push(new Border('Highlands', 'Go north, to the highlands'));

    z.addEncounter(false, 'nereid');
    z.addEncounter(false, 'ooze', 2);
    z.addEncounter(false, 'crab', 2);

    z.addEncounter(true, 'scylla');
    z.addEncounter(true, 'nereid');
    z.addEncounter(true, 'banditswithnereid', 3);
    Data.AddZone(z);

    z = new Zone('Grove', 'grove.jpg',  'This portion of the forest is deeply shadowed, and strange sounds drift in and out of hearing. Something about the atmosphere keeps the normal forest creatures silent, lending an eerie, mystic feeling to the grove you stand within.');
    z.spooky = 3;
    z.size = 4;

    z.border.push(new Border('Elvenforest', 'Go west to the Elven part of the forest'));
    z.border.push(new Border('Marsh', 'Go north to the Tainted Marsh'));

    z.exit.push({
        display: function() {
            if (Game.mission.plant > 0)
                ToText("

Go to the plant

");
            if (Game.mission.lis >= 2)
                ToText("

Move down the stream

");
            if (Game.mission.dolin == 6) {
                if (Game.energy >= 1)
                    ToText("

Search for Dolin

");
                else
                    ToText("
Search fo Dolin you don't have enough energy.");
            }
        }
    });
    z.addEncounter(false, 'fairy');
    z.addEncounter(false, 'wisp', 2);
    z.addEncounter(false, 'plant');
    z.addEncounter(false, 'elfwarrior');

    Data.encounter.mysteriousplant = new Encounter({
        etype: 'location',
        check: function() { return Game.mission.plant == 0; },
        description: 'You found a giant, mysterious plant.',
        dest: 'groveplant',
        button: 'Check it out',
    });
    z.addEncounter(true, 'mysteriousplant');
    z.addEncounter(true, 'fairy');
    z.addEncounter(true, 'dryad');
    Data.AddZone(z);

    z = new Zone('Marsh', 'marsh.jpg', 'Dank bog lies at the border of the forest and swamps beyond. Noxious smells and a sinister aura prevail throughout. The landscape itself is hostile, with pitch-black pools of water mixed among the solid ground and you doubt that the creatures that live here are any more pleasant than the land they live in.');
    z.spooky = 4;
    z.size = 6;
    z.exit.push({
        display: function() {
            if (Game.undercity_unlocked == 1)
                ToText("Go underground to the Undercity

");
        }
    });
    z.border.push(new Border('Grove', 'Go south, to the eerie looking woods'));

    Data.encounter.undercityenterance = {
        check: function() {
            if (Game.undercity_unlocked >= 1 || Game.Girls[Game.companion].skill.survival < 2)
                return false;
            return true;
        },
        display: function() {
            Game.undercity_unlocked = 1;
            ToText(simple_template('{{companion}} has discovered an enterance to the ancient underground ruins on the side of the bog.

', { companion: Game.Girls[Game.companion].name, }));
                ToText("Go underground to the Undercity

");
            ToText("Remember it and move on
");
        },
    };

    z.addEncounter(false, 'ooze', 2);
    z.addEncounter(false, 'plant', 2);
    z.addEncounter(false, 'undercityspider', 2);
    z.addEncounter(false, 'arachna');

    z.addEncounter(true, 'undercityenterance');
    z.addEncounter(true, 'demon');
    z.addEncounter(true, 'slime');
    z.addEncounter(true, 'elfwarrior', 2);
    Data.AddZone(z);

    z = new Zone('Undercity', 'undercity.jpg', 'You are in the middle of the ruins of an ancient city which is built in a vast cave system. Your senses tell you that this place may be very dangerous as of now, it's a home for many deformed creatures and hiding criminals.');
    z.spooky = 5;
    z.size = 4;

    z.border.push(new Border('templeruins', 'Move to the Demolished Temple'));
    z.addEncounter(false, 'undercityspider', 2);
    z.addEncounter(false, 'werebeast');
    z.addEncounter(false, 'cultist');

    z.addEncounter(true, 'drow');
    Data.AddZone(z);

    z = new Zone('Templeruins', 'undercity.jpg', 'You move through the ruins of what used to be living districts.');
    z.spooky = 5;
    z.size = 4;

    z.addEncounter(false, 'undercitytentacles');
    z.addEncounter(false, 'templeabbess');
    z.addEncounter(false, 'cultist', 2);

    z.addEncounter(true, 'drow');
    z.addEncounter(true, 'templepeasant');

    z.border.push({
        check: function() { return true; },
        display: function() {
            if (Game.undercity_cellar == 0) {
                var s = Game.Girls[Game.companion].skill.survival >= 4;
                var nv = Game.Girls[Game.companion].body.eyes.reflectors == true;
                if (nv && s) {
                    ToText("" + Game.Girls[Game.companion].name + " has discovered a hidden cellar!

");
                    Game.undercity_cellar = 1;
                } else if (s) {
                    // survival high enough, but no night vision
                    ToText("It really seems like something should be here, but it is too dark to tell.");
                } else if (nv) {
                    ToText(Game.Girls[Game.companion].name + " looks around, but doesn't find anything.");
                } else {
                    // no nightvision or survival.
                    ToText("There's obviously been foot traffic to this area, but it's just a dead end. " + Game.Girls[Game.companion].name + " looks around as well, but it's too dark.");
                }
            }
            // NOT ELSE.  undercity_cellar can be set to one above, so test again.
            if (Game.undercity_cellar) {
                var t = Game.days_elapsed - Game.undercity_cellar; // var6 = game day

                if (t > 5 && Math.tossCoin(Math.min((t-1)/10), 1)) // 50% chance 6 days after last entry
                    ToText("Move to the cellar");
                else
                    ToText("The cellar seems to be empty. Perhaps in time whoever used it will do so again.");
            }
        }
    });
    Data.AddZone(z);


code:
"use strict";

Test.tests.tools.math = {};
var t = Test.tests.tools.math;

/* Math and Numbers */

Number.isInteger = function(value) {
    if (value instanceof Number)
        value = value.valueOf();

    return typeof value === "number" &&
        isFinite(value) &&
        Math.floor(value) === value;
}

Math.clamp = function(a, b, c) {
    return Math.max(a, Math.min(b, c));
}

// Convert percents to probability
Math.withChance = function(c) {
    return Math.clamp(0, c, 100) / 100;
}

// Boolean, "true" with probability p
Math.tossCoin = function(p) {
    if (p === undefined)
        p = 0.5;

    if (p < 0 || 1 < p)
        throw new Error("Expected number in [0..1] interval, got " + p);

    var rnd = Math.random();
    return p >= rnd;
}

// Float from min to max, uniform distribution
Math.tossValue = function(min, max) {
    if (max < min)
        return undefined;

    return min + Math.random() * (max - min);
}

// Integer from min to max, uniform distribution
Math.tossDice = function(min, max) {
    if (max < min)
        return undefined;

    return min + Math.floor(Math.random() * (1 + max - min));
}

// check the distribution is ok
t.tossDice = function () {
    var iter = 1000;

    for (var min = -20; min < 10; min++) {
        for (var max = min; max < 20; max++) {
            var fn = function() {
                return Math.tossDice(min, max);
            }

            var expected = Math.floor(iter / (1 + max - min));
            var uniform = [];
            uniform.memset(expected, min, max);

            var real = Math.histogram(fn, iter, real);

            Math.compareHistograms(uniform, real);

            var maxchi = real.max();

            if (maxchi > 20) // seems to work for Math.random() on Firefox
                throw new Error("Distribution is not uniform: " + maxchi + " array: " + real);
        }
    }

    return true;
}

// Natural number from 0 to n, uniform, for switches
Math.tossCase = function(n) {
    return Math.tossDice(0, n);
}

// Natural number from 0 to n-1, uniform, for array indexes
Math.tossIndex = function(n) {
    return Math.tossDice(0, n-1);
}

// Produce histogram for function fn
Math.histogram = function (fn, iter) {
    var res = [];
    for (var i = 0; i < iter; i++) {
        var x = fn();
        if (res.hasOwnProperty(x))
            res[x] += 1;
        else
            res[x] = 0;
    }
    return res;
}

// Compare two histograms with Chi method
Math.compareHistograms = function(expected, got) {
    forEach(expected, function(i, v) {
        if (!got.hasOwnProperty(i))
            got[i] = 0;
    });
    forEach(got, function(i, v) {
        if (!expected.hasOwnProperty(i))
            throw new Error("Generated unexpected " + i);

        var variance = expected[i] - got[i];
        var chi = variance * variance / expected[i];
        got[i] = chi;
    });

    return got;
}

repo_name: spdevel/sfp

license: unknown


code:
forest
  * return to shaliq
  * encounters
     * enemybanditgirl|
     * enemywolf|You meet a lone creature.|Check
     * enemybanditaggressive|companion spots a lone person|Check
           - aggressive 2

  * advencounters
     * forestgnome|You meet a lone person.<yellow>SPECIAL</y>|Check
         - only if mission.dolin <= 1
     * enemybear|companion detects a lone creature.|Follow
         - aggressive 2

  * Borders
    * elvenforest|Go North, to the elven parts of the forest
    * crossroads|Go West, to the crossroads

elvenforest:
  * This portion of the forest is located dangerously close to eleven lands. They take poorly to intruders in their part of the woods so you should remain on your guard.
  * files/backgrounds/forest.jpg
  * Spooky 2

sforest: crossroads, elfforest
crossroads: meadows, sforest
meadows: crossroads, highlands
highlands: sea, meadows
sea: highlands

elfforest: sforest, grove
grove: elfforest, marsh
marsh: grove,

path: misc/map


code:
s/spells_known\[0\]/spells_known['mind_read']/g
s/spells_known\[1\]/spells_known['sedation']/g
s/spells_known\[2\]/spells_known['heal']/g
s/spells_known\[3\]/spells_known['dream']/g
s/spells_known\[4\]/spells_known['entrancement']/g
s/spells_known\[5\]/spells_known['fear']/g
s/spells_known\[6\]/spells_known['domination']/g
s/spells_known\[7\]/spells_known['summon_tentacle']/g
s/spells_known\[8\]/spells_known['daze']/g
s/spells_known\[9\]/spells_known['mutate']/g
s/spells_known\[10\]/spells_known['mind_blast']/g
s/spells_known\[11\]/spells_known['rejuvenation']/g
s/spells_known\[12\]/spells_known['guidance']/g

s/Spell\[0\]/Spell['mind_read']/g
s/Spell\[1\]/Spell['sedation']/g
s/Spell\[2\]/Spell['heal']/g
s/Spell\[3\]/Spell['dream']/g
s/Spell\[4\]/Spell['entrancement']/g
s/Spell\[5\]/Spell['fear']/g
s/Spell\[6\]/Spell['domination']/g
s/Spell\[7\]/Spell['summon_tentacle']/g
s/Spell\[8\]/Spell['daze']/g
s/Spell\[9\]/Spell['mutate']/g
s/Spell\[10\]/Spell['mind_blast']/g
s/Spell\[11\]/Spell['rejuvenation']/g
s/Spell\[12\]/Spell['guidance']/g

s/Game.spells_known\[\([^]]*\)] == 1/Game.SpellKnown(\1)/g
s/Game.spells_known\[\([^]]*\)] == 0/!Game.SpellKnown(\1)/g
s/Game.spells_known\[\([^]]*\)] != 1/!Game.SpellKnown(\1)/g
s/Game.spells_known\[\([^]]*\)] = 1/Game.LearnSpell(\1)/g
s/Game.spells_known\[\([^]]*\)]=1/Game.LearnSpell(\1)/g

path: misc/sedify


code:
s/function ToText(s,fromstart,div){/function ToText(s,fromstart){if (printDiv!=$('print')){return ToText2(s,fromstart);} if (fromstart) g_totext = s;else g_totext+=s;} function ToText2(s,fromstart,div){/
s@function FlipPage(){@function FlipPage(){ToText2(g_totext,true);@
#s@eval(Loc.Text);@eval(Loc.Text);ToText2(g_totext);@g
s/var asm_var0;/var asm_var0;var g_totext;/
s/closeWinds();ToText('',true);/closeWinds();ToText2('',true);/


code:
var13
 0: uneasy
 1: cheerful
 2: melancholic
 3: rebellious

var22: (response)
 1: strict
 2: kind
 3: boisterous
 4: apathetic

cheerful:
  strict: loyalty -2, obeidence +5
  kind: obedience -5, stress -5, loyalty + 12
  boisterous: stress -20, loyalty +8, obiedience -12
  apathetic: stress +10, loyalty +10

melancholic:
  strict : loyalty +4
  kindness: loyalty +7, stress +10
  boisterous: random
	1: loyalty-5;
	2: loyalty+12, stress-10
  apathetic: loyalty+10

Races:
0 human
1 elf
2 drow
3 orc
4 beastkin cat
5 beastkin fox
6 beastkin wolf
7 seraph
8 demon
9 fairy
10 dryad
11 dragonkin
12 gnome
13 taurus
14 goblin
15 slime
16 harpy
17 lamia
18 arachna
19 centaur
20 bunny
21 nereid
22 scylla
23 tanuki

Tongue Modification:
mana = 50, gold = 400, 0 = 16

Demon lab assistant: 1/5 mana and gold

Jobs:

forage: 20-30 food + 6*survival, maybe additional 18*survival

hunt: 35  + 10*survival chance of bringing home food, 40-55 + 20*survival food, 30% more if arachna, costs 10 health and 1 loyalty

library: gain 25 + 5*library level xp - 2*girl level

cook: 3*number of girls XP.  Reduces food requirement by 1+service

nurse: Each resident heals an additional 5+3*service, gain 3*girls XP

whore: 0-15 + sex*10 + allure * 5 + charm/2.  Multiply by pretty/40.
	 +25 for nipplefuck augmentation, +20 for tongue augmentation
	 XP gain is gold/10
	 10 health, 10 stress (-corruption/20)
	 gain 3-6 corruption
	 obedience and loyalty decrease unless frivolous
	 bunnys like this job
max 462

escort: 0-25 + 15*sex + 5 * allure + charm, multiply by pretty/40
        XP gain gold/12
	5 health, 10 stress (reduced to 0 by confidence)
	if corruption < 50 or frivolus
	    reduce obedience by confidence/5 and loyalty by 4.
max 562

fucktoy: 0-40 + sex*15 + corruption.
	+30 for nipple aug, +20 for tongue aug.
	15 health
	non-bunnies take 20 stress
	if corruption < 75 and not deviant
		lose confidence/3 obedience
		10 loyalty
		lose 3-8 confidence
		gain 8-13 corruption
max 265

store: 10-20+10*management+5*service  xp/4
max 95

Mage's Order: 40-50 + 12*magicarts + 7*management
max 145

entertainer: 30-40 + 10*allure + 6*bodycontrol + charm/4
	nereid gets 20% bonus
max 174

Jailer: 8 xp, effect on each jailed girl:
	gain 2+service*2 health
	lose 4+4*management stress
	gain 2+3*service + 3*management obedience

headgirl : xp += 2*total girls
  strict:
	if obedience < 65
		obedience += 5 + 5*management
	        stress += 5-10
  patronage:
 	loyalty + allure
	stress - 3*allure
	doesn't work on masochists

farm (milking): 0-10 + 20*breast size + 20*size*extratits/3 if lactating
	multiply by 1.33 if taurus
	requires farmer with maxed management & service or takes a loss
	max 210, 279 if taurus.
	stress + 13 if not will broken

farm (snails):
     160-185  * 1.25 for augmented nipples, * 1.2 for harpy
     requires maxed farmer
     stress + 16 if not deviant

apiary (mana worms):
     1-2 mana, +1 if drow
     lab assistant: + magicarts/2, +service/2.5
     7-8 if maxed

other nightly stuff:
elves in any role gain 2-3 confidence as long as it's below willpower*15

orc heals an extra 5

beastkin or halfkin wolf gain 2-3 courage if they have a refined brand.

toxicity drops to 0 on slimes
if toxicity > 35, 40% chance of taking 15 health and 20 stress.
if > 60 and didn't take the stress, 40% chance of a mutation
scylla can't get mutations

everyone (residents, jailed, farmed, etc):
Gain 10 health, 1 loyalty, reduce 12 stress.
fairy reduce an additional 12 stress
obedience reduces by willpower*3 + confidence/4 (max 41) minus loyalty.  High loyalty causes obedience to gain.

girls resting gain an additional 10 health and stress

passive girls gain 10 obedience
clingy gains 3 loyalty
if prudes have > 50 corruption, lose the prudishness
pliable with loyalty > 80 become devoted and gain 20-25 in courage and wit
pliable with corruption > 80 become slutty, gain 20-25 confidence and charm
devoted makes minimum loyalty 80

Rules:
  silence: reduces courage by 2-4, min 25 and increases obedience by 4-8.
  pet: reduces confidence by 6-10, charm by 4-7, increases obedience by 10-15.
  partial nudity: corruption < 30 reduces confidence by 3-6, corruption 3-5.
                  corruption > 30 increases lust 5-10.

Obedience:
  < 40 and not silenced, reduces obedience in the rest of the girls
       by her charm/3
       unless they're a loner

  < 60 and loyalty < 25:  50% chance steals 20-60 gold or 20-50 food.

Clothing:
 1: normal
 2: sundress: stress  -4 end of day
 3: maid: obedience +3 eod
 4: kimono - nothing
 5: Pet suit - confidence -2-4, obidience +5
 6: ninja suit
 7: miko outfit
 8: chainmail bikini
 9: Bedlah  corruption+2-4, charm +2-4 if < 30

Obedience again:
 < 25 and normal brand
     if courage/4 + stress/5 + willpower*5 > loyalty*2 + obedience + wit/5
	     escapes
 < 25 and no brand:
     if courage/3 + stress/4 + willpower * 10 + wit/3 > loyalty*2 + obedience
	     escapes

   Wit moves, because escaped branded slaves will be executed and they know that.

Prisoners:
     gain 10 stress, lose 15 health if not being fed
  otherwise:
     8 food minus cook bonus
     gain 10 health
     lose 10 stress
     fairy loses an additional 10 stress
     obedience reduces by willpower*3+confidence/4, gains by loyalty+5.

     all potion and spell effects nulled.

Day count:
    every 3 days a new custom slave request is generated
    every 4 days sebastian has a new slave

path: misc/walkthrough


code:
"use strict";
    /* These are selected at random when Girl.mutate() is run.  You only need to add them here */

    Data.AddMutation(function(girl) {
        ToText("

" + girl.name + '' + "'s ears mutated and changed shape. ");
        girl.body.ears.type = Math.tossDice(1, BodyType.c.ears.c.type.values.length - 1);
    });

    Data.AddMutation(function(girl) {
        var newtail = Math.tossIndex(BodyType.c.tail.c.type.values.length);
        if (newtail > 0) {
                if (girl.body.tail.type == "none") {
                    ToText(" A tail grew out of " + girl.name + '' + "'s ass. ");
                } else {
                    ToText("  " + girl.name + '' + "'s tail mutated and changed shape. ");
                }
        } else if (girl.body.tail.type != "none")
                ToText(" " + girl.name + ""s tail shrinks and vanishes.");
        girl.body.tail.type = newtail;
    });

    Data.AddMutation(function(girl) {
        var newhorns = BodyType.c.horns.c.type.random();
        if (newhorns != "none") {
            if (girl.body.horns.type == "none")
                ToText(" A pair of horns grew on the " + girl.name + '' + "'s head. ");
            else
                ToText("  " + girl.name + '' + "'s horns mutated and changed shape. ");
        } else if (girl.body.horns.type != "none")
                ToText(" " + girl.name + " suddenly gasps as her horns crumble into dust. ");

        girl.body.horns.type = newhorns;
    });

    Data.AddMutation(function(girl) {
        if (girl.body.wings.type == "none") {
            ToText(" A pair of wings grew on the " + girl.name + '' + "'s back. ");
        } else {
            ToText("  " + girl.name + '' + "'s wings mutated and changed shape. ");
        }
        girl.body.wings.type = Math.tossDice(1, BodyType.c.wings.c.type.values.length - 1);
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        ToText("

" + girl.name + '' + "'s skin mutated and changed pigmentation. ");
        girl.body.skin.color = BodyType.c.skin.c.color.random();
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        ToText("

");
        girl.body.skin.coverage = Math.tossDice(1, BodyType.c.skin.c.coverage.values.length - 1);
        ToText("

" + girl.name + '' + " has mutated and now covered in ");
        if (girl.body.skin.coverage == "scales") {
            ToText(" scales. ");
        } else {
            ToText(" fur. ");
        }
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        ToText("

");
        if (Math.tossCoin() && girl.body.chest.pairs == 0) {
            ToText("

");
            girl.body.chest.pairs += Math.tossDice(1, 3);
            girl.body.chest.ripe = Math.tossCoin();
            ToText("

" + girl.name + '' + " mutated and grew additional ");
            if (girl.body.chest.pairs == 1) {
                ToText(" pair ");
            } else {
                ToText(" pairs ");
            }
            ToText("  ");
            if (girl.body.chest.ripe == false) {
                ToText(" " + girl.body.chest.pairs + '' + " of nipples below her original ones. ");
            } else {
                ToText(" " + girl.body.chest.pairs + '' + " of tits below her original ones. ");
            }
            ToText("

");
        } else {
            ToText("

" + girl.name + '' + "'s tits mutated and changed in size. ");
            girl.body.chest.size = Math.tossDice(1, 5);
            ToText("

");
        }
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        ToText("

" + girl.name + '' + "'s ass mutated and changed in size. ");
        girl.body.ass.size = Math.tossDice(1, BodyType.c.ass.c.size.tiers.length); //FIXME
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        ToText("

");
        if (Math.tossCoin() && girl.body.cock.size >= 1 && Game.settings.futanari == 1) {
            ToText("

" + girl.name + '' + "'s penis mutated and changed in shape. ");
            girl.body.cock.type = BodyType.c.cock.c.type.random();
            ToText("

");
        } else if (Game.settings.futanari == 1) {
            ToText("

");
            if (girl.body.cock.size == 0) {
                ToText(" " + girl.name + '' + " grew a penis due to mutation.  ");
            } else {
                ToText(" " + girl.name + '' + "'s penis mutated and changed in size. ");
            }
            girl.body.cock.size = Math.tossDice(1, 3);
            ToText("

");
        }
    });

    Data.AddMutation(function(girl) {
        if (girl.body.balls.size == 0 && Game.settings.futa_balls == 1) {
            ToText(" " + girl.name + '' + " grew a pair of testicles due to mutation. ");
        } else if (Game.settings.futa_balls == 1) {
            ToText(" " + girl.name + '' + "'s testicles mutated and changed in size. ");
        } else {
            DisplayLocation('mutation');
        }
        girl.body.balls.size = Math.tossDice(1, BodyType.c.balls.c.size.tiers.length); //FIXME
    });

    Data.AddMutation(function(girl) {
        girl.body.hair.color = girl.body.race.hairColors.random();
        ToText("

" + girl.name + '' + "'s hair changed color due to mutation.

");
    });

    Data.AddMutation(function(girl) {
        if (Math.tossCoin(0.3) && girl.body.eyes.pupils == "round") {
            ToText("

" + girl.name + '' + "'s pupils changed shape.

");
            girl.body.eyes.pupils = BodyType.c.eyes.c.pupils.random();
        } else {
            girl.body.eyes.color = girl.body.race.eyeColors.random();
            ToText("

" + girl.name + '' + "'s eyes changed color due to mutation.

");
        }
        ToText("

");
    });

    Data.AddMutation(function(girl) {
        girl.body.face.beauty = girl.body.face.beauty + Math.tossDice(-10, 10);
        ToText("

" + girl.name + '' + "'s face structure slighly changed.

");
    });

    Data.AddMutation(function(girl) {
        /* Swap two attributes at random */
        var keys = Object.keys(girl.attr);
        var from = keys.random();
        var to = from;
        while (to == from)
            to = keys.random();
        var tmp = girl.attr[to];
        girl.attr[to] = girl.attr[from];
        girl.attr[from] = tmp;
        girl.WillRecounter();
        ToText("

" + girl.name + " blinks a few times.  Something changed, but what?");
    });

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

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

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


code:
"use strict";

function GameStart() {
    console.log("Starting new game");

    TimeToFade = 500;
    asmSys_gender = 0;
    asmSys_choice = 0;
    closeWinds();
    ToText2('', true);
    stopMusic();
    MenuInit();
    printAddText.innerHTML = '';
    printTitle.innerHTML = '';
    printImage.src = pictureDefault;
    document.body.style.backgroundImage = "url(files/backgrounds/bricks.jpg)";
    clearSprites();
    scrollDiv();
    CurLocation = '';

    /* Now show the Start screen */

    NoBack();
    stopAllEvents();
    if (GetLocation('Start') == false) {
        PrintLocation('StartPassages');
    } else {
        PrintLocation('Start');
    }
    NoBack();
    showCover = true;
}

// FIXME: cleanup this, the thing above and "Start" location
function GameStartFromData(data) {
    closeWinds();
    stopAllEvents();

    Game = new Game_(data.game);

    CurLocation = GetLocation(data.title);
    try {
        PrevLocation = GetLocation(data.prev);
    } catch (e) {
        PrevLocation = '';
    }

    fontSize = data.fontsize;
    animation_enable = data.animation_enable;
    audio_enable = data.audio_enable;
    musicAudio.src = data.musicAudio;
    playMusic();
    printImage.src = data.printImage;
    spritesDiv.innerHTML = data.spritesDiv;
    menuDiv.innerHTML = data.menuDiv;
    printTitle.innerHTML = data.printTitle;
    for(var i = 0; i< (data.events.length - 1); i++) {
        var ea = data.events[i].split(':');
        startEvent(Location[ea[0]].Title, ea[1]);
    }

    TimeToFade = data.timetofade;
    asmSys_gender = data.Sys_gender;
    asmSys_choice = data.Sys_choice;

    showCover = false;
    PrintLocation(CurLocation.Title);
}

Location.push(new Locations("Start", function() {
    Game.setup = {
        starting_girl_race: undefined,
        tmp2: 0,
        tmpgirl: undefined,
        reset: undefined,
    };

    printImage.src = pictureDefault;
    printImage.onload = function() {
        printImage.src  = 'files/backgrounds/intro.jpg';
        this.onload = null;
    }
    showImageForce();

    ToText("

Strive for Power v1 Web edition
");

    ToText("
Version: " + Data.version + "")
    ToText(" Changelog, credits, links");

    ToText("
");
    ToText("This game is strictly for those above the age of 18 (or your regional equivalent).");
    ToText(" This game contains some content which could offend some people.");
    ToText(" All characters described or depicted are either 18 or older.");
    ToText(" This work is one of pure fiction, and applying content depicted in this game to real life may break the law.");
    ToText(" We hold no responsibility for your actions.");
    ToText(" By proceeding on you agree that you are over the age of 18 (or regional equivalent), and you’re not going to complain.");
    ToText("
");

    ToText("Yes, I'm adult and I want to continue");
    ToText("

Leave");

    ToText("
");

    ToText("
Saves
");
    ToText("Saves are stored in browser cookies, which are based on the folder you run the game out of.");
    ToText(" If you move it, you will either have to restart or use export/import to copy your current game in.");
    ToText(" Save export is the gear icon or the right-click context menu.");

    ToText("

");
    for (var i = 1; i <= 6; i++) {
        var item = localStorage.getItem(i + '_' + filename);
        if (item) {
            ToText("Load Slot " + i + "");
        }
    }

    var hashes = window.location.hash.substr(1).split("#");

    if (hashes.indexOf("debug") !== -1) {
        ToText("
Developer corner
");

        ToText("

Run tests:");
        ToText(" All");
        forEach(Test.tests, function(k, v) {
            var res = Test.runTestsToConsole(v);
            var auto = " PASS" : "red'>FAIL " + res.failed + " of ") + " " + res.total;
            ToText(" " + k + auto + " ");
        });

        ToText("

Load a contributed save that was broken in some way:

");
        Test.saves.forEach(function(save) {
            ToText(" " + save.description + "", false);
        });
    }
}, 1));

Location.push(new Locations("changelog", function() {
    ToText(Data.changelog);
}, 1));

Location.push(new Locations("tests", function() {
    var acc = function (name, res, msg, isLeaf, level) {
        if (res === undefined)
            return "";

        ToText("
PASS" : "red'>FAIL") + " " + name + ": " + msg);
        if (!res)
            console.log(msg);
        return "";
    }

    Test.runTestTree(Data.savestring, "", acc);
    ToText("

Run again", false);
    ToText("

Back", false);
}, 1));

Location.push(new Locations("load", function() {
    StartFromStoryString(Data.savestring);
}, 1));

Location.push(new Locations("leave", function() {
    ToText("Goodbye.");
}, 1));

Location.push(new Locations("prologue", function() {
    ToText("This is a text game, but its mechanics are considerably convoluted, and may appear difficult to grasp at first. Would you like to read a brief tutorial? (recommended for those playing for the first time)

See tutorial

Start new game

Load game from text string (press ctrl+v to paste)

");
    var value = Data.savestring;
    if (!value && value != 0) {
        value = '';
    } else {
        value = value.toString();
    }
    var focus = '';
    if (!isTouchDevice) {
        focus = 'autofocus';
    }
    ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Data.savestring=Trim(this.childNodes[0].value);if(!isNaN(Data.savestring)){Data.savestring=parseFloat(Data.savestring);} PrintLocation("load");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
}, 1));

Location.push(new Locations("prologuetut", function() {
    ToText("First, you can save, load, and access some of the game engine options by clicking the gear icon in the top right corner. The save system is far from perfect and uses cookies if you play in a browser. Please save at the main mansion screen or go there immidiatly after reload, otherwise stuff may get bugged." + '' + " Sadly, this feature is purely engine-specific, and I have no control over it. However, there's another way to save progress and transfer it with string. For this follow to the settings&help tab and click 'save', then copy code and past it next time you start the game.

");
    ToText("You navigate the game by selecting options, although some links are more of a tooltip. You can click them without leaving the page or changing anything; for example you will see many tooltips shown as (?) — you can click those. Otherwise, there will sometimes be 'Help' buttons which may give you a broad description about various gameplay mechanics.

");
    ToText("Colored words" + '' + " are there to highlight important parts of the text and make it easier to analyze at first glance.

");
    ToText("This game is still being developed and a large amount of content is still in the planning phase, including character interactions and other options. If you want to report bugs, make a suggestion, or contact me, use my blog at http://strivefopower.blogspot.com/

");
    ToText("Have fun and enjoy the game.

");
    ToText("Start the game", false);
}, 1));

Location.push(new Locations("start1", function() {
    Game.setup.tmp2 = -1;

    ToText("
Prologue

");
    ToText("Becoming a mage is a rather trivial feat; nearly any drunkard on a bet is capable of using even basic magic. The problem is, mana" + '' + ", the fuel for magic, can't be obtained so easily, and it’s the accrual of mana that separates the hacks from the truly skilled. The most widely used methods of harvesting mana were discovered by the very first wizards, and developed over the centuries. These methods are conducted using the flow of energy given off by living beings, commonly referred to as life energy. Life energy is not something to trifle with, however, and using a living creature as a medium from which to draw mana can be dangerous with poor preparation. It is important to keep safety in mind when harvesting mana.

");
    ToText("However, even with safety in mind, you must also take into account the potency of the energy you are using. Barring a few eccentric schools, most magicians seek to exploit the life energy of sentient beings, who release the greatest quantities of mana. You are part of the callous majority, looking down on those who use alternative methods as fools without ambition. There are two main methods used to obtain mana from others for your needs. The first, known as Destructive Harvesting, is dull and straightforward, requiring only the perpetuation of pain and suffering. Death is not a requirement of this method, though it is often the result. Suffering, pain, and wounds all produce powerful mana springs to be harvested by a prepared wizard, making it popular when a large amount of mana is required in a short amount of time.

");
    ToText("The second method, known as Creative Harvesting, is trickier, but nevertheless more fun. Even simple masturbation can produce mana through this method, although it would be foolish to rely entirely on the power of just one person. With enough participants, mana generated through this method can match even the largest sacrifices, though requiring much more time and preparation. Creation and destruction are, after all, two sides of the same coin, a concept even many veteran mages fail to grasp.

");
    ToText("For now though, you decide that violence is too much for you, so you focus on sexuality.

");
    ToText("Proceed");
}, 1));

Location.push(new Locations("playercharacter", function() {
    var race = undefined;
    try {
        var race = Data.getRace(Game.tmp0);
    } catch (e) {
        // ignore errors from getRace
    }
    Game.tmp0 = 0;
    if (race != undefined) {
        var oldplayer = Game.player;
        Game.player = new Girl();
        Game.player.generate(race, oldplayer.body.age);
        Game.player.name = oldplayer.name;
        Game.player.lastname = oldplayer.lastname;
        Game.player.body.age = oldplayer.body.age;
        Game.player.body.gender = oldplayer.body.gender;
        Game.player.body.hair.color = oldplayer.body.hair.color;
        Game.player.body.eyes.color = oldplayer.body.eyes.color;
        Game.player.body.chest.size = oldplayer.body.chest.size;
        Game.player.body.cock.size = oldplayer.body.cock.size;

        /* if the chosen skin color is valid for the new race, keep it.
         * Otherwise go with the randomly picked one. */

        if (race.skinColors.indexOf(oldplayer.body.skin.color.index) != -1)
            Game.player.body.skin.color = oldplayer.body.skin.color;
    }

    ToText("(Hit 'Enter' after filling in each field to save input for that field)

Name — " + Game.player.name + '' + " ");
    var value = Game.player.name;
    if (!value && value != 0) {
        value = '';
    } else {
        value = value.toString();
    }
    var focus = '';
    if (!isTouchDevice) {
        focus = 'autofocus';
    }
    ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.player.name=Trim(this.childNodes[0].value);PrintLocation("playercharacter");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
    ToText("

Surname — " + Game.player.lastname + '' + " ");
    var value = Game.player.lastname;
    if (!value && value != 0) {
        value = '';
    } else {
        value = value.toString();
    }
    var focus = '';
    if (!isTouchDevice) {
        focus = 'autofocus';
    }
    ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.player.lastname=Trim(this.childNodes[0].value);if(!isNaN(Data.default_lastname)){Data.default_lastname=parseFloat(Data.default_lastname);} PrintLocation("playercharacter");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");

    ToText("

Race — ");

    var races = Data.listRaces().filter(function(r) { return r.tags.indexOf("market") != -1; });
    races.forEach(function(race) {
        if (!Game.player.isRace(race))
            ToText(" " + race.name.capitalize() + "  ");
        else
            ToText(" " + race.name.capitalize() + "  ");
    });

    ToText("

Gender (may not be fully supported by descriptions or narration) — ");
    if (Game.player.body.gender.index != 2) {
        ToText(" Male ");
    } else {
        ToText(" Male" + '' + " ");
    }
    if (Game.player.body.gender.index != 1) {
        ToText(" Futanari ");
    } else {
        ToText(" Futanari" + '' + " ");
    }
    ToText("

Age — ");
    if (Game.player.body.age != 1) {
        ToText(" Young ");
    } else {
        ToText(" Young" + '' + " ");
    }
    if (Game.player.body.age != 2) {
        ToText(" Middle-aged ");
    } else {
        ToText(" Middle-aged" + '' + " ");
    }
    ToText("

Hair Color — " + Game.player.body.hair.color + '' + " ");
    var value = Game.player.body.hair.color;
    if (!value && value != 0) {
        value = '';
    } else {
        value = value.toString();
    }
    var focus = '';
    if (!isTouchDevice) {
        focus = 'autofocus';
    }
    ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.player.body.hair.color=Trim(this.childNodes[0].value);if(!isNaN(Game.player.body.hair.color)){Game.player.body.hair.color=parseFloat(Game.player.body.hair.color);} PrintLocation("playercharacter");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
    ToText("

Eye Color — " + Game.player.body.eyes.color + '' + " ");
    var value = Game.player.body.eyes.color;
    if (!value && value != 0) {
        value = '';
    } else {
        value = value.toString();
    }
    var focus = '';
    if (!isTouchDevice) {
        focus = 'autofocus';
    }
    ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.player.body.eyes.color=Trim(this.childNodes[0].value);if(!isNaN(Game.player.body.eyes.color)){Game.player.body.eyes.color=parseFloat(Game.player.body.eyes.color);} PrintLocation("playercharacter");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
    ToText("

Skin color — ");
    Game.player.body.race.skinColors.forEach(function (i) {
        var out = '';
        if (Game.player.body.skin.color.index != i)
            out += " ";
        else
            out += " ";
        ToText(out + BodyType.c.skin.c.color.values[i] + " ");
    });
    ToText("

");
    /* same tits for everyone. */
    var tits = [
        { desc: 'Masculine', val: 0,},
        { desc: 'Flat', val: 1,},
        { desc: 'Small', val: 2,},
        { desc: 'Medium', val: 3,},
        { desc: 'Big', val: 4,},
        { desc: 'Huge', val: 5,},
    ];

    ToText(" Breast size — ");
    tits.forEach(function(choice) {
        var out = '';
        if (Game.player.body.chest.size != choice.val)
            out +=" ";
        else
            out += " ";
        out += choice.desc + " ";
        ToText(out);
    });

    ToText("

Penis size — ");
    if (Game.player.body.cock.size != 1) {
        ToText(" Small ");
    } else {
        ToText(" Small" + '' + " ");
    }
    if (Game.player.body.cock.size != 2) {
        ToText(" Medium ");
    } else {
        ToText(" Medium" + '' + " ");
    }
    if (Game.player.body.cock.size != 3) {
        ToText(" Huge ");
    } else {
        ToText(" Huge" + '' + " ");
    }

    ToText("

Choose your play mode.

Easy and normal modes involve progression and lots of locked stuff requiring completing quests and exploring to unlock and are also better explained.

Easy mode has a lot of starting money, normal mode does not.

Sandbox mode will unlock most stuff and disable many quests and progression. Advisable to those, who played previous version and want a quick start. (Currently inadvisable as buggy)

");

    ToText("

Continue in easy mode");
    ToText("

Continue in normal mode");
    ToText("

Continue in sandbox mode", false);
}, 1));

Location.push(new Locations("startinggirl", function() {
    ToText("

");
    if (Game.setup.starting_girl_race == 1) { // this is a flag.
        if (Game.sandbox_mode > 0) {
            Game.gold = 5000;
        } else {
            Game.gold = 500;
        }
        Game.newgirl.generate(Game.newgirl.body.race, undefined, Math.tossDice(1, 4));
        Game.newgirl.backstory = Data.backstory1[0];
        Game.newgirl.obedience = 70;
        Game.newgirl.loyalty = 25;
        Game.newgirl.body.face.beauty = 40;
        Game.setup.starting_girl_race = 0; // this is a flag.
    }
    Game.newgirl.WillRecounter();

    // Save the first available state
    if (Game.setup.tmpgirl === undefined)
        Game.setup.tmpgirl = deepclone(Game.newgirl);

    if (Game.setup.tmp2 == 0) {
        ToText(Game.newgirl.describe({requests: [ 'name', 'detailed' ]}));

        ToText("

Change race and reroll
", false);

        var value = Game.newgirl.name;
        if (!value && value != 0) {
            value = '';
        } else {
            value = value.toString();
        }
        var focus = '';
        if (!isTouchDevice) {
            focus = 'autofocus';
        }
        ToText("Change name (Hit 'Enter' after filling to save):
");
        ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.newgirl.name=Trim(this.childNodes[0].value);if(!isNaN(Game.newgirl.name)){Game.newgirl.name=parseFloat(Game.newgirl.name);} PrintLocation("startinggirl");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>

");
        ToText("Random name
")

        if (Game.newgirl.body.age == 2 || (Game.newgirl.body.age == 1 && Game.settings.loli == 1)) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Age ");
        if (Game.newgirl.body.age <= 1) {
            ToText(" ▶", false);
        }
        ToText("
");
        if (Game.newgirl.body.hair.length >= 1) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Hair Length");
        if (Game.newgirl.body.hair.length <= 3) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");
        if (Game.newgirl.body.hair.style.index >= 1) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Hair Style");
        if (Game.newgirl.body.hair.style.index <= 4) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
Change Hair Color
Change Eye Color
");
        if (!Game.newgirl.isRace(['drow', 'orc', 'dryad'])) {
            if (Game.newgirl.body.skin.color.index >= 1) {
                ToText(" ◀", false);
            } else {
                ToText(" ◀");
            }
            ToText(" Skin Color");
            if (Game.newgirl.body.skin.color <= 4) {
                ToText(" ▶", false);
            } else {
                ToText(" ▶  ");
            }
            ToText("
");
        }
        ToText("

Confirm");

    } else if (Game.setup.tmp2 == 1) {
        ToText(Game.newgirl.describe({requests: [ 'genitals', 'sexual' ]}));
        ToText("

");

        if (Game.newgirl.body.chest.size >= 2) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Breast Size");
        if (Game.newgirl.body.chest.size <= 4) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");
        if (Game.newgirl.body.ass.size >= 2) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Butt Size");
        if (Game.newgirl.body.ass.size <= 4) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");

        if (!Game.newgirl.body.pussy.virgin.value) {
            ToText(" ☐");
        } else {
            ToText(" ☑");
        }
        ToText(" Is a virgin
");
        if (Game.settings.futanari == 1) {
            if (Game.newgirl.body.cock.size < 1 && Game.settings.futanari == 1) {
                ToText(" ☐");
            } else if (Game.newgirl.body.cock.size >= 1) {
                ToText(" ☑");
            }
            ToText(" Have penis
");
            if (Game.newgirl.body.cock.size != 0) {
                if (Game.newgirl.body.cock.size >= 2) {
                    ToText("◀", false);
                } else {
                    ToText(" ◀");
                }
                ToText(" Penis Size ");
                if (Game.newgirl.body.cock.size <= 2) {
                    ToText(" ▶", false);
                } else {
                    ToText(" ▶  ");
                }
                ToText("
");
            }
            if (Game.settings.futa_balls == 1) {
                if (Game.newgirl.body.balls.size < 1 && Game.settings.futa_balls == 1) {
                    ToText(" ☐");
                } else if (Game.newgirl.body.balls.size >= 1) {
                    ToText(" ☑");
                }
                ToText(" Have testicles
");
                if (Game.newgirl.body.balls.size != 0) {
                    if (Game.newgirl.body.balls.size >= 2) {
                        ToText("◀", false);
                    } else {
                        ToText(" ◀");
                    }
                    ToText(" Testicles Size ");
                    if (Game.newgirl.body.balls.size <= 2) {
                        ToText(" ▶", false);
                    } else {
                        ToText(" ▶  ");
                    }
                    ToText("
");
                }
            }
        }
        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 2) {
        ToText(Game.newgirl.describe({requests: [ 'name', 'backstory', 'siblings', 'brand', 'standings' ]}));
        ToText("

");

        Game.setup.reset = function () {
            var ng = Game.newgirl;
            var tg = Game.setup.tmpgirl;
            ng.father = tg.father;
            ng.mother = tg.mother;
            ng.backstory= tg.backstory;
            ng.obedience = tg.obedience;
            ng.loyalty = tg.loyalty;
            ng.body.brand.type = tg.body.brand.type;
        };

        ToText("

She was

");
        ToText("Your childhood friend

");
        if (Game.newgirl.body.race == Game.player.body.race) {
            if (Game.player.body.age <= Game.newgirl.body.age) {
                ToText("Your older sister

");
            }
            if (Game.player.body.age >= Game.newgirl.body.age) {
                ToText("Your younger sister

");
            }
        }
        ToText("Your foster sister

");
        ToText("Your trusted personal servant

");
        ToText("Your slave

");

        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 3) {
        ToText(Game.newgirl.describe({requests: [ 'skills' ]}));
        ToText("

");

        Game.setup.reset = function () {
            var ng = Game.newgirl;
            var tg = Game.setup.tmpgirl;
            ng.skill = deepclone(tg.skill);
        };

        ToText("

Her main hobby was

");
        ToText("Fighting and keeping herself in shape

");
        ToText("Outdoorsmanship and wilderness survival

");
        ToText("Ladylike behavior and pleasing others

");
        if (Game.newgirl.backstory == '- I grew up with you as your servant' && !Game.newgirl.body.pussy.virgin.value) {
            ToText("

Serving you with her body

");
        }
        ToText("

Studying magic theory

");
        ToText("

Unknown

");

        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 4) {
        ToText(Game.newgirl.describe({requests: [ 'attributes' ]}));
        ToText("

");

        Game.setup.reset = function () {
            var ng = Game.newgirl;
            var tg = Game.setup.tmpgirl;
            ng.attr = deepclone(tg.attr);
        };

        ToText("

Her strength lies in her

");
        ToText("Bravery, as she often went out of her way for you

");
        ToText("Character, as she stayed strong and maintained an independent will despite adverse circumstances

");
        ToText("Mind, as she was always level-headed and clever when dealing with problems

");
        ToText("Charisma, as she could easily attract other people to herself

");
        ToText("Unknown

");
        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 5) {
        ToText(Game.newgirl.describe({requests: [ 'traits' ]}));
        ToText("

");

        Game.setup.reset = function () {
            var ng = Game.newgirl;
            var tg = Game.setup.tmpgirl;
            ng.trait_known = tg.trait_known;
            ng.trait = tg.trait;
        }

        ToText("

Sexually, she's

");

        Girl.prototype.sexualTraits.forEach(function(trait) {
            var trait_known = 0;
            if (trait.name != 'none')
                trait_known = 1;
            ToText("" + trait.desc + "

");
        });

        ToText("Unknown

");
        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 6) {
        ToText(Game.newgirl.describe({requests: [ 'traits' ]}));
        ToText("

");

        ToText("

Lastly, mentally she's

");

        Girl.prototype.mentalTraits.forEach(function(trait) {
            ToText("" + trait.desc + "

");
        });
        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 7) {
        ToText(Game.newgirl.describe({profile: 'startinggirl' }));

        ToText("

Available gold: " + Game.gold + '' + ". These options will cost gold to change.

");

        if (Game.newgirl.body.face.beauty >= 10) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Face" + '' + " ");
        if (Game.newgirl.body.face.beauty <= 90 && Game.gold >= 100) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");
        if (Game.newgirl.attr.courage >= 20) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Courage" + '' + " ");
        if (Game.newgirl.attr.courage <= 90 && Game.gold >= 75) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶ ");
        }
        ToText("
");
        if (Game.newgirl.attr.confidence >= 20) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Confidence" + '' + " ");
        if (Game.newgirl.attr.confidence <= 90 && Game.gold >= 75) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");
        if (Game.newgirl.attr.wit >= 20) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Wit" + '' + " ");
        if (Game.newgirl.attr.wit <= 90 && Game.gold >= 75) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("
");
        if (Game.newgirl.attr.charm >= 20) {
            ToText(" ◀", false);
        } else {
            ToText(" ◀");
        }
        ToText(" Charm" + '' + " ");
        if (Game.newgirl.attr.charm <= 90 && Game.gold >= 75) {
            ToText(" ▶", false);
        } else {
            ToText(" ▶  ");
        }
        ToText("

Confirm");
    } else if (Game.setup.tmp2 == 8) {
        ToText(Game.newgirl.describe({profile: 'resident' }));
        ToText("

");
        ToText("

Finish customization

");
        ToText("

Start over

");
    }

    if (Game.setup.tmp2 > 0) {
        ToText("

Previous page

");
    } else {
        ToText("

Previous page

");
    }
    ToText("

Change settings

");
}, 1));

Location.push(new Locations("startinggirlrace", function() {
    ToText("Race affects looks, skills and possible interactions. Some races may be disabled by settings.

Current race: " + Game.newgirl.body.race.name + '' + ".

");;

    var races = Data.listRaces().filter(function(r) { return r.tags.indexOf("market") != -1; });
    races.forEach(function (r) {
        ToText("" + r.name.capitalize() + "
");
    });
}, 1));

Location.push(new Locations("startstory", function() {
    this.SetImage('files/backgrounds/mansion.jpg');
    ToText("

");
    if (Game.magic_knife == 1) {
        ToText("Your knife allows you to collect life energy through destruction, although it is ill-advised to harm local people, especially considering the questionable morality of such actions. Still, it can be used to harvest mana from particularly dangerous beasts, should the need ever present itself.

");
    } else if (Game.magic_ring == 1) {
        ToText("

Your uncle has gifted you a magic ring which detects nearby sexual activity and harvests any produced mana. It also comes with the added benefit of improving your endurance, allowing you to partake in such activities for extended periods of time. ");
    }
    ToText("

You've inherited a lone mansion in the woods. It has almost everything you may need to start your own career as a mage, except one thing: servants. How you’ll obtain them has been left to you. You are not entirely without aid however, as you have access to a portal which leads to a distant lands, so you may hunt for slaves without the risk of garnering unwanted attention, or retaliation from the locals.

However, legal Branding requires you to be a part of a Mage's Order, which should be your main objective to complete, as it will also allow you to receive access to multiple valueable features.

");
    if (Game.sandbox_mode > 1) {
        Game.LearnSpell('mind_read');
        Game.mission.magequest = 10;
        Game.guildrank = 3;
        Game.alchemy_level = 1;
        Game.mission.brothel = 2;
        Game.builders_unlocked = 1;
        Game.mission.farm = 3;
        Game.learned_refined_brand = 1;
        Game.undercity_unlocked = 1;
        Game.portals = [];
        Object.forEach(Data.portal, function(k) {
            Game.portals.push(k);
        });
    }
    ToText("

To help you with settling up, you brought your old acquaintance.

");
    Game.newgirl = new Girl();
    Game.newgirl.setRace("human");
    Game.setup.starting_girl_race = 1;

    ToText("

Configure your starting girl

");
    ToText("

Start with random girl

");
    ToText("

Previous page

");
}, 1));

Location.push(new Locations("startinggirlcolors", function() {
    if (Game.tmp0 == 1) {
        ToText("

");
        var value = Game.newgirl.body.hair.color;
        if (!value && value != 0) {
            value = '';
        } else {
            value = value.toString();
        }
        var focus = '';
        if (!isTouchDevice) {
            focus = 'autofocus';
        }
        ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.newgirl.body.hair.color=Trim(this.childNodes[0].value);if(!isNaN(Game.newgirl.body.hair.color)){Game.newgirl.body.hair.color=parseFloat(Game.newgirl.body.hair.color);} PrintLocation("startinggirl");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
        ToText("

");
    } else if (Game.tmp0 == 2) {
        ToText("

");
        var value = Game.newgirl.body.eyes.color;
        if (!value && value != 0) {
            value = '';
        } else {
            value = value.toString();
        }
        var focus = '';
        if (!isTouchDevice) {
            focus = 'autofocus';
        }
        ToText("
<form action='' onsubmit='if(Trim(this.childNodes[0].value)!=""){Game.newgirl.body.eyes.color=Trim(this.childNodes[0].value);if(!isNaN(Game.newgirl.body.eyes.color)){Game.newgirl.body.eyes.color=parseFloat(Game.newgirl.body.eyes.color);} PrintLocation("startinggirl");} return false;'><input class='input_text' type='text' onclick='event.stopPropagation();' autocapitalize='off' size='10' value='" + value + "'" + focus + "><input class='input_butt' type='submit' value='✓'></form>
");
        ToText("

");
    }
}, 1));

document.addEventListener("DOMContentLoaded", function() {
    page.style.display='block';

    Game = new Game_();

    GameStart();
});


code:
"use strict";

Game_.prototype.recoverBaby = function(girl) {
    Game.tmp0 = Game.Girls.indexOf(girl);
    if (Game.tmp0 == -1)
        throw new Error("recoverBaby() called with an invalid girl");
    Game.tmp1 = -1;
    Game.tmp2 = 0;
    PrintLocation("babyrecovery");
}

Location.push(new Locations("babyrecovery", function() {
    ToText((Game.Girls[Game.tmp0].name + Game.Girls[Game.tmp0].lastname).trim() + " is having a difficult delivery.  Can you get the father in here?

");
    ToText("I am the father

");
    ToText("I don't know who the father was

");
    if (Game.tmp1 != -1) {
        var father = Game.Girls[Game.tmp1];
        ToText("It was " + (father.name + " " + father.lastname).trim() + "

");
    }
    ToText("It was one of my servants

");
    ToText("It was one of my prisoners

");
}, 1));
Location.push(new Locations("babyrecovery1", function() {
    /* Mom & Dad are both defined, let's save a baby */

    if (Game.tmp1 == -1)
        var father = Game.player;
    else if (Game.tmp1 == -2)
        var father = undefined;
    else if (Game.tmp1 < 0 || Game.tmp1 >= Game.Girls.length)
        throw new Error("Selected an impossible father.");
    else
        var father = Game.Girls[Game.tmp1];

    Game.Girls[Game.tmp0].newPregnancy(father);
    Game.Girls[Game.tmp0].body.pregnancy = 30;
    ToText("I think we've saved the baby now.

");
    ToText("Continue");
}, 1));