code:
"use strict";

var Race = function(name, tags) {
    this.name = name.toLowerCase();
    this.tags = tags;
    this.description = "FIXME";
    this.image = unixName(this.name);
    this.trait = undefined;
    this.essence = undefined;
    this.marketValue = 1;

    Data.addRace(this);
}

Race.prototype.toJSON = function() {
    return this.name;
}

Race.prototype.generateWord = function(syl1, syl2) {
    var out = "";

    var vowel = [ 'a', 'e', 'i', 'o', 'u', 'y' ];

    var cnt = Math.tossDice(1, 2);
    for (var i = 0; i < cnt; i++)
        out += syl1.random();

    if (Math.tossCoin())
        out += vowel.random();

    cnt = Math.tossDice(1, 2);
    for (var i = 0; i < cnt; i++)
        out += syl2.random();

    return out.capitalize();
}

Race.prototype.generateNickname = function(girl) {
    girl.name     = this.firstNames.random().capitalize();
    girl.lastname = "";
}

Race.prototype.generateProperName = function(girl) {
    girl.name     = this.firstNames.random().capitalize();
    girl.lastname = this.lastNames.random().capitalize();
}

Race.prototype.generateName = Race.prototype.generateNickname;

Race.prototype.firstNames = [
    'Alina', 'Alita', 'Amedea', 'Angelika', 'Ariana', 'Beatris',
    'Cecile', 'Chiara', 'Ciara', 'Cinthia', 'Dacia', 'Diana', 'Elinor',
    'Eliza', 'Eloise', 'Emmy', 'Enna', 'Erlene', 'Evelien', 'Filicia',
    'Fleurette', 'Freya', 'Genevie', 'Hanni', 'Irena', 'Isabella', 'Janay',
    'Jenni', 'Jestine', 'Julene', 'Kellye', 'Krysten', 'Leanora', 'Lera',
    'Lucilla', 'Maeva', 'Maria', 'Marylee', 'Mina', 'Nanci', 'Nelda',
    'Nerissa', 'Nila', 'Nina', 'Piper', 'Rita', 'Riya', 'Robin', 'Rosi',
    'Roterica', 'Shana', 'Sharlene', 'Shaylee', 'Shelba', 'Sonya',
    'Sophie', 'Tami', 'Vanessa', 'Vikki', 'Yoana'
];

Race.prototype.lastNames = [
    'Alexander', 'Ali', 'Brown', 'Brown', 'Campbell', 'Clarke', 'Cox',
    'Davies', 'Davis', 'Doherty', 'Graham', 'Hamilton', 'Hughes',
    'Jackson', 'James', 'Johnson', 'Johnston', 'Jones', 'Kelly', 'Khan',
    'Lewis', 'Martin', 'Mason', 'Mitchell', 'Moore', 'Murphy', 'Murray',
    'O’Neill', 'Patel', 'Phillips', 'Quinn', 'Roberts', 'Robinson',
    'Rodríguez', 'Rose', 'Smith', 'Smyth', 'Stewart', 'Taylor', 'Thomas',
    'Thompson', 'Williams', 'Wilson'
];

Race.prototype.hairColors = [
    'white', 'blonde', 'red', 'brown', 'black', 'green', 'purple', 'blue'
];

Race.prototype.eyeColors = [
    'red', 'blue', 'green', 'amber', 'hazel', 'black', 'brown', 'gray', 'purple'
];

Race.prototype.skinColors = [ 0, 1, 2, 3, 4, 5 ];

Race.prototype.generateColors = function (body) {
    body.eyes.color = this.eyeColors.random();
    body.hair.color = this.hairColors.random();
    body.skin.color = this.skinColors.random();
};

Race.prototype.generateBodyExtra = function(body) {};

Race.prototype.generateBody = function (body) {
    this.generateColors(body);
    this.generateBodyExtra(body);
}

Race.prototype.getImage = function() {
    if (Game.settings.per_race_images == 0) {
        if (this.tags.indexOf("furry") != -1)
            return "furry";
        if (this.tags.indexOf("halfkin") != -1)
            return "halfkin";
    }
    return this.image;
}

Race.prototype.nightly = function(girl) {};

var r;
var p;

r = new Race('Human', ["market"]);
r.description = "Humans are a highly successful militaristic people whose members can be found throughout much of the world, their presence often receiving a mixed reception. Slavery is a common part of human society, viewed as a civilized form of alternative punishment, with many laws and businesses based around the concept. Because of this slave driven culture, you have found that humans tend to be the most widely accessible residents, servants, and slaves.";
r.marketValue = 0.5;
r.trait = 'service training ignores stat requirements';
r.generateName = r.generateProperName;

r = new Race('Elf', ["market", "sebastian"]);
r.description = "Elves are famous among the various races for the legends and stories of ancient times, when elves held a similar status to modern humans. These tales speak of elves as arrogant, and in command of powerful nature magic, but ultimately spelling their own downfall in some great act of folly.

Modern elves bear few connections to their ancestors beyond physical appearance and a close connection to nature. In stark contrast, their lives are often fairly humble and reclusive, generally staying deep within forests. There is no evidence to suggest any inherent magic found in modern elves, but this hardly impedes their popularity.";
r.trait = 'confidence grows while in managing jobs (capped by willpower)';
r.nightly = function(girl) {
    if (girl.hasJob([ "Jailer", "Farm Manager", "Head Girl", "Lab Assistant"]) && (girl.attr.confidence + 3 < girl.attr.willpower * 15)) {
        girl.attr.confidence = Math.min(girl.attr.confidence + Math.tossDice(0, 2), girl.attr.willpower * 15);
    }
}
r.firstNames = [
    'Albis', 'Anhanna', 'Caenairra', 'Driszora',
    'Durrastra', 'Eilcahne', 'Eilkyrath', 'Eilxana', 'Enacelle',
    'Faeriele', 'Faesanna', 'Grucelle', 'Hyllyn', 'Illayana', 'Irexana',
    'Jelenriele', 'Jelenshana', 'Jelenzane', 'Kaicahne', 'Kaiqirith',
    'Kairastra', 'Menairra', 'Nairiele', 'Naitora', 'Nerixana', 'Oricelle',
    'Rigrys', 'Rolhyssa', 'Shameiv', 'Trihyssa', 'Ulkyrath', 'Ulthaea',
    'Ultrianna', 'Vacelle', 'Vameiv', 'Weswena', 'Weszora', 'Yllacelle',
    'Yllaparys', 'Zenhanna'
];
r.lastNames = [
    'Autumncrest', 'Autumnspirit', 'Blacksinger',
    'Bladeheart', 'Blademane', 'Darksky', 'Dawnwind', 'Dewbreath',
    'Dewspirit', 'Farspyre', 'Feathermane', 'Feathermoon',
    'Feathersword', 'Fogbow', 'Forestwhisper', 'Greenwater',
    'Lunarage', 'Moongrove', 'Rainsinger', 'Rapidsnow', 'Sagespear',
    'Sagespirit', 'Sealight', 'Shademane', 'Shadewind', 'Shadowblade',
    'Silverwhisper', 'Skyclouds', 'Skyswift', 'Stagbranch',
    'Stillrunner', 'Summerbloom', 'Swiftseeker', 'Thunderspear',
    'Treeleaf', 'Treespear', 'Truetree', 'Winterrage', 'Wintersinger',
    'Woodforest'
];
r.generateName = r.generateProperName;
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
}

r = new Race('Drow', ["market", "sebastian"]);
r.description = "Drow are a race, considered a branching species of elf. Little is known about their history and motherland, if they ever had one at all, as their underground cities are spread thin across multiple continents, and are even more difficult to enter than to locate.

Beyond a preference for isolation and their general appearance, drow are quite different from their cousins in both attitude and culture. However, what sets them apart most is the unusual pigmentation of their skin; a dark colors with occasional blue tint. The unusual pigmentation is the subject of much debate and speculation, with theories ranging from natural mutation, to the byproduct of ancient beings.";
r.trait = 'extra mana from sexual intercourse';
r.essence = 'magic_essence';
r.firstNames = [
    "Ahlysaaria", "Akordia", "Alaunirra",
    "Alystin", "Amalica", "Angaste", "Anluryn", "Ardulace", "Aunrae",
    "Balaena", "Baltana", "Bautha", "Belarbreena", "Beszrima",
    "Brigantyna", "Briza", "Brorna", "Burryna", "Byrtyn", "Cazna",
    "Chadra", "Chadzina", "Chalithra", "Chandara", "Chardalyn",
    "Charinida", "Charlindra", "Chenzira", "Chessintra", "Dhaunae",
    "Dilynrae", "Drada", "Drisinil", "Eclavdra", "Elerra", "Elvanshalee",
    "Elvraema", "Erakasyne", "Ereldra", "Faeryl", "Felyndiira",
    "Felyndiira", "Filfaere", "G'eldriia", "Gaussra", "Ghilanna",
    "Greyanna", "Gurina", "Haelra", "Halisstra", "Ilharess", "Ilivarrra",
    "Ilmra", "Imrae", "Jaelryn", "Jezzara", "Jhaelryna", "Jhaelrynna",
    "Jhalass", "Jhangara", "Jhanniss", "Jhulae", "Khaless", "Kiaran",
    "Laele", "Laele", "Larynda", "LiNeerlay", "Lledrith", "Llolfaen",
    "Lualyrr", "Lythrana", "Malice", "Maya", "Menzoberra", "Mez'Barris",
    "Micarlin", "Miz'ri", "Mizzrym", "Myrymma", "Narcelia", "Nathrae",
    "Nedylene", "Nendra", "Nizana", "Nulliira", "Olorae", "Pellanistra",
    "Phaere", "Phyrra", "Qilue", "Quarra", "Rauva", "Rilrae", "Sabrae",
    "Saradreza", "Sassandra", "Schezalle", "Shimyra", "ShriNeerune",
    "Shulvallriel", "Shurdriira", "Shurdriira", "Shurraenil", "Shyntlara",
    "SiNafay", "Sindyrrith", "Solenzara", "Ssapriina", "T'risstree",
    "Talabrina", "Talice", "Tallrene", "Thalra", "Thirza", "Thraele",
    "Triel", "Ulitree", "Ulviirala", "Umrae", "Urlryn", "Urmelena",
    "Vhondryl", "Viconia", "Vierna", "Vornalla", "Waerva", "Wuyondra",
    "Xalyth", "Xullrae", "Xune", "Yasrena", "Yvonnel", "Z'ress", "Zarra",
    "Zebeyana", "Zeerith", "Zelpassa", "Zendalure", "Zesstra", "Zilvra"
];
r.lastNames = [
    "A'Daragon", "Abaeir", "Abbylan", "Argith",
    "Baenre", "Beltaulur", "Blaerabban", "Blundyth", "Chaulssin",
    "Coborel", "Coloara", "Cormrael", "Daevion'lyr", "Dalael", "Dhalmass",
    "Dhunnyl", "Diliriy", "Dinoryn", "Dryaalis", "Duskryn", "Dyrr",
    "Elpragh", "Elpragh", "Faertala", "Filifar", "Gallaer", "Glannath",
    "Glaurach", "Helviiryn", "Hune", "Hunzrin", "Hyluan", "Icharyd",
    "Ilaleztice", "Illistyn", "Illykur", "Jhalavar", "Jusztiirn",
    "Keteeruae", "Khalazza", "Khalazza", "Kront'tane", "Lhalabar",
    "Lueltar", "Mizzrym", "Mlezziir", "Naerth", "Nirinath", "Olonrae",
    "Omriwin", "Philiom", "Quavein", "Rhomduil", "Rrostarr", "Seerear",
    "Ssambra", "T'orgh", "T'sarran", "Tanor'Thal", "Telenna", "Tlin'orzza",
    "Tlintarn", "Tuin", "Uloavae", "Vrammyr", "Vrinn", "Waeglossz",
    "Xiltyn", "Yauntyrr", "Yauthlo", "Yril'Lysaen", "Zaphresz", "Zauviir",
    "Zolond"
];
r.generateName = r.generateProperName;
r.skinColors = [ 5, 7 ];
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
}

r = new Race('Orc', ["market", "sebastian"]);
r.description = "Orcs are among the most recognizable races for their unique green skin color, and naturally untamed appearances. Orcs are widely considered barbarians for their tribal nature, but those who study them have found a diverse and highly communal society, far more civilized than it may first appear.

Orcs reside primarily in the south, within the borders of Gorn; a powerful nation. It is well documented in local history and folklore that as the scale and ferocity of the tribal wars grew, foreign invaders were pushed further out, allowing Gorn’s relatively undisturbed development. These days, though the constant wars have ended, the remaining tribes receive regular offerings to stay within the nation’s borders as a deterrent and to maintain friendly relations.";
r.marketValue = 0.7;
r.trait = 'health restores faster';
r.nightly = function(girl) {
    girl.healthChange(5);
}
r.skinColors = [ 8 ];
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
}

var beastkin_desc = "The term Beastkin refers to a wide range of sentient species with prominent humanoid and animalistic traits. True beastkin are fully covered in fur, feathers, or scales, with similar stature and proportions to a human. Even among individual species, there are wide differences to be observed with inherited animal traits, such as eyes, claws, and teeth. It is unknown whether Beastkin are man-made or the product of nature, as their spread and diversity often leads to inconsistent findings.

";
var halfkin_desc = "Halfkin are the offspring produced by the union of a beastkin and a human. Halfkin most prominently display their human lineage, lacking fur and other major animalistic characteristics, but do possess secondary traits from their beastkin forebears, such as claws, ears, and tails, and tend to exhibit behavior from both parents equally.

The fate of halfkin varies from place to place, some living normally as any other race, others living under persecution and prejudice, and even rumors of far off lands where they are respected and worshipped. Regardless of what kind of environment they live in, halfkin are a popular item among collectors for their exotic appearance and the unique quality that their offspring will often swing back to fully human or fully beast.

";

r = new Race('Beastkin cat', ["market", "sebastian", "furry"]);
r.description = beastkin_desc + "Cat folk are an unusually social breed of Beastkin, having no known settlements of their own, and living quite openly in populous towns and cities. They have a great deal of popularity among certain crowds for their lush appearance and lascivious nature.";
r.image = "beastcat";
r.marketValue = 1.5;
r.trait = "extra dodge chance in combat";
r.essence = 'beastial_essence';
r.generateName = function(girl) {
    var nm5 = ["","","","d","f","g","h","j","k","l","m","n","p","r","s","t","v","z"];
    var nm6 = ["a","e","i","o","u"];
    var nm7 = ["f","ff","l","ly","lh","ls","lr","lm","ln","m","my","mh","n","ny","nh","ph","phr","r","rr","ry","rh","sy","sh","sr","sl","th","ty","y"];
    var nm8 = ["","","","","","","","","","","h","s"];

    var rnd1 = nm5.random();
    var rnd2 = nm6.random();
    var rnd3 = nm7.random();
    var rnd4 = nm6.random();
    var rnd5 = nm8.random();
    var rnd6 = nm7.random();
    var rnd7 = nm8.random();

    if (Math.tossCoin(0.6))
        girl.name = rnd1 + rnd2 + rnd3 + rnd4 + rnd5;
    else
        girl.name = rnd1 + rnd2 + rnd3 + rnd4 + rnd6 + rnd7 + rnd5;

    girl.name = girl.name.capitalize();
    girl.lastname = "";
}

r.eyeColors = [ 'blue', 'green', 'amber' ];
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.cock.type = "feline";
    body.ears.type = "cat";
    body.shape.type = "beastial";
    body.skin.coverage = [ 'marble', 'gray', 'orange-white', 'black-white', 'black-gray', 'orange-black', 'white-black-spots' ].random();
    body.tail.type = "cat";
    body.eyes.pupils = "vertical";
}

r = new Race('Halfkin cat', ["market", "sebastian", "halfkin"]);
r.description = halfkin_desc;
r.image = "halfcat";
r.marketValue = 1.5;
p = Data.getRace("Beastkin cat");
r.trait = p.trait;
r.essence = p.essence;
r.generateName = p.generateName;
r.eyeColors = p.eyeColors;
r.generateBodyExtra = function(body) {
    body.ears.type = "cat";
    body.tail.type = "cat";
    if (Math.tossCoin())
        body.cock.type = "feline";
    body.eyes.pupils = "vertical";
}

r = new Race('Beastkin fox', ["market", "sebastian", "furry"]);
r.description = beastkin_desc + "Fox folk are a rare, and relatively mysterious breed of Beastkin. They display high intelligence, a tendency towards lifelong monogamy, and congregate in small, close-knit communities.";
r.image = "beastfox";
r.marketValue = 1.5;
r.trait = "allure training ignore stat requirements";
r.essence = 'beastial_essence';
r.generateName = function(girl) {
    var fn1 = ["ix", "ixix", "ox", "farim", "og", "sin", "star",
        "rai", "nin", "ith", "viv"];
    var fn2 = ["suigor", "", "grim", "tal", "ava", "avanav", "nav",
        "stav", "anstav", "grimstav", "atal", "ian"];

    var ln1 = ["bonn", "bann", "fox", "ex", "oxla", "la", "vin", "bush", "ro", "kel"];
    var ln2 = ["sly", "breath", "bredth", "plo", "sin", "wind", "kom", "tail", "trot", "kin"];

    girl.name = this.generateWord(fn1, fn2).capitalize();
    girl.lastname = this.generateWord(ln1, ln2).capitalize();
}
r.eyeColors = [ 'blue', 'green', 'amber' ];
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.cock.type = "canine";
    body.ears.type = "fox";
    body.shape.type = "beastial";
    body.skin.coverage = [ "marble", "gray", "orange-white", "black-white", "black-gray", "orange-black" ].random();
    body.tail.type = "fox";
    body.eyes.pupils = "vertical";
}

r = new Race('Halfkin fox', ["market", "sebastian", "halfkin"]);
r.description = halfkin_desc;
r.image = "halffox";
r.marketValue = 1.5;
p = Data.getRace("Beastkin fox");
r.trait = p.trait;
r.essence = p.essence;
r.generateName = p.generateName;
r.eyeColors = p.eyeColors;
r.generateBodyExtra = function(body) {
    body.ears.type = "fox";
    if (Math.tossCoin())
        body.cock.type = "canine";
    body.tail.type = "fox";
    body.eyes.pupils = "vertical";
}

r = new Race('Beastkin wolf', ["market", "sebastian", "furry"]);
r.description = beastkin_desc + "Unlike other Beastkin, Wolves are not viewed as simple novelties, but are treated as the powerful, agile pack hunters they are. Though they rarely show hostility towards outsiders unless threatened, great caution should be taken when dealing with them.

There are rumors that far to the north exists a nation richly populated by Beastkin, where wolves play a leading role, being well suited to the harsh environment.";
r.image = "beastwolf";
r.marketValue = 1.5;
r.essence = 'beastial_essence';
r.trait = "Refined Brand causes courage growth (capped by will)";
r.nightly = function(girl) {
    if (girl.body.brand.type == "refined" && (girl.attr.courage + 3 < girl.attr.willpower * 15)) {
        girl.attr.courage = Math.min(girl.attr.courage + Math.tossDice(0, 2), girl.attr.willpower * 15);
    }
}
r.generateName = r.generateProperName;
r.eyeColors = [ 'blue', 'green', 'amber' ];
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.cock.type = "canine";
    body.ears.type = "wolf";
    body.shape.type = "beastial";
    body.skin.coverage = [ "marble", "gray", "orange-white", "black-white", "black-gray" ].random();
    body.tail.type = "wolf";
}

r = new Race('Halfkin wolf', ["market", "sebastian", "halfkin"]);
r.description = halfkin_desc;
r.image = "halfwolf";
r.marketValue = 1.5;
p = Data.getRace("Beastkin wolf");
r.essence = p.essence;
r.trait = p.trait;
r.nightly = p.nightly;
r.generateName = p.generateName;
r.eyeColors = p.eyeColors;
r.generateBodyExtra = function(body) {
    body.cock.type = "canine";
    body.ears.type = "wolf";
    body.tail.type = "wolf";
}

r = new Race('Seraph', ["market", "sebastian"]);
r.description = "Seraphs were named because of their similarity in appearance to angels, the winged servants of divinity spoken of in myth. The reclusiveness of seraphs has led to a lack of research, but common theories are that they are a subspecies of harpy, or an artificial race created accidentally many generations ago.

Unlike demons, seraphs seem to exhibit many behavioral traits in line with their mythic counterparts, such as a prudish nature and a slight inclination towards public service, though it’s unknown if these traits are universal, or if they come down to an individual basis.";
r.marketValue = 2;
r.trait = "body control has no stat requirement";
r.generateName = r.generateProperName;
r.generateBodyExtra = function(body) {
    body.wings.type = "white";
}

r = new Race('Demon', ["market", "sebastian"]);
r.description = "Though they share a name and certain physical traits, modern demons bear no true resemblance to their ancient counterparts, who were driven into the great depths of the underground, and even into other dimensions. It is speculated that modern demons are either the offspring of humans and actual monsters, or as some research suggests, the byproduct of extensive magical corruption, similar to gnomes.

Though demons are often feared and reviled, those with outstanding talent or skill often receive recognition for their abilities, so it is not entirely uncommon to see demons among the elite, or in high profile positions.";
r.marketValue = 2;
r.trait = "laboratory upgrades take less resources"
r.essence = 'tainted_essence';
r.firstNames = [
    'Ardat-Lili', 'Batibat', 'Empusa', 'Hantu-Kopek', 'Hecate', 'Jezebeth', 'Kasdeya',
    'Kok-Lir', 'Lamashtu', 'Lezabel', 'Lilith', 'Lilim', 'Lilitu', 'Mara', 'Nimue',
    'Pandora', 'Prosperine', 'Qarinah'
];
r.generateBodyExtra = function(body) {
    body.ears.type = "bat";
    body.tail.type = "demon";
    body.wings.type = "demon";
    body.horns.type = BodyType.c.horns.c.type.random();
}

r = new Race('Fairy', ["market", "sebastian"]);
r.description = "Modern fairies, often referred to as city fairies by some parts of the magic community, are an unexpected evolution of nature spirits sharing the same name, resulting from interaction with outsiders and migration into cities. These fairies have retained much of the cute, friendly, and playful attitudes of the spirits they came from, making them popular for positions with high volumes of public interaction.";
r.marketValue = 1.5;
r.trait = "stress recovers twice as fast";
r.essence = 'magic_essence';
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
    body.shape.type = "petite";
    body.wings.type = "fairy";
}

r = new Race('Dryad', ["market", "sebastian"]);
r.description = "Dryads are a virtual unknown, with little information on their natural habitat and reproduction. It is speculated that they are nature spirits born from old trees that have absorbed a large amount of mana.";
r.trait = "requires half as much food, good at foraging";
r.essence = 'nature_essence';
r.hairColors = [ 'green', 'purple', 'blue' ];
r.skinColors = [ 4, 5, 8 ];
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
    if (body.hasFur()) { //FIXME: fur-and-leaves if they have fur already.  Should only happen for the plant quest.
        body.skin.coverage = "fur-leaves";
    } else {
        body.skin.coverage = "leaves";
    }
}

r = new Race('Dragonkin', ["market", "sebastian"]);
r.description = "Dragonkin are an extremely rare breed of human with draconic lineage, partially possessing the blood and certain physical traits of dragons, such as wings, scales, and a tail. While originally confined to the whims of dragons, a coalition of influential mages banded together and developed a powerful ritual to imbue grown humans with dragon blood, in spite of observed difficulties in adoption of the blood so late into development. There is a great deal of secrecy surrounding the ritual, and outside of a few involved elite, its success rate is unknown. The few dragonkin alleged to have been produced by the ritual are virtually indistinguishable from those who were naturally birthed.";
r.image = "dragon";
r.trait = "doubles chance to break from grabs";
r.essence = 'magic_essence';
r.generateName = function(girl) {
    var syl6 = ["","","","","","","","","","","b","bh","c","ch","d","g","h","kh","l","m","n","ph","phr","r","s","shr","str","sth","t","th","tr","z","zh"];
    var syl7 = ["a","e","i","o","u","a","e","i","o","u","a","e","i","o","u","a","e","i","o","u","a","e","i","o","u","a","e","i","o","u","a","e","i","o","u","ai","ae","ia","ea","ie","ei"];
    var syl8 = ["dr","dh","dn","dhr","gn","gr","ghr","gtr","gt","k","kk","kh","kt","kth","l","lk","ll","lg","ld","ldr","lgr","ln","lm","lkh","ls","lz","n","nd","ndh","ndr","ns","nsh","nz","nh","nhr","ng","ngh","r","rc","rph","rsh","rz","rl","s","sh","ss","sth","sht","shl","sn","sg","sk","th","thr","thn","tr","z","zh"];
    var syl9 = ["l","m","n","r","s","sh","t","th","x","z"];
    var syl10 = ["","","","","","","","","","","h","s","sh","th","x","z"];

    var rnd1 = syl6.random();
    var rnd2 = syl7.random();
    var rnd3 = syl8.random();
    var rnd4 = syl7.random();
    var rnd5 = syl10.random();

    while(rnd3 == rnd1 || rnd3 == rnd5)
        rnd3 = syl8.random();

    var out = "";

    if (Math.tossCoin(0.6)) {
        out = rnd1 + rnd2 + rnd3 + rnd4 + rnd5;
    } else {
        var rnd6 = syl9.random();
        var rnd7 = syl7.random();

        while (rnd6 == rnd3 || rnd6 == rnd5)
            rnd6 = syl9.random();

        if (Math.tossCoin())
            out = rnd1 + rnd2 + rnd3 + rnd4 + rnd6 + rnd7 + rnd5;
        else
            out = rnd1 + rnd2 + rnd6 + rnd7 + rnd3 + rnd4 + rnd5;
    }
    girl.name = out.capitalize();
    girl.lastname = "";
}
r.eyeColors = [ 'red', 'amber', 'black' ];
r.generateBodyExtra = function(body) {
    body.ears.type = [ 'elf', 'bat' ].random();
    body.horns.type = BodyType.c.horns.c.type.random();
    body.skin.coverage = "scales";
    body.tail.type = "dragon";
    body.wings.type = "dragon";
    body.eyes.pupils = "vertical";
}

r = new Race('Gnome', ["market", "sebastian"]);
r.description = "Though nowadays the gnome capital is open as a tourist spot, there are stories and documents stating that generations ago, they were once humans sealed within the city by demons, experimented on and mutated into the forms they have today.

Though they are a stocky, physically weak race, they compensate for their shortcomings with outstanding intellect, holding a commanding lead as the world’s frontrunners in technology.";
r.trait = "maximum skill increased";
r.generateName = function(girl) {
    var nm3 = ["Alu","Ari","Ban","Bree","Car","Cel","Daphi","Do","Eili","El","Fae","Fen","Fol","Gal","Gren","Hel","Hes","Ina","Iso","Jel","Jo","Klo","Kri","Lil","Lori","Min","My","Ni","Ny","Oda","Or","Phi","Pri","Qi","Que","Re","Rosi","Sa","Sel","Spi","Ta","Tifa","Tri","Ufe","Uri","Ven","Vo","Wel","Wro","Xa","Xyro","Ylo","Yo","Zani","Zin"];
    var nm4 = ["bi","bys","celi","ci","dira","dysa","fi","fyx","gani","gyra","hana","hani","kasys","kini","la","li","lin","lys","mila","miphi","myn","myra","na","niana","noa","nove","phina","pine","qaryn","qys","rhana","roe","sany","ssa","sys","tina","tra","wyn","wyse","xi","xis","yaris","yore","za","zyre"];

    girl.name = nm3.random() + nm4.random().capitalize();
    girl.lastname = '';
}
r.generateBodyExtra = function(body) {
    body.shape.type = "petite";
}

r = new Race('Taurus', ["sebastian"]);
r.description = "Tauruses are a purely artificial race, created on the orders of a group of noblemen looking for bodyguards. The experiment seems to have been an attempt at recreating the size and strength of orcs and beastkin in a more easily controlled package, but was ultimately considered only partially successful, as specimens of the new species had a tendency of taking too many bovine behavioral traits, becoming too passive, or in some cases, too aggressive.

Still, they, especially the females, remain popular among certain individuals for their appearance and outstanding natural lactation.";
r.image = "cow";
r.trait = "milk production increased"
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.horns.type = BodyType.c.horns.c.type.random();
    body.skin.coverage = [ 'black-white', 'black-gray', 'white-black-spots' ].random();
    body.tail.type = "scruffy";
    body.eyes.pupils = "horizontal";
}

r = new Race('Goblin', ["sebastian"]);
r.description = "Goblins are a race of cave dwellers, loosely resembling short, green-skinned elves. They have existed for a very long time, but despite their prevalence rarely played any influential role. While often been considered nothing more than common monsters, they have surprising skill and understanding, putting them on par with many humanoid races in term of sentience.";
r.trait = "pregnancy duration reduced";
r.marketValue = 0.7;
r.skinColors = [ 5, 8 ];
r.generateBodyExtra = function(body) {
    body.shape.type = "petite";
}

r = new Race('Slime', []);
r.description = "Slimes are the result of a relatively common magical mutation, which dramatically changes a person's bodily integrity. Slimes are heavily reliant on water, even though their body is not actually 'slimy'. They can freely ooze over anything they touch, yet are in fact, pretty cohesive and firm to the touch.

Unlike most other ooze-type slime monsters, slimes of humanoid origins are capable of nearly the same mental processes, although they are generally ill-regarded for having a clearly monstrous appearance.";
r.trait = "can't be modified in the lab, no magical toxicity accumulation";
r.essence = 'fluid_substance';
r.skinColors = [ 6, 7, 8 ];
r.generateBodyExtra = function(body) {
    body.hair.color += ' jelly';
    body.shape.type = "jelly";
}

r = new Race('Harpy', []);
r.description = "Harpies are human-bird hybrids with easily recognisable features, such as their feathered arms and avian lower quarters. Commonly seen as monsters, they have existed since time immemorial. Wild harpies generally inhabit mountain regions and are relatively aggressive. Their intelligence has a wide range, and people have had some success making them into slave-pets.";
r.trait = "higher production on egg farm assignment";
r.essence = 'beastial_essence';
r.generateBodyExtra = function(body) {
    body.tail.type = "bird";
    body.shape.type = "avian";
}

r = new Race('Lamia', []);
r.description = "Lamias are easily recognized and tend to be timid in their interactions, rarely showing themselves to majority of the population. Their population has been severely reduced by hunting and extermination expeditions launched by different races. Lamias are surprisingly intelligent, as the few captured and studied samples have shown.";
r.trait = "naturally long tongue";
r.essence = 'tainted_essence';
r.eyeColors = [ 'blue', 'green', 'amber' ];
r.generateBodyExtra = function(body) {
    body.ears.type = "elf";
    body.shape.type = "lamia";
    body.tail.type = "snake";
    body.eyes.pupils = "vertical";
    body.tongue.type = "split";
    body.tongue.size = 200;
}

r = new Race('Arachna', []);
r.description = "Arachna live in isolation and tend to choose caves and other underground locations as their homes. While not being especially aggressive, they are fearsome hunters and have been reported for rare night attacks on both cattle and humans.";
r.trait = "very effective on hunter";
r.essence = 'tainted_essence';
r.generateBodyExtra = function(body) {
    body.shape.type = "spider";
    body.tail.type = "spider";
}

r = new Race('Centaur', []);
r.description = "The Centaur race is somewhat distant, yet not unheard of in southern regions. Some individuals have made it very far by adopting a nomadic lifestyle, making the race common enough to be recognized by most. The centauri population is relatively small, due to dealing with territorial oppression from humanoid races.";
r.trait = "when accompanies you, energy is not consumed on relocations";
r.essence = 'beastial_essence';
r.generateBodyExtra = function(body) {
    body.cock.type = "equine";
    body.shape.type = "centaur";
    body.skin.coverage = [ 'gray', 'orange-white', 'black-white', 'black-gray', 'orange-black', 'white-black-spots' ].random();
    body.tail.type = "horse";
}

r = new Race('Bunny', ["furry", "sebastian"]);
r.description = beastkin_desc + "Bunnies are one of the least self-sufficient, but netherless common beast races. They are not aggressive and can actually be quite timid. They are quite well liked  due to their comforting appearance and their natural lewdness makes them a popular choice for slave pets.";
r.image = "beastbunny";
r.marketValue = 1.5;
r.trait = "no stress on prostitution and fucktoy assignment";
r.essence = 'beastial_essence';
r.generateName = function(girl) {
    var fn1 = ["eli", "les", "sae", "tae", "torra", "qui", "glim", "zeira", "saeta", "glin", "myra", "reira"];
    var fn2 = ["rith", "quin", "qui", "lith", "ilim", "lim", "urim", "ysse", "sith", "yena", "sev"];

    var ln1 = ["meal", "grif", "orni", "horni", "nee", "long", "red", "pen", "hand"];
    var ln2 = ["glow", "tail", "lest", "wood", "flow", "san", "que", "qain", "quin"];

    girl.name = this.generateWord(fn1, fn2).capitalize();
    girl.lastname = this.generateWord(ln1, ln2).capitalize();
}
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.ears.type = "bunny";
    body.shape.type = "beastial";
    body.skin.coverage = [ 'marble', 'gray'].random();
    body.tail.type = "bunny";
}

r = new Race('Halfkin bunny', ["sebastian", "halfkin"]);
r.description = halfkin_desc;
r.image = "halfbunny";
r.marketValue = 1.5;
p = Data.getRace("Bunny");
r.trait = p.trait;
r.essence = p.essence;
r.generateName = p.generateName;
r.generateBodyExtra = function(body) {
    body.ears.type = "bunny";
    body.tail.type = "bunny";
}

r = new Race('Nereid', []);
r.description = "Nereid are considered to be another subspecies of the humanoid races, yet they likely split from another race in the distant past. They adapted to an aquatic lifestyle. Nereids are often seen by sailors and fishermen, but they tend to be hesitant in making contact with humans, likely viewing them as a threat.";
r.trait = "skilled entertainer";
r.essence = 'fluid_substance';
r.skinColors = [ 6, 8 ];
r.generateBodyExtra = function(body) {
    body.ears.type = "fins";
    body.shape.type = "finned";
    body.skin.coverage = "scales";
    body.tail.type = "fish";
}

r = new Race('Scylla', []);
r.description = "Scylla are rather unusual in appearance, possessing a number of tentacle-like appendages they use in the place of legs. They generally prefer damp and aquatic regions. In general, their behavior and capabilities are not much different from lamia. Their appearance is extremely rare, to the point of being treated as mere myth or drunken fancy by some.";
r.trait = "toxicity does not cause mutations";
r.essence = 'fluid_substance';
r.generateBodyExtra = function(body) {
    body.tail.type = "tentacles";
    body.shape.type = "tentacles";
}

r = new Race('Tanuki', ["furry", "sebastian"]);
r.description = "The Tanuki are a rare beast race possessing raccoon features. It's hard to pinpoint any specific mental differences between them and the majority of the humanoid races. Some say that their behavior and attitudes are much like that of the average human. There's still much  speculation as to how they originated.";
r.image = "beasttanuki";
r.marketValue = 1.5;
r.trait = "increases income from store assignment."
r.essence = 'beastial_essence';
r.generateBodyExtra = function(body) {
    body.chest.pairs = 3;
    body.cock.type = "canine";
    body.ears.type = "raccoon";
    body.shape.type = "beastial";
    body.skin.coverage = "black-gray";
    body.tail.type = "raccoon";
}

r = new Race('Halfkin tanuki', ["sebastian", "halfkin"]);
r.description = halfkin_desc;
r.image = "halftanuki";
r.marketValue = 1.5;
p = Data.getRace("Tanuki");
r.trait = p.trait;
r.essence = p.essence;
r.generateName = p.generateName;
r.generateBodyExtra = function(body) {
    body.ears.type = "raccoon";
    body.tail.type = "raccoon";
    if (Math.tossCoin())
        body.cock.type = "canine";
}


code:
"use strict";

var OldData = {};

OldData.job_name = ['Rest', 'Forage', 'Hunt', 'Library', 'Chef', 'Nurse', 'Prostitution', 'Escort', 'Fucktoy', 'Store', 'Order Assistant', 'Public Entertainer'];
OldData.job_name[100] = 'Companion';
OldData.job_name[25] = 'Jailer';
OldData.job_name[26] = 'Lab Assistant';
OldData.job_name[27] = 'Head Girl';
OldData.job_name[29] = 'Farm Manager';
OldData.job_name[30] = 'Farm Cow';
OldData.job_name[31] = 'Farm Hen';
OldData.job_name[36] = 'Apiary';

OldData.job_name[-1] = '';

OldData.race_names = ['human', 'elf', 'drow', 'orc', 'beastkin cat', 'beastkin fox', 'beastkin wolf', 'seraph', 'demon', 'fairy', 'dryad', 'dragonkin', 'gnome', 'taurus', 'goblin', 'slime', 'harpy', 'lamia', 'arachna', 'centaur', 'bunny', 'nereid', 'scylla', 'tanuki'];
OldData.race_names[101] = 'halfkin cat';
OldData.race_names[102] = 'halfkin fox';
OldData.race_names[103] = 'halfkin wolf';
OldData.race_names[104] = 'halfkin bunny';
OldData.race_names[105] = 'halfkin tanuki';

OldData.version = 11;

/* Don't fill in anything.  This is only ever used here, to create structures that can be
 * updated then imported by actual Girl(obj); */

var GirlStub = function() {
    this.attr = {};
    this.skill = {};
    this.body = {};
}

var b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

function base64encode(str) {
    str = unescape(encodeURIComponent(str));
    var b64encoded = '';
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    for (var i = 0; i < str.length;) {
        chr1 = str.charCodeAt(i++);
        chr2 = str.charCodeAt(i++);
        chr3 = str.charCodeAt(i++);
        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = isNaN(chr2) ? 64 : (((chr2 & 15) << 2) | (chr3 >> 6));
        enc4 = isNaN(chr3) ? 64 : (chr3 & 63);
        b64encoded += b64chars.charAt(enc1) + b64chars.charAt(enc2) + b64chars.charAt(enc3) + b64chars.charAt(enc4);
    }
    return b64encoded;
}

function base64decode(str) {
    var b64decoded = '';
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    str = str.replace(/[^a-z0-9+/=]/gi, '');
    for (var i = 0; i < str.length;) {
        enc1 = b64chars.indexOf(str.charAt(i++));
        enc2 = b64chars.indexOf(str.charAt(i++));
        enc3 = b64chars.indexOf(str.charAt(i++));
        enc4 = b64chars.indexOf(str.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        b64decoded = b64decoded + String.fromCharCode(chr1);
        if (enc3 < 64) {
            b64decoded += String.fromCharCode(chr2);
        }
        if (enc4 < 64) {
            b64decoded += String.fromCharCode(chr3);
        }
    }
    return decodeURIComponent(escape(b64decoded));
}

function urlencode(str) {
    str = (str + '').toString();
    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
var filename = Trim(window.location.pathname.substring(window.location.pathname.lastIndexOf('/') + 1));
if (!filename) {
    filename = 'test';
}
var data_spl = '\t';
var data_splArr = String.fromCharCode(11);

function getXmlHttp() {
    var xmlhttp;
    try {
        xmlhttp = new ActiveXObject('Msxml2.XMLHTTP');
    } catch (e) {
        try {
            xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
        } catch (E) {
            xmlhttp = false;
        }
    }
    if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
        xmlhttp = new XMLHttpRequest();
    }
    return xmlhttp;
}

function getServerData(url) {
    var xmlhttp = getXmlHttp();
    xmlhttp.open('GET', url, false);
    xmlhttp.send(null);
    return xmlhttp.responseText;
}

function putServerData(url, data) {
    var xmlhttp = getXmlHttp();
    xmlhttp.open('POST', url, false);
    xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    xmlhttp.send(data);
    if (xmlhttp.status == 200) {
        return xmlhttp.responseText;
    } else {
        return '';
    }
}

function SavedList(save) {
    var save_slots = 6;
    var savesList = [];
    var item;
    console.log(filename);
    for (var i = 1; i <= save_slots; i++) {
        item = localStorage.getItem(i + '_' + filename);
        if (item) {
            savesList.push('1');
        } else {
            savesList.push('');
        }
    }
    var com = '';
    if (save) {
        com = 'Game.SaveStory';
    } else {
        com = 'Game.LoadStory';
    }
    var str = '';
    for (var i = 1; i <= save_slots; i++) {
        if (savesList[i - 1]) {
            if (save) {
                var savep = 'Overwrite';
            } else {
                var savep = 'Restore';
            }
            str += '
' + savep + '
';
        } else {
            if (save) {
                str += '
Save
';
            } else {
                str += '
Empty
';
            }
        }
    }
    return str;
}

Game_.prototype.SaveLoadWind = function(save) {
    var inner = SavedList(save);
    if (inner) {
        prefsDiv.innerHTML = inner;
        prefsDiv.style.display = 'block';
        Animate(prefsDiv, 'effect_fade_fast');
    }
}

function DumpGameToStoryString(raw) {
    Game.check_new_props();
    var data = {}
    data.version = OldData.version;
    data.game = Game;
    data.title = CurLocation.Title;
    data.prev = PrevLocation.Title;
    data.fontsize = fontSize;
    data.animation_enable = animation_enable;
    data.audio_enable = audio_enable;
    data.musicAudio  = musicAudio.src;
    data.printImage = printImage.src;
    data.spritesDiv = spritesDiv.innerHTML;
    data.menuDiv = menuDiv.innerHTML;
    data.printTitle = printTitle.innerHTML;
    data.events = Events;
    data.timetofade = TimeToFade;
    data.Sys_gender = asmSys_gender;
    data.Sys_choice = asmSys_choice;

    if (raw)
        return "JSON" + JSON.stringify(data, null, 4);

    var serialized = JSON.stringify(data);
    var compressed = pako.deflate(serialized, { to: 'string' });

    return 'ZSAVE' + base64encode(compressed);
}

function SaveStoryLocal(data, slot_num) {
    var savedOk = false;
    localStorage.setItem(slot_num + '_' + filename, data);
    if (localStorage.getItem(slot_num + '_' + filename) == data) {
        savedOk = true;
    }
    return savedOk;
}

Game_.prototype.SaveStory = function(slot_num) {
    if (slot_num === undefined) {
        slot_num = 1;
    }
    var data = DumpGameToStoryString();
    var status = SaveStoryLocal(data, slot_num);
    if (status) {
        AlertMessage('State Stored');
    } else {
        AlertMessage('Failed save!');
    }
}

function LoadStoryLocal(slot_num) {
    return localStorage.getItem(slot_num + '_' + filename);
}

function UpdateSave(data) {
    if (data.version === undefined)
        data.version = 0;

    if (data.version > OldData.version)
        throw Fail("Don't know how to import v" + data.version.toString() +
                   " save. My saves are v" + OldData.version.toString() +
                   ". Did you come from the future?");

    console.log("Importing a v" + data.version + " save.");

    /* sanity check */
    if (!isArray(data.game.Girls)) {
        console.log("data.game.Girls not stored as an array");
        var arr = Object.keys(data.game.Girls).map(function(k) {
            return data.game.Girls[k] });
        data.game.Girls = arr;
        console.log("New array is: " + arr.length);
    }

    if (data.version < 1) {
        console.log("Updating save to v1");
        if (data.game.newgirl !== undefined) {
            data.game.newgirl.combat_health = data.game.asm.var170;
            data.game.newgirl.combat_name = data.game.asm.var171;
            data.game.newgirl.combat_procs = data.game.asm.var211;
            if (data.game.asm.var55 !== undefined) // fixup for some in-between saves.
                data.game.newgirl.attr.willpower = data.game.asm.var55;
        }
        data.game.zone_travelled = data.game.tmp3;
        data.game.gold_change = data.game.asm.var50;
        data.game.mission.street_urchin = data.game.asm.var76;
        delete data.game.asm.var128; // obed images
        data.game.mission.brothel = data.game.asm.var130;
        data.game.alchemy_level = data.game.asm.var131;
        data.game.tojail_or_race_description = data.game.asm.var144;
        data.game.magic_ring = data.game.asm.var155;
        data.game.magic_knife = data.game.asm.var158;
        data.game.combat = {};
        data.game.combat.held = data.game.asm.var165;
        delete data.game.asm.var172; // health images
        data.game.mission.magequest = data.game.asm.var173;
        delete data.game.asm.var175; // stress images
        data.game.jail_capacity = data.game.asm.var209;
        data.game.max_residents = data.game.asm.var223;
        data.game.last_location = data.game.asm.var234;
        data.game.zone_size = data.game.asm.var267;
    }
    if (data.version < 2) {
        console.log("Updating save to v2");
        if (isArray(data.game.asm.var15))
            data.game.hide_tutorials = data.game.asm.var15;
        for (var i = 0; i < data.game.Girls.length; i++) {
            var girl = data.game.Girls[i];
            if ('augmentations' in girl) {
                girl.body.augmentations = girl.augmentations;
                delete girl.augmentations;
            }
        }
    }
    if (data.version < 3) {
        console.log("Updating save to v3");
        // move the player stuff where it belongs.
        var player=data.game.player;
        player.body = {}
        var keys = [ 'ass', 'balls', 'cocksize', 'cockstyle', 'ears',
            'eyecolor', 'face', 'hair_color', 'hair_length', 'hair_style',
            'horns', 'lactation', 'piercings', 'shape', 'skin_coverage',
            'skincolor', 'tail', 'taken_virginity', 'tits', 'wings',
            'augmentations', 'gender', ];
        for(var i = 0; i < keys.length; i++) {
            if (player[keys[i]] !== undefined) {
                player.body[keys[i]] = player[keys[i]];
                delete player[keys[i]];
            }
        }

        if (player.extra_description === undefined)
            player.extra_description = 0;

        player.body.ass = player.butt;
        delete player.butt;

        player.body.skin_coverage = player.skincovering;
        delete player.skincovering;

        player.body.lactation = player.lactating;
        delete player.lactating;

        player.body.cocksize = player.cock;
        delete player.cock;

        /* skin -> skincolor */
        for (var i = 0; i<data.game.Girls.length; i++) {
            var girl = data.game.Girls[i];
            girl.body.skincolor = girl.body.skin;
            delete girl.body.skin;
        }
        player.age++; // add youth in.
        if (player.body.gender == 0)
            player.body.gender = 2;

        player.name = data.game.firstname;
        player.lastname = data.game.lastname;
        player.body.eyecolor = data.game.eyecolor;
        player.body.hair_color = data.game.haircolor;
        delete data.game.firstname;
        delete data.game.lastname;
        delete data.game.eyecolor;
        delete data.game.haircolor;
    }
    if (data.version < 4) {
        console.log("Updating save to v4");
        delete data.game.asm.var157; // !orgasm is entirely function local.

        if (data.game.asm.var142 !== undefined) {
            data.game.my_bed_capacity = data.game.asm.var142;
            delete data.game.asm.var142;
        }
        data.game.baby = [];
        if (!Array.isArray(data.game.asm.var121)) {
            console.log("baby ID is not stored as an array, fixing");
            var arr = Object.keys(data.game.asm.var121).map(function(k) {
                return data.game.asm.var121[k]; });
            data.game.asm.var121 = arr;
            console.log("New array is: " + arr.length);
        }
        if (data.game.asm.var121 !== undefined) {
            // baby conversion
            for (var i = 0; i < data.game.asm.var121.length; i++) {
                var baby = new GirlStub();

                baby.id = data.game.asm.var121[i];
                if (!Number.isInteger(baby.id))
                    continue;
                baby.race = data.game.asm.var168[i];
                baby.age = 0;

                /* augmentations may not be saved properly */
                if (data.game.asm.var134[i] !== undefined)
                    baby.body.augmentations = data.game.asm.var134[i];

                baby.body.ears = data.game.asm.var146[i];
                // 146 may not exist on import, it was never exported by the original code.
                if (baby.body.ears === undefined)
                    baby.body.ears = 0;
                baby.sex = data.game.asm.var150[i];
                baby.body.eyecolor = data.game.asm.var151[i];
                baby.body.tail = data.game.asm.var159[i];
                baby.body.horns = data.game.asm.var162[i];
                baby.body.face = data.game.asm.var167[i];
                baby.body.skincolor = data.game.asm.var181[i];
                baby.body.wings = data.game.asm.var183[i];
                baby.body.hair_color = data.game.asm.var202[i];
                baby.mother = data.game.asm.var214[i];
                baby.father = data.game.asm.var215[i];
                baby.body.skin_coverage = data.game.asm.var227[i];
                baby.body.shape = data.game.asm.var278[i];

                data.game.baby.push(baby);
            }
            delete data.game.asm.var121;
            delete data.game.asm.var134;
            delete data.game.asm.var146;
            delete data.game.asm.var150;
            delete data.game.asm.var151;
            delete data.game.asm.var159;
            delete data.game.asm.var162;
            delete data.game.asm.var167;
            delete data.game.asm.var168;
            delete data.game.asm.var181;
            delete data.game.asm.var183;
            delete data.game.asm.var202;
            delete data.game.asm.var214;
            delete data.game.asm.var215;
            delete data.game.asm.var227;
            delete data.game.asm.var278;
        }
        if (data.game.asm.var316 !== undefined) { // convert over the rest
            data.game.companion_sexact = data.game.asm.var198;
            data.game.mission.slaveguild = data.game.asm.var232;
            data.game.headgirl_orders = data.game.asm.var312;
            data.game.sebastian_request = data.game.asm.var316;

            data.game.restock = {};
            data.game.restock.slaveguild = data.game.asm.var187[0];
            data.game.restock.sebastian = data.game.asm.var187[1];

            data.game.revert = {};
            data.game.revert.food = data.game.asm.var191;
            data.game.revert.mana = data.game.asm.var197;
            data.game.revert.gold = data.game.asm.var217;

            data.game.days_elapsed = data.game.asm.var6;
            data.game.dates_available = data.game.asm.var30;
            data.game.total_snails = data.game.asm.var52;

            if (data.game.combat !== undefined)
                data.game.combat.nextround = data.game.asm.var65;

            /* drop var92 "torture publically" */

            /* tmp variables */
            data.game.tmp0 = data.game.asm.var0;
            data.game.tmp1 = data.game.selected;
            data.game.tmp2 = data.game.asm.var2;
            data.game.tmp3 = data.game.asm.var13;
            data.game.tmp5 = data.game.asm.var5;

            data.game.settings.combat_graph_length = data.game.asm.var122;

            data.game.setup = undefined;  // we can no longer import saves mid-setup.
        }
    }
    var fixgirls = function(func) {
        func(data.game.player, "player");
        if (data.game.newgirl != undefined)
            func(data.game.newgirl, "newgirl");
        for(var i = 0; i < data.game.Girls.length; i++)
            func(data.game.Girls[i], "girl");
        for(var i = 0; i < data.game.baby.length; i++)
            func(data.game.baby[i], "baby");
    }
    if (data.version < 5) {
        console.log("Updating save to v5");
        var fixjobraces = function(girl) {
            if (girl.job !== undefined)
                girl.job = OldData.job_name[girl.job];
            else
                girl.job = "Rest";

            if (girl.race !== undefined)
                girl.race = OldData.race_names[girl.race];
        }
        fixgirls(fixjobraces);
    }
    if (data.version < 6) {
        console.log("Updating save to v6");
        data.game.player.job = "Rest";

        if (data.game.sebastian_request == 0)
            data.game.sebastian_request = undefined;
        else
            data.game.sebastian_request = OldData.race_names[data.game.sebastian_request];

        if (data.game.requested == 0)
            data.game.requested = undefined;
        else
            data.game.requested = OldData.race_names[data.game.requested];
    }
    if (data.version < 7) {
        console.log("Updating save to v7");
        var fixsex = function(girl) {
            if (girl.sex !== undefined)
                girl.body.gender = girl.sex;
            delete girl.sex;
        }
        fixgirls(fixsex);
    }
    if (data.version < 8) {
        console.log("Updating save to v8");

        var fixbody = function(girl) {
            if (girl.race !== undefined)
                girl.body.race = girl.race;
            delete girl.race;

            if (girl.age !== undefined)
                girl.body.age = girl.age;
            delete girl.age;

            if (girl.brand !== undefined)
                girl.body.brand = girl.brand;
            delete girl.brand;

            if (girl.clothing !== undefined)
                girl.body.clothing = girl.clothing;
            delete girl.clothing;

            if (girl.contraception !== undefined)
                girl.body.contraception = girl.contraception;
            delete girl.contraception;

            if (girl.pregnancy !== undefined)
                girl.body.pregnancy = girl.pregnancy;
            delete girl.pregnancy;

            if (girl.pregnancyID !== undefined && girl.pregnancyID != 0 && girl.pregnancyID != -1)
                girl.body.pregnancyID = [ girl.pregnancyID ];
            else
                girl.body.pregnancyID = [];
            delete girl.pregnancyID;

            if (girl.toxicity !== undefined)
                girl.body.toxicity = girl.toxicity;
            delete girl.toxicity;
        }
        fixgirls(fixbody);
    }
    if (data.version < 9) {
        console.log("Updating save to v9");

        var fixbody = function(girl, who) {
            var body = girl.body;
            var t;

            // add any missing values
            if (body.brand === undefined)
                body.brand = 0;

            if (body.shape === undefined)
                body.shape = 0;

            if (body.ears === undefined)
                body.ears = 0;

            if (body.wings === undefined)
                body.wings = 0;

            if (body.hair_style === undefined)
                body.hair_style = 0;

            if (body.toxicity === undefined)
                body.toxicity = 0;

            if (body.contraception === undefined)
                body.contraception = 0;

            if (body.lactation === undefined)
                body.lactation = 0;

            if (body.pregnancy === undefined)
                body.pregnancy = 0;
            if (body.pregnancyID === undefined)
                body.pregnancyID = [];

            if (body.clothing === undefined)
                body.clothing = 1;

            if (body.piercings === undefined || body.piercings == 0)
                body.piercings = 1111111111;

            if (body.augmentations === undefined || body.augmentations == 0)
                body.augmentations = 1111111111;

            // update for new Body

            // sometimes the old values are strings instead of integers,
            // we fix that here too
            var sub = function (name, prop) {
                if (prop === undefined)
                    prop = "type";

                t = body[name];
                body[name] = {};
                body[name][prop]= parseInt(t);
            }

            body.gender = parseInt(body.gender);
            body.contraception = parseInt(body.contraception);
            body.lactation = parseInt(body.lactation);
            body.toxicity = parseInt(body.toxicity);

            sub("brand");
            sub("shape");

            body.skin = {};
            body.skin.color = parseInt(body.skincolor);
            delete body.skincolor;
            body.skin.coverage = parseInt(body.skin_coverage);
            delete body.skin_coverage;

            sub("horns");
            sub("wings");
            sub("tail");

            sub("face", "beauty");

            body.eyes = {};
            body.eyes.color = body.eyecolor;
            delete body.eyecolor;

            sub("ears");

            body.hair = {};
            body.hair.color = body.hair_color;
            delete body.hair_color;
            body.hair.length = parseInt(body.hair_length);
            delete body.hair_length;
            body.hair.style = parseInt(body.hair_style);
            delete body.hair_style;

            body.chest = {};

            var tits = body.tits.toString();
            t = parseInt(tits.charAt(0));
            if (t > 5)
                body.chest.size = 0;
            else
                body.chest.size = t;

            t = parseInt(tits.charAt(1));
            if (t.toString() == "NaN")
                body.chest.pairs = 0;
            else
                body.chest.pairs = t;

            t = parseInt(tits.charAt(2));
            if (t == 1)
                body.chest.ripe = true;
            else
                body.chest.ripe = false;
            delete body.tits;

            sub("ass", "size");
            sub("balls", "size");

            body.cock = {};
            body.cock.type = parseInt(body.cockstyle);
            delete body.cockstyle;
            body.cock.size = parseInt(body.cocksize);
            delete body.cocksize;

            body.pussy = {};
            if (who == "player") {
                body.pussy.size = 0;
                body.pussy.virgin = true;
            } else {
                body.pussy.size = 1;
                body.pussy.virgin = (body.taken_virginity == 1) ? false : true;
            }
            delete body.taken_virginity;
        };
        fixgirls(fixbody);
    }
    if (data.version < 10) {
        console.log("Updating save to v10");
        // Fix stuff
        var fixbody = function(girl, who) {
            var body = girl.body;

            if (who == "player") {
                if (girl.id != 1)
                    girl.id = 1;
                if (girl.father != -1)
                    girl.father = -1;
                if (girl.mother != -2)
                    girl.mother = -2;
            }

            if (body.cock.type > 3)
                body.cock.type = 0;

            if (!Number.isInteger(body.face.beauty))
                body.face.beauty = 50;

            if (!Number.isInteger(body.hair.length))
                body.hair.length = 0;
        }
        fixgirls(fixbody);
    }
    if (data.version < 11) {
        console.log("Updating save to v11");
        var hasAugmentation = function(body, name) {
            switch(name) {
            case 'pupils':
                return body.augmentations.toString().charAt(0) == 2;
            case 'nightvision':
                return body.augmentations.toString().charAt(1) == 2;
            case 'hearing':
                return body.augmentations.toString().charAt(2) == 2;
            case 'fur':
                return body.augmentations.toString().charAt(3) == 2;
            case 'scales':
                return body.augmentations.toString().charAt(3) == 3;
            case 'skin':
                return body.augmentations.toString().charAt(3) >= 2;
            case 'tits':
                return body.augmentations.toString().charAt(4) == 2;
            case 'tongue':
                return body.augmentations.toString().charAt(5) == 2;
            default:
                throw Fail("Unknown augmentation", name);
            }
        };

        var fixbody = function(girl, who) {
            var body = girl.body;

            var eyes = body.eyes;
            eyes.type = "simple";
            eyes.size = 100;

            eyes.pupils = "round";
            if (hasAugmentation(body, "pupils"))
                eyes.pupils = "vertical";

            eyes.reflectors = false;
            if (hasAugmentation(body, "nightvision"))
                eyes.reflectors = true;

            eyes.pairs = 1;
            eyes.acuity = 100;

            var ears = body.ears;
            ears.size = 100;
            ears.acuity = 100;
            if (hasAugmentation(body, "hearing"))
                ears.acuity = 200;

            body.tongue = {};
            body.tongue.type = "normal";
            body.tongue.size = 100;
            if (hasAugmentation(body, "tongue"))
                body.tongue.size = 200;

            body.chest.nipples = {};
            body.chest.nipples.elastic = false;
            if (hasAugmentation(body, "tits"))
                body.chest.nipples.elastic = true;

            // FIX stuff
            if (body.race == "beastkin cat" || body.race == "halfkin cat")
                eyes.pupils = "vertical";

            if (body.race == "beastkin wolf" || body.race == "halfkin wolf")
                eyes.pupils = "round";

            if (body.race == "beastkin fox" || body.race == "halfkin fox")
                eyes.pupils = "vertical";

            if (body.race == "taurus")
                eyes.pupils = "horizontal";

            if (body.race == "lamia")
                body.tongue.type = "split";
        }
        fixgirls(fixbody);
    }
    data.version = OldData.version;

    /* Fix some broken things here */
    if (!Array.isArray(data.game.portals)) {
        console.log("Portal data is corrupted, reconstructing");
        var _map = [ 'grove', 'highlands', undefined, 'undercity' ];

        var old = data.game.portals;

        data.game.portals = [];

        var keys = Object.keys(old);
        for (var i = 0; i < keys.length; i++) {
            if (old[i] == "1" && _map[i] !== undefined)
                data.game.portals.push(_map[i]);
            if (old[i] == "0")
                continue;
            data.game.portals.push(old[i]);
        }
    }
    var idx = data.game.portals.indexOf('highlands');
    if (idx != -1) {
        console.log("Moving highlands portal to meadows");
        data.game.portals.splice(idx, 1);
        if (data.game.portals.indexOf('meadows') == -1)
            data.game.portals.push('meadows');
    }
    // some spell lists may be broken, fix them here.
    for(var i in data.game.spells_known) {
        if (!Data.Spell.hasOwnProperty(unixName(i))) {
            console.log("Removing unknown spell " + i);
            delete data.game.spells_known[i];
        }
    }
}

/* At this point, this should never be touched again.  LoadFromData
 * will see a v3 save, and do any updates needed. */

function ParseOldSave(rawdata) {
    var game = {};

    rawdata = (rawdata + '').split('-');

    var tmp_savedata1 = '';
    for (var i = 0; i < rawdata.length; i++) {
        tmp_savedata1 = tmp_savedata1 + String.fromCharCode(rawdata[i]);
    }
    tmp_savedata1 = tmp_savedata1.split('&');

    var tmp_decoded_save = tmp_savedata1[0].split('|');
    while (tmp_decoded_save.length < 28)
        tmp_decoded_save.push(0);

    game.gold = parseInt(tmp_decoded_save[0]);
    game.food = parseInt(tmp_decoded_save[1]);
    game.mana = parseInt(tmp_decoded_save[2]);
    game.energy = tmp_decoded_save[3];
    game.days_elapsed = tmp_decoded_save[4];
    game.max_residents = tmp_decoded_save[5];
    game.jail_capacity = tmp_decoded_save[6];
    game.mission = {};
    game.mission.magequest = tmp_decoded_save[7];
    game.mission.street_urchin = tmp_decoded_save[8];
    game.slaversguild_firstvisit = tmp_decoded_save[9];
    game.guildrank = tmp_decoded_save[10];
    game.mission.brothel = tmp_decoded_save[11];
    game.mission.farm = tmp_decoded_save[12];
    game.setup = {};
    game.setup.tmp2 = tmp_decoded_save[13];
    game.mission.slaveguild = tmp_decoded_save[14];
    game.mission.plant = tmp_decoded_save[15];
    game.builders_unlocked = tmp_decoded_save[16];
    game.mission.dolin = tmp_decoded_save[17];
    game.mission.lis = tmp_decoded_save[18];
    game.mission.cali = tmp_decoded_save[19];
    game.patron_beg = tmp_decoded_save[20];
    game.companion = tmp_decoded_save[21];
    game.lab_level = tmp_decoded_save[22];
    game.library_level = tmp_decoded_save[23];
    game.sebastian_request = tmp_decoded_save[24];
    game.alchemy_level = tmp_decoded_save[25];
    game.settings = {};
    game.settings.textpopulation = tmp_decoded_save[26];
    game.personal_rooms = tmp_decoded_save[27];

    tmp_decoded_save = tmp_savedata1[1];
    var tmp_settings = tmp_decoded_save.split('|');
    while (tmp_settings.length < 8)
        tmp_settings.push('0');

    var _map = ['futanari', 'futa_balls', 'furry', 'loli', 'flavor_image',
                'tutorial', 'hide_description', 'per_race_images' ];
    for(var i = 0; i < _map.length; i++)
       game.settings[_map[i]] = tmp_settings[i];

    tmp_decoded_save = tmp_savedata1[2].split('|');
    while (tmp_decoded_save.length < 22)
            tmp_decoded_save.push(0);

    game.player = new GirlStub();
    game.player.id = 1;
    game.player.name = tmp_decoded_save[0];
    game.player.lastname = tmp_decoded_save[1];
    game.player.age = parseInt(tmp_decoded_save[2]) + 1; // adjust to new standard.
    game.player.race = parseInt(tmp_decoded_save[3]);
    game.player.body.gender = parseInt(tmp_decoded_save[4]);
    if (game.player.body.gender == 0)
        game.player.body.gender = 2; // new "male" number.
    game.player.body.hair_color = tmp_decoded_save[5];
    game.player.body.skincolor = tmp_decoded_save[6];
    game.player.body.eyecolor = tmp_decoded_save[7];
    game.player.body.tail = tmp_decoded_save[8];
    game.player.body.horns = tmp_decoded_save[10];
    game.player.body.taken_virginity = tmp_decoded_save[12];
    game.player.body.cocksize = tmp_decoded_save[13];
    game.player.body.balls = tmp_decoded_save[14];
    game.player.body.tits = tmp_decoded_save[15];
    game.player.body.ass = tmp_decoded_save[16];
    game.player.body.skincolor = tmp_decoded_save[17];
    game.player.body.skin_coverage = tmp_decoded_save[18];
    game.player.body.augmentations = tmp_decoded_save[19];
    game.player.body.lactation = tmp_decoded_save[20];
    game.player.body.cockstyle = tmp_decoded_save[21];
    tmp_decoded_save = tmp_savedata1[3].split('|');
    game.reservoirs = tmp_decoded_save[0];
    game.roles = {};
    game.roles.headgirl = tmp_decoded_save[1];
    game.roles.labassistant = tmp_decoded_save[2];
    game.roles.jailer = tmp_decoded_save[3];
    game.roles.manager = tmp_decoded_save[4];
    game.headgirl_orders = tmp_decoded_save[5];
    game.total_snails = tmp_decoded_save[6];

    // fixup inventory (potions)
    game.inventory = {};
    var tmp_inv = tmp_decoded_save[7].split('*');
    var _map = ['hair_dye', 'hair_growth_elixir', 'aphrodisiac', 'elixir_of_maturity', 'elixir_of_youth', 'clear_mind_potion', 'stimulant_potion', 'deterrence_potion', 'nursing_potion', 'majorus_concoction', 'minorus_concoction', 'amnesia_potion', 'miscarriage_potion', 'oblivion_potion', 'beauty_mixture'];
    for(var i = 0; i < _map.length; i++)
        game.inventory[_map[i]]  = parseInt(tmp_inv[i], 10);
    tmp_inv = tmp_decoded_save[8].split('*');
    _map = ['fluid_substance', 'tainted_essence', 'magic_essence', 'nature_essence', 'beastial_essence'];
    for(var i = 0; i < _map.length; i++)
        game.inventory[_map[i]]  = parseInt(tmp_inv[i], 10);

    game.portals = [];
    var tmp_portals = tmp_decoded_save[9].split('*');
    _map = [ 'grove', 'highlands', undefined, 'undercity' ];
    for (var i = 0; i < _map.length; i++)
        if (parseInt(tmp_portals[i]) > 0)
            game.portals.push(_map[i]);

    game.spells_known = {};
    var tmp_spells_known = tmp_decoded_save[10].split('*');
    _map = [ 'mind_read', 'sedation', 'heal', 'dream', 'entrancement', 'fear', 'domination', 'summon_tentacle', 'daze', 'mutate', 'mind_blast', 'rejuvenation', 'guidance'];
    for(var i = 0; i < _map.length; i++)
        if (parseInt(tmp_spells_known[i]) > 0)
            game.spells_known[_map[i]] = parseInt(tmp_spells_known[i], 10);

    tmp_decoded_save = tmp_savedata1[4].split('|');

    while (tmp_decoded_save.length < 73) {
        // UGH!
        var temp = "0";
        for (var i = 1; i < tmp_id.length; i++) {
            temp += "*0";
        }
        tmp_decoded_save.push(temp);
    }

    var tmp_id = tmp_decoded_save[0].split('*');
    var tmp_age = tmp_decoded_save[1].split('*');
    var tmp_ass = tmp_decoded_save[2].split('*');
    var tmp_augmentations = tmp_decoded_save[3].split('*');
    var tmp_busy = tmp_decoded_save[4].split('*');
    var tmp_brand = tmp_decoded_save[5].split('*');
    var tmp_charm = tmp_decoded_save[6].split('*');
    var tmp_clothing = tmp_decoded_save[7].split('*');
    var tmp_confidence = tmp_decoded_save[8].split('*');
    var tmp_contraception = tmp_decoded_save[9].split('*');
    var tmp_corruption = tmp_decoded_save[10].split('*');
    var tmp_courage = tmp_decoded_save[11].split('*');
    var tmp_ears  = tmp_decoded_save[12].split('*');
    var tmp_experience = tmp_decoded_save[13].split('*');
    var tmp_eyecolor = tmp_decoded_save[14].split('*');
    var tmp_father = tmp_decoded_save[15].split('*');
    var tmp_feeding = tmp_decoded_save[16].split('*');
    var tmp_hair_color = tmp_decoded_save[17].split('*');
    var tmp_hair_length = tmp_decoded_save[18].split('*');
    var tmp_hair_style = tmp_decoded_save[19].split('*');
    var tmp_health = tmp_decoded_save[20].split('*');
    var tmp_horns = tmp_decoded_save[21].split('*');
    var tmp_lactation = tmp_decoded_save[22].split('*');
    var tmp_level = tmp_decoded_save[23].split('*');
    var tmp_face  = tmp_decoded_save[24].split('*');
    var tmp_loyalty = tmp_decoded_save[25].split('*');
    var tmp_lust = tmp_decoded_save[26].split('*');
    var tmp_backstory = tmp_decoded_save[27].split('*');
    var tmp_mission = tmp_decoded_save[28].split('*');
    var tmp_mother = tmp_decoded_save[29].split('*');
    var tmp_servant_name = tmp_decoded_save[30].split('*');
    var tmp_obedience = tmp_decoded_save[31].split('*');
    var tmp_cocksize = tmp_decoded_save[32].split('*');
    var tmp_poteffect = tmp_decoded_save[33].split('*');
    var tmp_pregnancy = tmp_decoded_save[34].split('*');
    var tmp_pregnancyID = tmp_decoded_save[35].split('*');
    var tmp_race = tmp_decoded_save[36].split('*');
    var tmp_rules = tmp_decoded_save[37].split('*');
    var tmp_sex = tmp_decoded_save[38].split('*');
    var tmp_combat = tmp_decoded_save[39].split('*');
    var tmp_bodycontrol = tmp_decoded_save[40].split('*');
    var tmp_survival = tmp_decoded_save[41].split('*');
    var tmp_management = tmp_decoded_save[42].split('*');
    var tmp_service = tmp_decoded_save[43].split('*');
    var tmp_allure = tmp_decoded_save[44].split('*');
    var tmp_sexual_proficiency = tmp_decoded_save[45].split('*');
    var tmp_magicarts = tmp_decoded_save[46].split('*');
    var tmp_skillmax = tmp_decoded_save[47].split('*');
    var tmp_skillpoints = tmp_decoded_save[48].split('*');
    var tmp_skin = tmp_decoded_save[49].split('*');
    var tmp_skin_coverage = tmp_decoded_save[50].split('*');
    var tmp_sleep = tmp_decoded_save[51].split('*');
    var tmp_spelleffect = tmp_decoded_save[52].split('*');
    var tmp_stress = tmp_decoded_save[53].split('*');
    var tmp_lastname = tmp_decoded_save[54].split('*');
    var tmp_tail = tmp_decoded_save[55].split('*');
    var tmp_balls = tmp_decoded_save[56].split('*');
    var tmp_tits = tmp_decoded_save[57].split('*');
    var tmp_trait = tmp_decoded_save[58].split('*');
    var tmp_trait2 = tmp_decoded_save[59].split('*');
    var tmp_trait_known = tmp_decoded_save[61].split('*');
    var tmp_unique = tmp_decoded_save[62].split('*');
    var tmp_taken_virginity = tmp_decoded_save[63].split('*');
    var tmp_willpower = tmp_decoded_save[64].split('*');
    var tmp_wings = tmp_decoded_save[65].split('*');
    var tmp_wit = tmp_decoded_save[66].split('*');
    var tmp_job = tmp_decoded_save[67].split('*');
    var tmp_cock_style = tmp_decoded_save[68].split('*');
    var tmp_toxicity = tmp_decoded_save[69].split('*');
    var tmp_body_shape = tmp_decoded_save[70].split('*');
    var tmp_piercings = tmp_decoded_save[71].split('*');
    var tmp_extra_description =  tmp_decoded_save[72].split('*');

    /* new set of data? */
    tmp_decoded_save = tmp_savedata1[5].split('|');

    /* these need to be in ASM form for so the v4 import can accept them */
    game.asm = {};
    game.asm.var146 = []; // baby ears were never exported, sorry.
    game.asm.var134 = tmp_decoded_save[0].split('*');
    game.asm.var151 = tmp_decoded_save[1].split('*');
    game.asm.var215 = tmp_decoded_save[2].split('*');
    game.asm.var202 = tmp_decoded_save[3].split('*');
    game.asm.var162 = tmp_decoded_save[4].split('*');
    game.asm.var121 = tmp_decoded_save[5].split('*');
    game.asm.var167 = tmp_decoded_save[6].split('*');
    game.asm.var214 = tmp_decoded_save[7].split('*');
    game.asm.var168 = tmp_decoded_save[8].split('*');
    game.asm.var150 = tmp_decoded_save[9].split('*');
    game.asm.var181 = tmp_decoded_save[10].split('*');
    game.asm.var227 = tmp_decoded_save[11].split('*');
    game.asm.var159 = tmp_decoded_save[12].split('*');
    game.asm.var183 = tmp_decoded_save[13].split('*');
    game.asm.var278 = tmp_decoded_save[14].split('*');

    game.mission.slaveguild = 0;

    game.Girls = [];
    for (var i = 0; i < tmp_id.length; i++) {
        var girl = new GirlStub();
        girl.id = parseInt(tmp_id[i]);
        girl.age = parseInt(tmp_age[i]);
        girl.body.ass = parseInt(tmp_ass[i]);
        girl.body.augmentations = tmp_augmentations[i];
        girl.busy = parseInt(tmp_busy[i]);
        girl.brand = parseInt(tmp_brand[i]);
        girl.attr.charm = parseInt(tmp_charm[i]);
        girl.clothing = parseInt(tmp_clothing[i]);
        girl.attr.confidence = parseInt(tmp_confidence[i]);
        girl.contraception = parseInt(tmp_contraception[i]);
        girl.corruption = parseInt(tmp_corruption[i]);
        girl.attr.courage = parseInt(tmp_courage[i]);
        girl.body.ears = parseInt(tmp_ears[i]);
        girl.experience = parseInt(tmp_experience[i]);
        girl.body.eyecolor = tmp_eyecolor[i];
        girl.father = parseInt(tmp_father[i]);
        girl.prisoner_feeding = parseInt(tmp_feeding[i]);
        girl.body.hair_color = tmp_hair_color[i];
        girl.body.hair_length = parseInt(tmp_hair_length[i]);
        girl.body.hair_style = parseInt(tmp_hair_style[i]);
        girl.health = parseInt(tmp_health[i]);
        girl.body.horns = parseInt(tmp_horns[i]);
        girl.body.lactation = parseInt(tmp_lactation[i]);
        girl.level = parseInt(tmp_level[i]);
        girl.body.face = parseInt(tmp_face[i]);
        girl.loyalty = parseInt(tmp_loyalty[i]);
        girl.lust = parseInt(tmp_lust[i]);
        girl.backstory = tmp_backstory[i];
        girl.mission = parseInt(tmp_mission[i]);
        girl.mother = parseInt(tmp_mother[i]);
        girl.name = tmp_servant_name[i];
        girl.obedience = parseInt(tmp_obedience[i]);
        girl.body.cocksize = parseInt(tmp_cocksize[i]);
        girl.potion_effect = parseInt(tmp_poteffect[i]);
        girl.pregnancy = parseInt(tmp_pregnancy[i]);
        girl.pregnancyID = parseInt(tmp_pregnancyID[i]);
        girl.race = parseInt(tmp_race[i]);
        girl.rules = tmp_rules[i];
        girl.sex = parseInt(tmp_sex[i]);
        girl.skill.combat = parseInt(tmp_combat[i]);
        girl.skill.bodycontrol = parseInt(tmp_bodycontrol[i]);
        girl.skill.survival = parseInt(tmp_survival[i]);
        girl.skill.management = parseInt(tmp_management[i]);
        girl.skill.service = parseInt(tmp_service[i]);
        girl.skill.allure = parseInt(tmp_allure[i]);
        girl.skill.sex = parseInt(tmp_sexual_proficiency[i]);
        girl.skill.magicarts = parseInt(tmp_magicarts[i]);
        girl.skillmax = parseInt(tmp_skillmax[i]);
        girl.skillpoints = parseInt(tmp_skillpoints[i]);
        girl.body.skincolor = parseInt(tmp_skin[i]);
        girl.body.skin_coverage = parseInt(tmp_skin_coverage[i]);
        girl.where_sleep = parseInt(tmp_sleep[i]);
        girl.spell_effect = parseInt(tmp_spelleffect[i]);
        girl.stress = parseInt(tmp_stress[i]);
        girl.lastname = tmp_lastname[i];
        girl.body.tail = parseInt(tmp_tail[i]);
        girl.body.balls = parseInt(tmp_balls[i]);
        girl.body.tits = tmp_tits[i];
        girl.trait = parseInt(tmp_trait[i]);
        girl.trait2 = parseInt(tmp_trait2[i]);
        girl.trait_known = parseInt(tmp_trait_known[i]);
        girl.unique = parseInt(tmp_unique[i]);
        girl.body.taken_virginity = parseInt(tmp_taken_virginity[i]);
        girl.attr.willpower = parseInt(tmp_willpower[i]);
        girl.body.wings = parseInt(tmp_wings[i]);
        girl.attr.wit = parseInt(tmp_wit[i]);
        girl.job = parseInt(tmp_job[i]);
        girl.body.cockstyle = parseInt(tmp_cock_style[i]);
        girl.sex = parseInt(tmp_sex[i]);
        girl.body.cockstyle = parseInt(tmp_cock_style[i]);
        girl.toxicity = parseInt(tmp_toxicity[i]);
        girl.body.shape = parseInt(tmp_body_shape[i]);
        girl.body.piercings = parseInt(tmp_piercings[i]);
        girl.extra_description = tmp_extra_description[i];
        game.Girls.push(girl);
    }

    game.Girls.forEach(function (girl) {
        if (girl.toxicity.toString() == 'NaN')
            girl.toxicity = 0;
    });

    if (game.guildrank >= 2) {
        game.learned_refined_brand = 1;
    }

    var data = {};
    data.version = 3;
    data.game = game;
    data.title = "Main";
    data.prev = "Main";
    data.events = [];
    data.fontsize = fontSize;
    data.animation_enable = animation_enable;
    data.audio_enable = audio_enable;
    data.musicAudio = musicAudio.src;
    data.printImage = printImage.src;
    data.spritesDiv = spritesDiv.innerHTML;
    data.menuDiv = menuDiv.innerHTML;
    data.printTitle = printTitle.innerHTML;
    data.timetofade = TimeToFade;
    data.Sys_gender = asmSys_gender;
    data.Sys_choice = asmSys_choice;

    return data;
}

function ParseNewSave(data) {
    if (data.substr(0, 1) != '{')
        throw Fail("Expecting an encoded JSON object! Got:", data);

    var olddata = data;
    data = JSON.parse(data);
    return data;
}

function ParseStoryString(rawdata) {
    if (rawdata.substr(0,4) == 'SAVE') {
        console.log("Loading new-style uncompressed save");
        var data = base64decode(rawdata.substr(4));
        data = ParseNewSave(data);
    } else if (rawdata.substr(0,5) == 'ZSAVE') {
        console.log("Loading compressed new-style save");
        var compressed = base64decode(rawdata.substr(5));
        var data = pako.inflate(compressed, {to: 'string'});
        data = ParseNewSave(data);
    } else if (rawdata.substr(0,4) == 'JSON') {
        console.log("Loading new-style JSON save");
        var data = ParseNewSave(rawdata.substr(4));
    } else if (rawdata.match(/^[0-9]*-[0-9]*-[0-9]*/)) {
        console.log("Loading legacy-style save");
        var data = ParseOldSave(rawdata);
    } else {
        var data = base64decode(rawdata);
        if (data.charAt(0) == '{') {
            console.log("Loading new-style JSON save (missing header)");
            data = ParseNewSave(data);
        } else {
            throw Fail("Unrecognized save type", rawdata);
        }
    }
    UpdateSave(data);
    return data;
}

function StartFromStoryString(rawdata) {
    try {
        var data = ParseStoryString(rawdata);
        GameStartFromData(data);
    } catch (e) {
        var stack = anonymizeStack(e.stack);

        var err = "Failed to restore! Pastebin the save data and report it to the devs.";
        err += "

<textarea rows='20' cols='120' onclick='this.focus();this.select();'>";
        err += e + "\n\r" + stack + "\n\r";
        err += "Save was:\n\r\n\r" + rawdata + "\n\r" + "</textarea>";
        popup(err, true);
    }
}

Game_.prototype.LoadStory = function(slot_num) {
    if (slot_num === undefined) {
        slot_num = 1;
    }
    var data = '';
    data = LoadStoryLocal(slot_num);
    if (data) {
        StartFromStoryString(data);
    } else {
        AlertMessage('Failed restore!');
    }
}

Location.push(new Locations("save", function() {
    var save = DumpGameToStoryString().replace(/[&]/g, "&").replace(/[<]/g, "<").replace(/[>]/g, ">");
    ToText("

Please, save this code and paste it in the input field on next session. (select and press ctrl+c and ctrl+v to paste)

");
    ToText("<textarea rows='4' cols='70' onclick='this.focus();this.select();'>" + save + "</textarea>");
}, 1));

Location.push(new Locations("savedebug", function() {
    var save = DumpGameToStoryString(true).replace(/[&]/g, "&").replace(/[<]/g, "<").replace(/[>]/g, ">");
    ToText("

Please, save this code and paste it in the input field on next session. (select and press ctrl+c and ctrl+v to paste)

");
    ToText("<textarea rows='20' cols='120' onclick='this.focus();this.select();'>" + save + "</textarea>");
}, 1));

repo_name: spdevel/sfp

license: unknown