code:
/* Hair: base is red */

@include make_hair('black') {
    -webkit-filter: hue-rotate(0deg) saturate(0%) brightness(40%) contrast(100%);
    filter: hue-rotate(0deg) saturate(0%) brightness(40%) contrast(100%);
}

@include make_hair('platinumblond') {
    -webkit-filter: hue-rotate(57deg) saturate(250%) brightness(180%) contrast(60%) sepia(0.75);
    filter: hue-rotate(57deg) saturate(250%) brightness(180%) contrast(60%) sepia(0.75);
}

@include make_hair('blond') {
    -webkit-filter: hue-rotate(60deg) saturate(130%) brightness(135%) contrast(100%);
    filter: hue-rotate(60deg) saturate(130%) brightness(135%) contrast(100%);
}

@include make_hair('blue') {
    -webkit-filter: hue-rotate(210deg) saturate(200%) brightness(60%) contrast(150%);
    filter: hue-rotate(210deg) saturate(200%) brightness(60%) contrast(150%);
}

@include make_hair('brown') {
    -webkit-filter: hue-rotate(40deg) saturate(100%) brightness(80%) contrast(100%);
    filter: hue-rotate(40deg) saturate(100%) brightness(80%) contrast(100%);
}

@include make_hair('lightbrown') {
    -webkit-filter: hue-rotate(40deg) saturate(100%) brightness(100%) contrast(110%);
    filter: hue-rotate(40deg) saturate(100%) brightness(100%) contrast(110%);
}

@include make_hair('strawberryblond') {
    -webkit-filter: hue-rotate(30deg) saturate(110%) brightness(110%) contrast(100%);
    filter: hue-rotate(30deg) saturate(110%) brightness(110%) contrast(100%);
}

@include make_hair('ginger') {
    -webkit-filter: hue-rotate(55deg) saturate(200%) brightness(110%) contrast(100%);
    filter: hue-rotate(55deg) saturate(200%) brightness(110%) contrast(100%);
}

@include make_hair('green') {
    -webkit-filter: hue-rotate(90deg) saturate(100%) brightness(100%) contrast(100%);
    filter: hue-rotate(90deg) saturate(100%) brightness(100%) contrast(100%);
}

@include make_hair('teal') {
    -webkit-filter: hue-rotate(165deg) saturate(100%) brightness(100%) contrast(100%);
    filter: hue-rotate(165deg) saturate(100%) brightness(100%) contrast(100%);
}

@include make_hair('pink') {
    -webkit-filter: hue-rotate(-20deg) saturate(70%) brightness(130%) contrast(100%);
    filter: hue-rotate(-20deg) saturate(70%) brightness(130%) contrast(100%);
}

@include make_hair('hotpink') {
    -webkit-filter: hue-rotate(320deg) saturate(100%) brightness(120%) contrast(100%);
    filter: hue-rotate(320deg) saturate(100%) brightness(120%) contrast(100%);
}

@include make_hair('softpink') {
    -webkit-filter: hue-rotate(0deg) saturate(30%) brightness(190%) contrast(100%);
    filter: hue-rotate(0deg) saturate(30%) brightness(190%) contrast(100%);
}

@include make_hair('purple') {
    -webkit-filter: hue-rotate(-80deg) saturate(110%) brightness(60%) contrast(200%);
    filter: hue-rotate(-80deg) saturate(110%) brightness(60%) contrast(200%);
}

@include make_hair('red') {
    -webkit-filter: hue-rotate(0deg) saturate(100%) brightness(100%) contrast(100%);
    filter: hue-rotate(0deg) saturate(100%) brightness(100%) contrast(100%);
}

@include make_hair('crimson') {
    -webkit-filter: hue-rotate(0deg) saturate(155%) brightness(75%) contrast(135%);
    filter: hue-rotate(0deg) saturate(155%) brightness(75%) contrast(135%);
}

@include make_hair('white') {
    -webkit-filter: hue-rotate(0deg) saturate(0%) brightness(160%) contrast(100%);
    filter: hue-rotate(0deg) saturate(0%) brightness(160%) contrast(100%);
}

@include make_hair('snowwhite') {
    -webkit-filter: hue-rotate(0deg) saturate(0%) brightness(190%) contrast(100%);
    filter: hue-rotate(0deg) saturate(0%) brightness(190%) contrast(100%);
}


code:
@each $key, $value in $layers {
    .layer-#{$key} {
        z-index: $value;
    }
}

repo_name: UnbarredStream/degrees


code:
#map-display-container {
    width: 100%;
    position: relative;

    img {
        display: block;
    }

    #background-layer {
        z-index: -1;
    }

    .location-single {
        position: absolute;
        transition: margin-top $mapLocTransition;
        margin-top: 0;

        .location-light {
            position: absolute;
            top: 0;
            left: 0;
        }

        .location-active-trigger {
            position: absolute;
            z-index: 1;
        }

        &.link-active {
            &.location-is-hovered {
                margin-top: -10px;
            }

            // DEV
            border: solid 3px blue;
        }
    }
}


code:
@mixin make_meter($selector, $color) {
    .#{unquote($selector)} {
        position: absolute;
        left: 0;
        top: 0;
        height: 0.3em;
        background-color: $color;
        z-index: -10;
    }
}

@mixin make_hair($selector) {
    .hair-#{unquote($selector)} {
        .colour-hair {
            @content;
        }
    }
}

@mixin make_eye($selector) {
    .eye-#{unquote($selector)} {
        .colour-eye {
            @content;
        }
    }
}

@mixin make_clothing($selector) {
    .upper-#{unquote($selector)} {
        .colour-upper {
            @content;
        }
    }

    .lower-#{unquote($selector)} {
        .colour-lower {
            @content;
        }
    }

    .under-#{unquote($selector)} {
        .colour-under {
            @content;
        }
    }
}

@mixin make_animation($identifier, $steps, $time, $pace: "", $overrideSteps: false) {
    $class: unquote("anim-") + unquote($identifier) + "-" + $steps + unquote("f");

    @if $pace != "" {
        $class: $class + unquote("-") + unquote($pace);
    }

    @if $overrideSteps != false {
        $steps: $overrideSteps;
    }

    .#{$class} {
        -webkit-animation-duration: $time;
        -moz-animation-duration: $time;
        -o-animation-duration: $time;
        animation-duration: $time;

        -webkit-animation-timing-function: steps($steps);
        -moz-animation-timing-function: steps($steps);
        -o-animation-timing-function: steps($steps);
        animation-timing-function: steps($steps);

        -webkit-animation-iteration-count: infinite;
        -moz-animation-iteration-count: infinite;
        -o-animation-iteration-count: infinite;
        animation-iteration-count: infinite;

        -webkit-animation-name: anim-default;
        -moz-animation-name: anim-default;
        -o-animation-name: anim-default;
        animation-name: anim-default;
    }
}


code:
.macro-numberslider {
    display: inline-block;

    input {
        width: 3em;
        text-align: center;

        -webkit-appearance: none;
        cursor: pointer;
        height: 1.25em;
        min-width: 20em;
        vertical-align: middle;

        &:focus {
            outline: none;
        }

        &::-webkit-slider-runnable-track, &::-moz-range-track {
            background: #222;
            border: 1px solid #444;
            border-radius: 0;
            cursor: pointer;
            height: 10px;
            width: 100%;

            &:focus {
                background: #222;
            }
        }

        &::-webkit-slider-thumb, &::-moz-range-thumb {
            -webkit-appearance: none;
            background: #35a;
            border: 1px solid #57c;
            border-radius: 0;
            cursor: pointer;
            height: 18px;
            margin-top: -5px;
            width: 33px;
        }

        &::-ms-track {
            background: transparent;
            border-color: transparent;
            color: transparent;
            cursor: pointer;
            height: 10px;
            width: 99%; /* fallback for MS browsers without support for calc() */
            width: calc(100% - 1px);
        }

        &::-ms-thumb {
            background: #35a;
            border: 1px solid #57c;
            border-radius: 0;
            cursor: pointer;
            height: 16px;
            width: 33px;
        }

        &::-ms-fill-lower {
            background: #222;
            border: 1px solid #444;
            border-radius: 0;
        }

        &::-ms-fill-upper {
            background: #222;
            border: 1px solid #444;
            border-radius: 0;
        }
    }

    button {
        font-family: "tme-fa-icons";
        font-style: normal;
        font-weight: normal;
        font-variant: normal;
        text-transform: none;
        line-height: normal;
        speak: none;
        padding: 0.4em 0.6em;

        &[id^="numberbox-minus"] {
            margin-right: 0.4em;
        }

        &[id^="numberbox-plus"] {
            margin-left: 0.4em;
        }
    }

    span {
        display: inline-block;
        /*font-size: 1.25em;*/
        margin-left: 0.25em;
        min-width: 3em;
        text-align: center;
        vertical-align: middle;
    }
}


code:
#ui-bar {
    z-index: 300;
}

#ui-bar-toggle {
    z-index: 500;
}

body {
    &.has-images {
        header {
            &#title {
                display: none;
            }
        }

        #story-caption {
            /* 256 px: images
	        2.5 em: ui-bar-body margin-top */
            margin-top: calc(256px - 2.5em);
        }
    }
}

.hide {
    a {
        color: $white;
        text-decoration: none !important;

        &:hover {
            color: $white;
        }
    }
}

.general {
    opacity: 0.99;
}

#statbar {
    position: relative;
    left: 0;
    top: 9px;
    z-index: 0;
}

#img {
    height: 256px;
    width: 256px;
    overflow: hidden;

    position: fixed;
    top: 0;
    left: 0;

    & > * {
        position: absolute;
        top: 0;
        left: 0;
    }
}

#daystate {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 10;
}

#weather {
    position: fixed;
    left: 0;
    top: 64px;
    z-index: 10;
}

#location {
    position: fixed;
    left: 0;
    top: 128px;
    z-index: 10;
}

#divmap {
    position: relative;
    display: inline-block;
    height: 256px;
    width: 256px;

}

#map {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 10;
}

#maparrow {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 20;
}

.meter {
    position: relative;
    height: 0.3em;
    width: 100%;
    background-color: gray;
    text-align: center;
    z-index: -10;
}

@include make_meter('redbar', $red);
@include make_meter('pinkbar', $pink);
@include make_meter('purplebar', $purple);
@include make_meter('bluebar', $blue);
@include make_meter('lbluebar', $light-blue);
@include make_meter('tealbar', $teal);
@include make_meter('greenbar', $green);
@include make_meter('goldbar', $gold);


code:
$red: #EC3535;
$pink: #E40081;
$purple: #AA4BC8;
$blue: #4372FF;
$light-blue: #559BC0;
$teal: #08C1C1;
$green: #38B20A;
$platinum: #FEF593;
$gold: #FFD700;
$orange: #f28500;
$strawberry: #F08C51;
$tangerine: #f28500;
$brown: #aa7243;
$light-brown: #C5793A;
$gray: #929292;
$light-gray: #B7B7B7;

$white: #eee;

$action-brat: #B75D41;
$action-meek: #C44B8B;
$action-def: #D20000;
$action-sub: #AA4BC8;
$action-lewd: #E04880;

$mapLocTransition: 0.3s;

$layers: (
    'bg': 0,
    'backhair': 10,
    'base': 20,
    'hirsute': 21,
    'eyes': 30,
    'sclera': 35,
    'mouth': 40,
    'blush': 50,
    'tears': 55,
    'hair': 60,
    'brow': 62,
    'lashes': 64,
    'under': 75,
    'legsacc': 80,
    'feetacc': 85,
    'lower': 90,
    'upper': 95,
    'collar': 100,
    'rightarm': 105,
    'leftarm': 105,
    'rightarmclothes': 110,
    'leftarmclothes': 110,
    'faceacc': 125,
    'headacc': 130
);


code:
.wetstage2 {
    opacity: 0.90;
    filter: brightness(0.95);
}

.wetstage3 {
    opacity: 0.75;
    filter: brightness(0.90);
}


code:
#divsex {
    position: relative;
    display: inline-block;
    height: 256px;
    width: 768px;

    & > * {
        position: absolute;
        left: 256px;
        top: 0;
    }
}

#divandroidsex {
    position: relative;
    display: inline-block;
    height: 256px;
    width: 256px;
    left: -256px;

    & > * {
        position: absolute;
        left: 256px;
        top: 0;
    }
}

#closepenis {
    &, #divsex & {
        position: absolute;
        left: 321px;
        top: -30px;
        z-index: 250;
    }
}

#closeanus {
    &, #divsex & {
        position: absolute;
        left: 451px;
        top: -30px;
        z-index: 250;
    }
}

#closevagina {
    &, #divsex & {
        position: absolute;
        left: 386px;
        top: -30px;
        z-index: 250;
    }
}

#divandroidxray {
    position: relative;
    display: inline-block;
    height: 256px;
    width: 256px;
}

#xrayvaginal {
    &, #divsex & {
        position: absolute;
        left: 0;
        top: 0;
        z-index: 3;
    }
}

#xrayvaginalcum {
    &, #divsex & {
        position: absolute;
        left: 0;
        top: 0;
        z-index: 4;
    }
}

#xraypenile {
    &, #divsex & {
        position: absolute;
        left: 0;
        top: 0;
        z-index: 2;
    }
}

#xrayanal {
    &, #divsex & {
        position: absolute;
        left: 512px;
        top: 0;
        z-index: 1;
    }
}

#xrayandroidanal {
    &, #divsex & {
        position: relative;
        left: 0;
        top: -150px;
        z-index: 1;
    }
}


code:
@import "mixins";
@import "variables";

@import "colors";

@import "ui";
@import "numberbox";
@import "wet";
@import "layers";
@import "xray";
@import "hair";
@import "eyes";
@import "clothing";

@import "animation_base";
@import "animation";

@import "map";


code:
export default [{
    type: 'outfit',
    states: {
        100: {
            upper: 'img/upperclothes/sundressupper/sundressupperred.png'
        }
    }
}];


code:
export default [{
    type: 'upper',
    id: 't_shirt',
    name: 'T-Shirt',
    states: {
        100: {
            sprite: 'GOOD_SPRITE'
        },
        70: {
            sprite: 'TORN_WHATEVER'
        },
        //[...]
    },
    variants: {
        'red': {
            class: 'red'
        },
        'blue': {
            class: 'red'
        },
        'black_with_heart': {
            states: {
                100: {
                    sprite: 'GOOD_SPRITE_WITH_HEART'
                },
                70: {
                    sprite: 'TORN_WHATEVER_WITH_HEART'
                },
            },
            combat: {
                'hips': 'special',
                'neck': 'sprite',
                'thigs': 'goes',
                'tummy': 'here'
            }
        }
    },
    combat: {
        'hips': 'whatever',
        'neck': 'sprite',
        'thigs': 'sprite',
        'tummy': 'sprite'
    },
    integrity: {
        max: 100,
        current: 100 // or saved
    },
    fabric_strength: 100, // dmg - fabric strength (min 1 dmg?)
    reveal: 100
}];


code:
class Clothing {

}

export default Clothing;


code:
import {checkForDaylightExposure} from "./PlayerStates";
import {colourContainerClasses} from "../Macros/Misc";
import "../Macros/Numberpool";
import {upperIntegrity, lowerIntegrity, underIntegrity} from "../Macros/Clothing";
import {macroSetup} from "./Utilities";
import GameMap from "./GameMap";
import "./Numberify";
import {ImportDefaultGameState, GetNpc} from "../Macros/GameState";
import "../Update/SaveUpdater";

class Game {
    constructor() {
        this.initState();
        this.initMacros();
        this.initTwineHelper();

        console.log("Degrees of Lewdity initialized.");
    }

    initMacros() {
        macroSetup('display_map', GameMap);

        macroSetup('underintegrity', underIntegrity, true, true);
        macroSetup('lowerintegrity', lowerIntegrity, true, true);
        macroSetup('upperintegrity', upperIntegrity, true, true);
        macroSetup('init_game_state', ImportDefaultGameState, false, true);
    }

    initState() {
        Config.history.controls = false;
        Config.history.maxStates = 20;

        State.initPRNG();

        $(document).ready(() => {
            $('#sidetooltip').appendTo('body');
        });
    }

    initTwineHelper() {
        window.DoL = {
            colourContainerClasses: colourContainerClasses,
            daylightExposure: checkForDaylightExposure
        };
    }

    static getGameVersion() {
        return '1.25.0';
    }
}

export default Game;


code:
import Maps from "../Maps/All";
import {get} from "./Utilities";
import _ from 'lodash';

class GameMap {
    macro = null;
    map = null;
    locationID = null;

    location = null;

    renderedLocations = {};

    constructor(macro) {
        this.macro = macro;
        const [locationID, mapID] = this.macro.args;

        this.map = GameMap.getMapById(mapID);
        this.locationID = locationID;
        if (this.map === null)
            this.macro.error(`Invalid map id specified (${mapID}) or default map is not set!`);

        this.validateLocation();
    }

    validateLocation() {
        if (typeof this.locationID !== "string")
            this.macro.error("Invalid location id type");

        for (let m of this.map.map.locations) {
            if (this.locationID === m.id) {
                this.location = m;
                break;
            }
        }

        if (this.location === null)
            this.macro.error(`Invalid location id specified (${this.locationID}), not found in map (${this.map.mapID})`);
    }

    static getDefaultMap() {
        for (let m of Maps)
            if (typeof m.default !== "undefined" && m.default)
                return m;

        return null;
    }

    static getMapById(mapID) {
        if (typeof mapID === "undefined")
            return GameMap.getDefaultMap();

        for (let m of Maps)
            if (m.mapID === mapID)
                return m;

        return null;
    }

    renderLocation(loc, map) {
        const location = $('<div>').attr('id', 'location-' + loc.id).css({
            left: loc.x,
            top: loc.y
        }).addClass('location-single');

        $(location).append(
            $('<img>').addClass('location-sprite').attr('src', get(loc.sprite))
        );

        if (typeof loc.light !== "undefined")
            $(location).append(
                $('<img>').addClass('location-light').attr('src', get(loc.light))
            );

        if (typeof loc.active === "undefined") {
            $(location).append(
                $('<div>').addClass('location-active-trigger').css({
                    left: 0,
                    top: 0,
                    width: '100%',
                    height: '100%'
                })
            );
        }
        else {
            const active = get(loc.active);

            const left = active.x;
            const top = active.y;
            const width = active.width;
            const height = active.height;

            $(location).append(
                $('<div>').addClass('location-active-trigger').addClass('location-active-trigger-custom').css({
                    left,
                    top,
                    width,
                    height
                })
            );
        }

        if (this.locationID === loc.id) {
            $(location).addClass('location-current');
        }

        this.renderedLocations[loc.id] = location;

        $(map).append(location);
    }

    attachLinks() {
        let currentLocation = null;
        for (let l of this.map.map.locations) {
            if (this.locationID === l.id) {
                currentLocation = l;
                break;
            }
        }

        let links = get(currentLocation.links);
        let locations = _.keyBy(_.cloneDeep(this.map.map.locations), o => o.id);

        for (let link of links) {
            if (typeof this.renderedLocations[link] === "undefined") {
                console.warn(`Unknown selected for the current location (${link})`);
                continue;
            }

            if (typeof locations[link].canBeVisited !== "undefined") {
                if (!get(locations[link].canBeVisited)) {
                    delete locations[link];
                    continue;
                }
            }

            $(this.renderedLocations[link]).addClass('link-active');

            const activeTrigger = $(this.renderedLocations[link]).find('.location-active-trigger');

            $(activeTrigger).on('click', () => {
                const myLocation = _.keyBy(this.map.map.locations, o => o.id)[link];
                if (typeof myLocation.passage === "undefined") {
                    console.warn(`Passage for link ${link} is not defined.`);
                    return;
                }

                const passage = get(myLocation.passage);
                if (typeof passage !== "string") {
                    console.error(`Passage returned for link ${link} is not a string.`);
                    return;
                }

                const timePass = get(myLocation.timePass);
                if (typeof timePass === "number")
                    Wikifier.wikifyEval(`<<pass ${timePass}>>`);

                Engine.play(passage);
            }).on('mouseenter', (e) => {
                $(e.currentTarget).closest('.location-single').addClass('location-is-hovered');
            }).on('mouseleave', (e) => {
                $(e.currentTarget).closest('.location-single').removeClass('location-is-hovered');
            });
            delete locations[link];
        }

        for (let link in locations) {
            if (link === this.locationID)
                continue; // current location is in this list, no need to pollute it with the class

            $(this.renderedLocations[link]).addClass('link-inactive');
        }
    }

    renderMap() {
        const container = $('<div>').attr('id', 'map-display-container').addClass('map-id-' + this.map.mapID);
        $(container).append(
            $('<img>').attr('id', 'background-layer').attr('src', get(this.map.map.background))
        );

        for (let l of this.map.map.locations)
            this.renderLocation(l, container);

        this.attachLinks();

        return container;
    }

    run() {
        jQuery(this.macro.output).append(this.renderMap());
    }
}

export default GameMap;


code:
const disableNumberifyInVisibleElements = [
    '#passage-hairdressers-seat',
    '#passage-start',
    '#passage-wardrobe',
    '#passage-cheats',
    '#passage-testing-room'
];

let currentLinks = [];

function getPrettyKeyNumber(counter) {
    var str = "";

    if (counter > 30)
        str = "Ctrl + ";
    else if (counter > 20)
        str = "Alt + ";
    else if (counter > 10)
        str = "Shift + ";

    if (counter % 10 === 0)
        str += "0";
    else if (counter < 10)
        str += counter;
    else {
        let c = Math.floor(counter / 10);
        str += (counter - (10 * c)).toString();
    }

    return str;
}

$(document).on(':passagerender', (ev) => {
    currentLinks = [];

    for (var i = 0; i < disableNumberifyInVisibleElements.length; i++) {
        if ($(ev.content).find(disableNumberifyInVisibleElements[i]).length || $(ev.content).is(disableNumberifyInVisibleElements[i]))
            return; // simply skip this render
    }

    currentLinks = $(ev.content).find(".link-internal"); // wanted to use .macro-link, but wardrobe and something else doesn't get selected, lmao

    $(currentLinks).each(function(i, el) {
        $(el).html("(" + getPrettyKeyNumber(i + 1) + ") " + $(el).html());
    });
});

$(document).on('keyup', (ev) => {
    if ((ev.keyCode >= 48 && ev.keyCode <= 57) || (ev.keyCode >= 96 && ev.keyCode <= 105)) {
        const fixedKeyIndex = (ev.keyCode < 60 ? ev.keyCode - 48 : ev.keyCode - 96);

        let requestedLinkIndex = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8][fixedKeyIndex];

        if (ev.ctrlKey)
            requestedLinkIndex += 30;
        else if (ev.altKey)
            requestedLinkIndex += 20;
        else if (ev.shiftKey)
            requestedLinkIndex += 10;

        if ($(currentLinks).length >= requestedLinkIndex + 1)
            $(currentLinks[requestedLinkIndex]).click();
    }
});


code:
export function checkForDaylightExposure() {
    const exposedState = parseInt(State.variables.exposed);
    const dayState = State.variables.daystate;

    return exposedState >= 1 && dayState !== "night";
}


code:
export function macroSetup(tag, target, output = false, asFunction = false, functionTarget = 'run') {
    Macro.add(tag, {
        isWidget: false,
        handler: function() {
            const self = this;
            let returnValue = (asFunction ? target(self) : new target(self)[functionTarget]());

            if (output)
                $(self.output).wiki(returnValue);
        }
    });
}

export function get(target) {
    if (typeof target === "function")
        return target();

    return target;
}


code:
export function underIntegrity() {
    let output = '';
    const underIntegrity = State.variables.underintegrity;
    const underIntegrityMax = State.variables.underintegritymax;

    if (underIntegrityMax !== 0) {
        if (underIntegrity <= (underIntegrityMax / 10) * 2)
            output = 'tattered \t';
        else if (underIntegrity <= (underIntegrityMax / 10) * 5)
            output = 'torn \t';
        else if (underIntegrity <= (underIntegrityMax / 10) * 9)
            output = 'frayed \t';
    }

    return output;
}

export function lowerIntegrity() {
    let output = '';
    const lowerIntegrity = State.variables.lowerintegrity;
    const lowerIntegrityMax = State.variables.lowerintegritymax;

    if (lowerIntegrityMax !== 0) {
        if (lowerIntegrity <= (lowerIntegrityMax / 10) * 2)
            output = 'tattered \t';
        else if (lowerIntegrity <= (lowerIntegrityMax / 10) * 5)
            output = 'torn \t';
        else if (lowerIntegrity <= (lowerIntegrityMax / 10) * 9)
            output = 'frayed \t';
    }

    return output;
}

export function upperIntegrity() {
    let output = '';
    const upperIntegrity = State.variables.upperintegrity;
    const upperIntegrityMax = State.variables.upperintegritymax;

    if (upperIntegrityMax !== 0) {
        if (upperIntegrity <= (upperIntegrityMax / 10) * 2)
            output = 'tattered \t';
        else if (upperIntegrity <= (upperIntegrityMax / 10) * 5)
            output = 'torn \t';
        else if (upperIntegrity <= (upperIntegrityMax / 10) * 9)
            output = 'frayed \t';
    }

    return output;
}


code:
import {GameState, GameVersion, NpcShared, NpcHuman, NpcBeast, NpcSwarm, NpcTentacles, NpcVore} from "../Update/Versions";
import * as deepmerge from "deepmerge";

const NpcMap = {
    'man': NpcHuman,
    'beast': NpcHuman,
    'vore': NpcVore,
    'tentacles': NpcTentacles,
    'swarm': NpcSwarm
};

const SugarCubeVariables = [
    'passage',
    'tags'
];

export function ImportDefaultGameState() {
    for (let key in State.variables)
        if (SugarCubeVariables.indexOf(key) === -1)
            delete State.variables[key];

    for (let key in GameState)
        State.variables[key] = GameState[key];

    State.variables._gameVersion = GameVersion;

    console.log("Default game state set.");
}

export function GetNpc(npcType = null) {
    if (!NpcMap.hasOwnProperty(npcType))
        throw new Error(`No valid npc type specified (${npcType}`);

    return deepmerge.merge(NpcShared, NpcMap[npcType]);
}


code:
Macro.add('time', {
    handler: () => {
        const time = State.variables.time;
        const hour = Math.floor(time/60);

        let dayState = '';

        if (hour < 6)
            dayState = 'night';
        else if (hour < 9)
            dayState = 'dawn';
        else if (hour < 18)
            dayState = 'day';
        else if (hour < 21)
            dayState = 'dusk';
        else
            dayState = 'night';

        State.variables.hour = hour;
        State.variables.daystate = dayState;
    }
});

export function colourContainerClasses() {
    const V = State.variables;
    return 'hair-'  + (V.haircolour||'').replace(/ /g,'-') +
        ' ' + 'eye-'   + (V.eyecolour||'').replace(/ /g,'-') +
        ' ' + 'upper-' + (V.uppercolour||'').replace(/ /g,'-') +
        ' ' + 'lower-' + (V.lowercolour||'').replace(/ /g,'-') +
        ' ' + 'under-' + (V.undercolour||'').replace(/ /g,'-')
}


code:
Macro.add("numberbox", {
    handler: function() {
        function validateAndApply(el, addend) {
            var curValue = Math.trunc(Wikifier.getValue(varName)),
                newValue = Math.trunc(el.value),
                newPoolValue = null;
            if (Number.isNaN(newValue) || !Number.isFinite(newValue)) return el.value = curValue, !1;
            if (null != addend && (newValue += addend), newValue < minValue ? newValue = minValue : newValue > maxValue && (newValue = maxValue), null !== pool) {
                var poolValue = pool.get(),
                    delta = (newValue - curValue) * poolCost;
                delta < 0 ? newPoolValue = poolValue - delta : delta > 0 && poolValue >= poolCost ? (poolValue < delta && (newValue = curValue + Math.trunc(poolValue / poolCost), delta = poolValue - poolValue % poolCost), newPoolValue = poolValue - delta) : newValue = curValue
            }
            return Wikifier.setValue(varName, newValue), el.value = newValue, null !== newPoolValue && pool.set(newPoolValue), !0
        }
        var _this = this;
        if (this.args.length < 4) {
            var errors = [];
            return this.args.length < 1 && errors.push("variable name"), this.args.length < 2 && errors.push("default value"), this.args.length < 3 && errors.push("min value"), this.args.length < 4 && errors.push("max value"), this.error("no " + errors.join(" or ") + " specified")
        }
        if ("string" != typeof this.args[0]) return this.error("variable name argument is not a string");
        var varName = this.args[0].trim();
        if ("$" !== varName[0] && "_" !== varName[0]) return this.error('variable name "' + this.args[0] + '" is missing its sigil ($ or _)');
        var varId = Util.slugify(varName),
            defValue = Number(this.args[1]),
            minValue = Number(this.args[2]),
            maxValue = Number(this.args[3]),
            poolCost = 1,
            autofocus = !1;
        if (this.args.length > 5 ? (poolCost = Number(this.args[4]), autofocus = "autofocus" === this.args[5]) : this.args.length > 4 && ("autofocus" === this.args[4] ? autofocus = !0 : poolCost = Number(this.args[4])), Number.isNaN(defValue) || !Number.isFinite(defValue) || Math.trunc(defValue) !== defValue) return this.error("default value (" + this.args[1] + ") is not a whole number");
        if (Number.isNaN(minValue) || !Number.isFinite(minValue) || Math.trunc(minValue) !== minValue) return this.error("min value (" + this.args[2] + ") is not a whole number");
        if (Number.isNaN(maxValue) || !Number.isFinite(maxValue) || Math.trunc(maxValue) !== maxValue) return this.error("max value (" + this.args[3] + ") is not a whole number");
        if (Number.isNaN(poolCost) || !Number.isFinite(poolCost) || Math.trunc(poolCost) !== poolCost || poolCost <= 0) return this.error("pool cost (" + this.args[4] + ") is not a whole number greater than zero");
        if (defValue < minValue) return this.error("default value (" + this.args[1] + ") is less than min value (" + this.args[2] + ")");
        if (defValue > maxValue) return this.error("default value (" + this.args[1] + ") is greater than max value (" + this.args[3] + ")");
        var pool = function() {
            var parent = _this.contextSelect(function(ctx) {
                return "numberpool" === ctx.name
            });
            return null !== parent && parent.hasOwnProperty("pool") ? parent.pool : null
        }();
        Config.debug && this.debugView.modes({
            block: !0
        });
        var $elControl = jQuery(document.createElement("div")),
            $elInput = jQuery(document.createElement("input"));
        $elControl.attr("id", this.name + "-body-" + varId).addClass("macro-" + this.name).appendTo(this.output), jQuery(document.createElement("button")).attr({
            id: this.name + "-minus-" + varId,
            tabindex: 0
        }).text("").on("click", function() {
            return validateAndApply($elInput[0], -1)
        }).appendTo($elControl), $elInput.attr({
            id: this.name + "-input-" + varId,
            name: this.name + "-input-" + varId,
            type: "text",
            pattern: "\\d+",
            tabindex: 0
        }).on("change", function() {
            validateAndApply(this)
        }).on("keypress", function(ev) {
            13 === ev.which && (ev.preventDefault(), $elInput.trigger("change"))
        }).appendTo($elControl), jQuery(document.createElement("button")).attr({
            id: this.name + "-plus-" + varId,
            tabindex: 0
        }).text("").on("click", function() {
            return validateAndApply($elInput[0], 1)
        }).appendTo($elControl), $elInput.val(defValue), validateAndApply($elInput[0]), autofocus && ($elInput.attr("autofocus", "autofocus"), postdisplay["#autofocus:" + $elInput.attr("id")] = function(task) {
            delete postdisplay[task], setTimeout(function() {
                return $elInput.focus()
            }, Engine.minDomActionDelay)
        })
    }
});

Macro.add("numberpool", {
    tags: ["onchange"],
    handler: function() {
        if (0 === this.args.length) return this.error("no variable name specified");
        if (this.payload.length > 2) return this.error("multiple <<onchange>> sections specified");
        if ("string" != typeof this.args[0]) return this.error("variable name argument is not a string");
        var varName = this.args[0].trim();
        if ("$" !== varName[0] && "_" !== varName[0]) return this.error('variable name "' + this.args[0] + '" is missing its sigil ($ or _)');
        var curValue = Wikifier.getValue(varName);
        if ("number" != typeof curValue || Number.isNaN(curValue) || !Number.isFinite(curValue)) return this.error("pool value is not a number");
        var varId = Util.slugify(varName);
        TempState.hasOwnProperty(this.name) || (TempState[this.name] = {}), TempState[this.name].hasOwnProperty(varId) || (TempState[this.name][varId] = 0), Object.defineProperty(this, "pool", {
            value: Object.defineProperties({}, {
                get: {
                    value: function() {
                        return Wikifier.getValue(varName)
                    }
                },
                set: {
                    value: function(content) {
                        return function(value) {
                            var curValue = Wikifier.getValue(varName);
                            value !== curValue && (Wikifier.setValue(varName, value), content && new Wikifier(null, content))
                        }
                    }(this.payload.length > 1 ? this.payload[1].contents.trim() : "")
                }
            })
        }), jQuery(document.createElement("div")).attr("id", this.name + "-" + varId + "-" + TempState[this.name][varId]++).addClass("macro-" + this.name).wiki(this.payload[0].contents.replace(/^\n/, "")).appendTo(this.output)
    }
});

Macro.add("numberslider", {
    handler: function() {
        function stepValidate(value) {
            if (fracDigits > 0) {
                var ma = Number(minValue + "e" + fracDigits),
                    sa = Number(stepValue + "e" + fracDigits),
                    _va = Number(value + "e" + fracDigits) - ma;
                return Number(_va - _va % sa + ma + "e-" + fracDigits)
            }
            var va = value - minValue;
            return va - va % stepValue + minValue
        }

        function validateAndApply(el) {
            var curValue = Wikifier.getValue(varName),
                newValue = Number(el.value),
                newPoolValue = null;
            if (Number.isNaN(newValue) || !Number.isFinite(newValue)) return el.value = curValue, !1;
            if (newValue = stepValidate(newValue), newValue < minValue ? newValue = minValue : newValue > maxValue && (newValue = maxValue), null !== pool)
                if (fracDigits > 0) {
                    var pa = Number(pool.get() + "e" + fracDigits),
                        ca = Number(curValue + "e" + fracDigits),
                        na = Number(newValue + "e" + fracDigits),
                        delta = na - ca;
                    pa < delta && (na -= delta - pa, delta = na - ca, newValue = Number(na + "e-" + fracDigits)), newPoolValue = Number(pa - delta + "e-" + fracDigits)
                } else {
                    var poolValue = pool.get(),
                        _delta = newValue - curValue;
                    poolValue < _delta && (newValue -= _delta - poolValue, _delta = newValue - curValue), newPoolValue = poolValue - _delta
                }
            return Wikifier.setValue(varName, newValue), el.value = newValue, null !== newPoolValue && pool.set(newPoolValue), !0
        }
        var _this2 = this;
        if (this.args.length < 5) {
            var errors = [];
            return this.args.length < 1 && errors.push("variable name"), this.args.length < 2 && errors.push("default value"), this.args.length < 3 && errors.push("min value"), this.args.length < 4 && errors.push("max value"), this.args.length < 5 && errors.push("step value"), this.error("no " + errors.join(" or ") + " specified")
        }
        if ("string" != typeof this.args[0]) return this.error("variable name argument is not a string");
        var varName = this.args[0].trim();
        if ("$" !== varName[0] && "_" !== varName[0]) return this.error('variable name "' + this.args[0] + '" is missing its sigil ($ or _)');
        var varId = Util.slugify(varName),
            defValue = Number(this.args[1]),
            minValue = Number(this.args[2]),
            maxValue = Number(this.args[3]),
            stepValue = Number(this.args[4]),
            autofocus = this.args.length > 5 && "autofocus" === this.args[5];
        if (Number.isNaN(defValue) || !Number.isFinite(defValue)) return this.error("default value (" + this.args[1] + ") is not a number");
        if (Number.isNaN(minValue) || !Number.isFinite(minValue)) return this.error("min value (" + this.args[2] + ") is not a number");
        if (Number.isNaN(maxValue) || !Number.isFinite(maxValue)) return this.error("max value (" + this.args[3] + ") is not a number");
        if (Number.isNaN(stepValue) || !Number.isFinite(stepValue) || stepValue <= 0) return this.error("step value (" + this.args[4] + ") is not a number greater than zero");
        if (defValue < minValue) return this.error("default value (" + this.args[1] + ") is less than min value (" + this.args[2] + ")");
        if (defValue > maxValue) return this.error("default value (" + this.args[1] + ") is greater than max value (" + this.args[3] + ")");
        var fracDigits = function() {
            var str = String(stepValue),
                pos = str.lastIndexOf(".");
            return pos === -1 ? 0 : str.length - pos - 1
        }();
        if (stepValidate(maxValue) !== maxValue) return this.error("max value (" + this.args[3] + ") is not a multiple of the step value (" + this.args[4] + ") plus the min value (" + this.args[2] + ")");
        var pool = function() {
            var parent = _this2.contextSelect(function(ctx) {
                return "numberpool" === ctx.name
            });
            return null !== parent && parent.hasOwnProperty("pool") ? parent.pool : null
        }();
        Config.debug && this.debugView.modes({
            block: !0
        });
        var $elControl = jQuery(document.createElement("div")),
            $elInput = jQuery(document.createElement("input")),
            $elValue = void 0,
            showValue = void 0;
        $elControl.attr("id", this.name + "-body-" + varId).addClass("macro-" + this.name).appendTo(this.output), $elInput.attr({
            id: this.name + "-input-" + varId,
            name: this.name + "-input-" + varId,
            type: "range",
            min: minValue,
            max: maxValue,
            step: stepValue,
            tabindex: 0
        }).on("change input." + Util.slugify(this.name), function() {
            validateAndApply(this), "function" == typeof showValue && showValue()
        }).on("keypress", function(ev) {
            13 === ev.which && (ev.preventDefault(), $elInput.trigger("change"))
        }).appendTo($elControl), !Browser.isIE || Browser.ieVersion > 9 ? ($elValue = jQuery(document.createElement("span")).attr("id", this.name + "-value-" + varId).appendTo($elControl), showValue = function() {
            $elValue.text(Number($elInput.val()).toFixed(fracDigits))
        }) : $elInput.off("input." + Util.slugify(this.name)), $elInput.val(defValue), validateAndApply($elInput[0]), "function" == typeof showValue && showValue(), autofocus && ($elInput.attr("autofocus", "autofocus"), postdisplay["#autofocus:" + $elInput.attr("id")] = function(task) {
            delete postdisplay[task], setTimeout(function() {
                return $elInput.focus()
            }, Engine.minDomActionDelay)
        })
    }
});


code:
import Overworld from "./Overworld";

export default [{
    map: Overworld,
    mapID: 'overworld',
    default: true
}];


code:
import {checkForDaylightExposure} from "../Game/PlayerStates";

export default {
    background: 'img/map_placeholders/map_placeholder.png',
    locations: [{
        id: 'domus_street',
        passage: 'Domus Street',
        timePass: 5,
        x: 35,
        y: 118,
        name: 'Domus',
        prefix: 'Street',
        suffix: '',
        sprite: 'img/map_placeholders/building_placeholder.png',
        light: 'img/map_placeholders/placeholder_light.png',
        canBeVisited: () => !checkForDaylightExposure(),
        links: [
            'barb_street',
            'danube_street',
            'residential_alleyways'
        ]
    }, {
        id: 'barb_street',
        passage: 'Barb Street',
        timePass: 5,
        x: 135,
        y: 30,
        name: 'Barb',
        prefix: 'Street',
        suffix: '',
        sprite: 'img/map_placeholders/building_placeholder.png',
        light: 'img/map_placeholders/placeholder_light.png',
        canBeVisited: () => !checkForDaylightExposure(),
        links: [
            'domus_street',
            'connudatus_street',
            'residential_alleyways'
        ],
        active: {
            x: 15,
            y: 15,
            width: 15,
            height: 15
        }
    }, {
        id: 'residential_alleyways',
        passage: 'Residential alleyways',
        timePass: 5,
        x: 115,
        y: 110,
        name: 'Residential Alleyways',
        prefix: '',
        sprite: 'img/map_placeholders/building_placeholder.png',
        light: 'img/map_placeholders/placeholder_light.png',
        links: [
            'domus_street',
            'barb_street',
            'danube_street',
            'connudatus_street'
        ]
    }, {
        id: 'danube_street',
        passage: 'Danube Street',
        timePass: 5,
        x: 137,
        y: 207,
        name: 'Danube',
        prefix: 'Street',
        suffix: '',
        sprite: 'img/map_placeholders/building_placeholder.png',
        light: 'img/map_placeholders/placeholder_light.png',
        canBeVisited: () => !checkForDaylightExposure(),
        links: [
            'domus_street',
            'connudatus_street',
            'residential_alleyways'
        ]
    }, {
        id: 'connudatus_street',
        passage: 'Connudatus Street',
        timePass: 5,
        x: 210,
        y: 100,
        name: 'Connudatus',
        prefix: 'Street',
        suffix: '',
        sprite: 'img/map_placeholders/building_placeholder.png',
        light: 'img/map_placeholders/placeholder_light.png',
        canBeVisited: () => !checkForDaylightExposure(),
        links: [
            'barb_street',
            'danube_street',
            'residential_alleyways'
        ]
    }] /* First cluster of 5 */
};


code:
export const GameVersion = '1.26.1';

// Save update function
export default (variables) => {

};

export const GameState = {
    settings: {
        text_only: false, // $textonly
        enable_images: true, // $images
        debug: true, // $debug
        numberify: true, // $numberify_enable
        devmode: false, // $dev
        gamemode: "normal", /// $gamemode

        intro: true, // $intro
        tutorial: false, // $tutorial

        content: {
            // content toggles here
        }
    },

    player: {
        skills: {
            seduction: 0,
            vaginal: 0,
            penile: 0,
            anal: 0,
            oral: 0,
            hand: 0,
            feet: 0,
            chest: 0,
            thigh: 0,
            bottom: 0,
        },
        stats: {
            control: {
                max: 1000, // $controlmax
                current: 1000 // $control
            },
            trauma: {
                max: 5000, //$traumamax
                current: 0 // $trauma
            },
            stress: {
                max: 10010, // $stressmax
                current: 0 // $stress
            },
            physique: {
                max: 20000, // $physiquemax
                current: 3500 // $physique
            },
            arousal: {
                max: 10000, // $arousalmax
                current: 0 // $arousal ?
            },
            pain: {
                max: 1000, // $painmax ?
                current: 0 // $pain
            },
            fatigue: {
                max: 2000, // $tirednessmax
                current: 0 // $tiredness
            },
            beauty: {
                max: 10000, // $beautymax
                current: 100 // $beauty
            }
        },
        special_stats: {
            purity: 1000, // $purity
            submissive: 1000, // $submissive
            assertive: 0, // $assertive
            assertive_action: "trauma", // $assertiveaction
            awareness: {
                current: 0, // $awareness
                level: 0 // $awarelevel
            },
            fame: 0, // $fame
            fame_exhibitionism: 0, // $fameexhibitionism
            fame_prostitution: 0, // $fameprostitution
            fame_bestiality: 0, // $famebestiality
            fame_sex: 0, // $famesex
            fame_rape: 0, // $famerape
            fame_good: 0, // $famegood
            fame_business: 0, // $famebusiness
            fame_park: 0, // $famepark

            transformation: {
                wolf: {
                    build: 0, // $wolfbuild
                    girl: false // $wolfgirl
                }
            },

            oxygen: {
                max: 1200, // $oxygenmax
                current: 1200 // $oxygen
            }
        },
        possesions: {
            money: 500, // $money
            items: {
                clothing: {

                }
            }
        },
        looks: {
            development_level: 0, // $devlevel
            gender_appearance: '', // $playergenderappearance

            hair: {
                length: 200, // $hairlength
                color: '', // $haircolor
            }
        },
        school: {
            deliquency: 10, /// $deliquency

            cool: 120, // $cool
            cool_max: 400, // $coolmax

            school: 800, // $school

            math: 200, // $maths
            english: 200, // $english
            science: 200, // $science
            history: 200 // $history
        },
        data: {
            birth_month: "september", // $birthmonth
            birth_day: 3, // $birthday
            name: "It's you.",
            gender: 'f', // $playergender
            background: 'waif', // $background
            breast_size: '', // $breastsize
        }
    },

    location: {
        time: 420, // $time
        day: 0, // $days
        week_day: 1, // $weekday
        month: "september", // $month
        month_day: 4, // $monthday
        year: 2018, // $year
        map_id: 'overworld'
    },

    state: {
        combat: false, // $combat
        skill_level_up: [], // contains the name of the skill that leveled up so things can display after combat

        water: false, // if in water - $water
        underwater_time: 0, // $underwatertime
        underwater: 0, // if underwater - $underwater

        player: {
            mind: {
                /*Disables you if 1.*/
                panic_paralysis: false,
                panic_violence: false,
                trance: false,
                /*Builds during consensual, provides benefit at end.*/
                assertive: false,
                /*Set to 1 if NPC is successfully asked not to go ham on the specified body part.*/
                no_vaginal: false,
                no_anal: false,
                no_penile: false,
                /*Trauma and stress at encounter's start.*/
                trauma_saved: 0,
                stress_saved: 0,
                /*Trauma and stress gained during encounter.*/
                trauma_gain: 0,
                stress_gain: 0,
            },

            body: {
                /*Set to a string indicating which body part it is occupied by, such as "penis" or "lefthand"*/
                vagina_use: 0,
                penis_use: 0,
                mouth_use: 0,
                anus_use: 0,
                thigh_use: 0,
                bottom_use: 0,
                feet_use: 0,
                left_arm: 0,
                right_arm: 0,
                chest_use: 0,
                head: 0,
                left_let: 0,
                right_leg: 0,
                breast_use: 0,
                left_nipple: 0,
                right_nipple: 0,
                /*Set to a string indicating both which body part it is occupied by, and the type of "hold" that body part has on in. Such as "anusentrance" or "anusimminent"*/
                vagina_state: 0,
                penis_state: 0,
                anus_state: 0,
                mouth_state: 0,
                left_arm_state: 0,
                right_arm_state: 0,
                feet_state: 0,
                /*More stuff I hope is self-explanatory*/
                penis_fucked: false,
                vagina_fucked: false,
                pull_away: false,
                orgasm_current: 0,
                water_wash: 0,
            }
        },

        encounter: {
            state: {
                /*Makes actions easier.*/
                enemy_trust: 0,
                /*Encounter ends if this reaches 0.*/
                enemy_health: 0,
                enemy_health_max: 0,
                /*Encounter ends if this reaches max.*/
                enemy_arousal: 0,
                enemy_arousal_max: 0,
                /*Impacts NPC behaviour during encounters.*/
                enemy_anger: 0,
                enemy_anger_max: 0,
            },

            audience: {
                /*This is the audience for regular encounters, and is separate to dance audiences.*/
                /*What NPCs are in the audience.*/
                audience_camera: [
                    // description of the person
                ],/*audience_camera_2 - 6 too*/
                /*Forces all the audience to be boys.*/
                audience_force_boy: false,
                /*Forces all the audience to be girls.*/
                audience_force_girl: false,
            }
        }
    },

    playthrough_stats: {
        rape: 0, // $rapestat
        beast_rape: 0, // $beastrapestat
        tentacle_rape: 0, // $tentaclerapestat
        molested: 0, // $moleststat

        vaginal: 0, // $vaginalstat
        vaginal_ejac: 0, // $vaginalejacstat
        vaginal_entrance_ejac: 0, // $vaginalentranceejacstat
        anal: 0, // $analstat
        anal_ejac: 0, // $analejacstat
        oral: 0, // $oralstat
        oral_ejac: 0, // $oralejacstat
        hand: 0, // $ohandstat
        hand_ejac: 0, // $handejacstat
        feet: 0, // $feetstat
        feet_ejac: 0, // $feetejacstat
        thigh: 0, // $thighstat
        thigh_ejac: 0, // $thighejacstat
        bottom: 0, // $bottomstat
        bottom_ejac: 0, // $bottomejacstat
        penile: 0, // $penilestat
        penile_ejac: 0, // $penileejacstat
        face_ejac: 0, // $faceejacstat
        chest: 0, // $cheststat
        chest_ejac: 0, // $chestejacstat
        hair_ejac: 0, // $hairyejacstat
        tummy_ejac: 0, // $tummyejacstat
        neck_ejac: 0, // $neckejacstat
        ejac: 0, // $ejacstat
        orgasm: 0, // $orgasmstat

        clothes_stripped: 0, // $clothesstripstat
        clothes_ruined: 0, // $clothesruinstat

        hit: 0, // $hitstat
        attack: 0, // $attackstat

        prostitution: 0, // $prostitutionstat
        forced_prostitution: 0, // $forcedprostitutionstat

        tables_served: 0, // $tablesservedstat
        parasite: 0, // $parasitestat
        pass_out: 0, // $passoutstat

        masturbation: 0, // $masturbationstat
        masturbation_orgasm: 0, // $masturbationorgasmstat
        masturbation_time: 0, // $masturbationtimestat

        dance: 0, // $dancestat
        pull_away: 0, // $pullaway

        no_vaginal: 0, // $novaginal
        no_anal: 0, // $noanal
        no_penile: 0, // $nopenile

        vaginal_chastity: 0, // $vaginalchastity
        anal_chastity: 0, // $analchastity
        penile_chastity: 0, // $penilechastity
        drinks_served: 0, // $drinksservedstat
    }
};

export const NpcShared = {
    state: {
        npc: null, // npc name
        type: null, // npc type "man", "beast", "swarm", "parasite", "tentacle"
        strength: 0, // unused for now
    },
    specific_speech: {
        /*Speech variables that disappear after the current NPC's turn, meaning other NPCs won't use them.*/
        /* I don't even WANNA touch this shit */
        speech_orgasm_weak_cum_count: 0,
        speech_orgasm_no_cum_count: 0,
        speech_orgasm_count: 0,
        speech_orgasm_repeat: 0,
        speech_crossdress_angry: 0,
        speech_crossdress_aroused: 0,
        speech_crosdress_shock: 0,
        speech_crossdress_disappointed: 0,
        speech_cum: 0,
        speech_penis_penetrated: 0,
        speech_vagina_penetrated: 0,
        speech_anus_penetrated: 0,
        speech_mouth_penetrated: 0,
        speech_other_anus_penetrated: 0,
        speech_vagina_imminent: 0,
        speech_penis_imminent: 0,
        speech_anus_imminent: 0,
        speech_mouth_imminent: 0,
        speech_other_anus_imminent: 0,
        speech_vagina_entrance: 0,
        speech_penis_entrance: 0,
        speech_anus_entrance: 0,
        speech_mouth_entrance: 0,
        speech_other_anus_entrance: 0,
        speech_vagina_withhold: 0,
        speech_anus_withhold: 0,
        speech_penis_withold: 0,
        speech_other_anus_withold: 0,
        speech_vagina: 0,
        speech_penis: 0,
        speech_anus: 0,
        speech_vagina_mouth: 0,
        speech_vagina_vagina: 0,
        speech_beat: 0,
        speech_hit: 0,
        speech_throat: 0,
        speech_vagina_foot: 0,
        speech_penis_foot: 0,
        speech_chastity: 0,
        speech_struggle: 0,
        speech_strip_struggle: 0,
        speech_spank: 0,
        speech_arms: 0,
        speech_clit: 0,
        speech_glans: 0,
        speech_bottom: 0,
        speech_hair: 0,
        speech_chest_rub: 0,
        speech_breast_rub: 0,
        speech_vagina_flaunt: 0,
        speech_vagina_virgin: 0,
        speech_anus_virgin: 0,
        speech_mouth_virgin: 0,
        speech_penis_virgin: 0,
        speech_penis_escape: 0,
        speech_vagina_escape: 0,
        speech_anus_escape: 0,
    }
};

export const NpcHuman = {
    state: {
        type: "man",
    },
    person: {
        /*These all have five additional versions, the same but with a number on the end, 2 - 6. One for each NPC "slot" in scenes and combat. Note that the first instance sometimes has the number 1 on the end, and sometimes does not.*/
        /*Controls correct grammar and capitalisation during encounters.*/
        intro: false,
        /*What their body parts are occupied by. Note this is different from, but will impact, the actions they take.*/
        lefthand: false,
        righthand: false,
        penis: false,
        vagina: false,
        mouth: false,
        /*NPC gender, pronouns and description, like "muscular" or "thin".*/
        gender: 'f',
        pronoun: 'thin',
        description: null,
    }
};

export const NpcBeast = {
    state: {
        type: "beast"
    },
    beast: {
        /*How many beasts are waiting their turn, or having their turn.*/
        beast_no: 0,
        /*How many beasts in the encounter total.*/
        beast_nomax: 0,
        /*Type of beast. Dog, wolf etc.*/
        beast_name: "none",
        /*If they have claws.*/
        claws: 0,
        /*Currently active beast's gender and pronouns.*/
        data: [/*{
            gender: 'f', // beastgender
            pronoun: 'x' // beastpronoun
        }*/]
    }
};

export const NpcSwarm = {
    state: {
        type: "swarm"
    },
    swarm: {
        swarm: [],/*Also swarm2, swarm3...up to swarm10*/
        swarm_pending: 0,
        /*Set at start of encounter*/
        swarm_steady: 0,
        swarm_Steady: 0,
        swarm_name: 0,
        swarm_move: 0,
        swarm_active: 0,
        swarm_creature: 0,
        swarm_count: 0,
        /*I tried something different with the swarms, having each action they could take associated with a variable, which would then be used to output text elsewhere*/
        swarm_chest_molest: 0,
        swarm_chest_grab: 0,
        swarm_chest_grab_intro: 0,
        swarm_chest_grab_clothes: 0,
        swarm_chest_clothed: 0,
        swarm_chest_covered: 0,
        swarm_activate: 0,
        swarm_spill: 0,
        swarm_back: 0,
        swarm_back_molest: 0,
        swarm_back_grab: 0,
        swarm_back_grab_intro: 0,
        swarm_back_grab_under_clothed: 0,
        swarm_back_under_clothed: 0,
        swarm_back_grab_lower_clothed: 0,
        swarm_back_lower_clothed: 0,
        swarm_back_cover: 0,
        swarm_back_inside: 0,
        swarm_back_inside_intro: 0,
        swarm_back_grab_lower_chastity: 0,
        swarm_front: 0,
        swarm_front_molest: 0,
        swarm_front_grab: 0,
        swarm_front_grab_intro: 0,
        swarm_front_grab_under_clothed: 0,
        swarm_front_under_clothed: 0,
        swarm_front_grab_lower_clothed: 0,
        swarm_front_lower_clothed: 0,
        swarm_front_cover: 0,
        swarm_front_inside: 0,
        swarm_front_inside_intro: 0,
        swarm_front_grab_lower_chastity: 0,
        front: 0,
        back: 0,
        chest: 0,
    }
};

export const NpcVore = {
    state: {
        type: "swarm"
    },
    vore: {
        /*Set at encounter's start.*/
        vore_strength: 0,
        /*How hard you struggle.*/
        vore_struggle: 0,
        /*If the encounter has tentacles.*/
        vore_tentacles: false,
        /*What part of the beast's digestive system you're in.*/
        vore_stage: 0,
        /*Name of creature.*/
        vore_creature: null,
        /*Set to 1 if swallowed, reset at end of encounter.*/
        swallowed: false,

    }
};

export const NpcTentacles = {
    state: {
        type: "tentacles"
    },
    tentacles: {
        /*Set at encounter's start*/
        /*Total number of tentacles.*/
        tentacle_no: 0,
        /*Number of active tentacles.*/
        active_tentacle_no: 0,
        /*How much health each tentacle has. Set at start.*/
        tentacle_health: 0,
        /*There alternative versions of the following, with the number 1 replaced with 2 - 16, covering each possible tentacle*/
        /*What each tentacle is doing.*/
        tentacle: [/*{
            description: null,
            shaft: false,
            head: false,
            health: 0
        }*/],
    }
};


code:
import Migration from "./Versions";
import Game from "../Game/Game";

Config.saves.onLoad = (save) => {
    let saveGameVersion = save.state.history[save.state.history.length-1].variables._gameVersion;
    if (typeof saveGameVersion === "undefined")
        saveGameVersion = '1.26.1'; // any saves before this need to be handled via the old game version

    if (saveGameVersion !== Game.getGameVersion()) {
        console.log(`Trying to update game save from ${saveGameVersion} to ${Game.getGameVersion()}`);

        while (true) {
            const major = saveGameVersion.split('.')[0];
            const minor = saveGameVersion.split('.')[1];
            const patch = saveGameVersion.split('.')[2];

            if (typeof Migration[major] === "undefined")
                break;

            if (typeof Migration[major][minor] === "undefined")
                break;

            if (typeof Migration[major][minor][patch] === "undefined")
                break;

            saveGameVersion = Migration[major][minor][patch](save.state.history[save.state.history.length-1].variables);
            if (saveGameVersion === Game.getGameVersion())
                break; // on current version
        }

        save.state.history[save.state.history.length-1].variables._gameVersion = saveGameVersion;

        if (saveGameVersion !== Game.getGameVersion())
            console.warn(`Reported game save version is ${saveGameVersion}, while the game version is ${Game.getGameVersion()}, some errors can occur.`);
    } // no need to update when version matches already boi
};


code:
import V1_26_1 from "./1/26/1"

export default {
    '1': {
        '26': {
            '1': V1_26_1
        }
    }
};

// current game data for new saves here
export {GameState, GameVersion, NpcShared, NpcHuman, NpcBeast, NpcSwarm, NpcTentacles, NpcVore} from "./1/26/1";


code:
import Game from "./Game/Game";

const instance = new Game();


description: App for degrees-of-lewdity

build (dev): webpack

build: webpack --env.mode=production

compile: cd .. && compile.bat

license: SEE LICENSE IN LICENSE.md


code:
const path = require('path');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

module.exports = (env) => {
    const webpackMode = (typeof env === "undefined" ? 'development' : (
        typeof env.mode === "string" ? env.mode : 'development'
    ));

    let options = {
        mode: webpackMode,
        entry: [
            'babel-polyfill',
            './index.js'
        ],
        output: {
            path: path.resolve(__dirname, '../game'),
            filename: 'base.js'
        },
        module: {
            rules: [{
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: [
                            '@babel/plugin-proposal-object-rest-spread',
                            '@babel/plugin-proposal-class-properties',
                            'lodash'
                        ]
                    }
                }
            }]
        },
        plugins: [
            new LodashModuleReplacementPlugin()
        ]
    };

    if (webpackMode === 'development')
        options['devtool'] = 'inline-source-map';

    return options;
};


code:
// Settings
$textonly $settings.text_only
$images $settings.enable_images
$debug $settings.debug
$numberify_enable $settings.numberify
$dev $settings.devmode
$gamemode $settings.gamemode

// Initial settings
$intro $settings.intro
$tutorial $settings.tutorial

// Location / time
$time $location.time
$days $location.day
$weekday $location.week_day
$month $location.month
$monthday $location.month_day
$year $location.year

// Player looks
$hairlength $player.looks.hair.length
$playergenderappearance $player.looks.gender_appearance
$haircolor $player.looks.hair.color

// Player information
$playergender $player.data.gender
$background $player.data.background
$birthmonth $player.data.birth_month
$birthday $player.data.birth_day
$breastsize $player.data.breast_size

// Stats
$controlmax $player.stats.control.max
$control $player.stats.control.current
$traumamax $player.stats.trauma.max
$trauma $player.stats.trauma.current
$stressmax $player.stats.stress.max
$stress $player.stats.stress.current
$physiquemax $player.stats.physique.max
$physique $player.stats.physique.current
$pain $player.stats.pain.current
$tirednessmax $player.stats.fatigue.max
$tiredness $player.stats.fatigue.current
$beautymax $player.stats.beauty.max
$beauty $player.stats.beauty.current

// Special stats
$purity $player.special_stats.purity
$submissive $player.special_stats.submissive
$assertive $player.special_stats.assertive
$assertiveaction $player.special_stats.assertive_action
$awareness $player.special_stats.awareness.current
$awarelevel $player.special_stats.awareness.level
$fame $player.special_stats.fame
$fameexhibitionism $player.special_stats.fame_exhibitionism
$fameprostitution $player.special_stats.fame_prostitution
$famebestiality $player.special_stats.fame_bestiality
$famesex $player.special_stats.fame_sex
$famerape $player.special_stats.fame_rape
$famegood $player.special_stats.fame_good
$famebusiness $player.special_stats.fame_business
$famepark $player.special_stats.fame_park

$oxygenmax $player.special_stats.oxygen.max
$oxygen $player.special_stats.oxygen.current

// Transformations
$wolfbuild $player.special_stats.transformation.wolf.build
$wolfgirl $player.special_stats.transformation.wolf.girl


code:
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain

repo_name: SpitfireSmoke/rimworld

path: .gitattributes

license: unknown


code:
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb
/Source/Patches/PawnAnimationPatches/HarmonyPatch_Pawn_DrawTracker.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRotation.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRenderer.cs
/Source/Patches/OtherModPatches/HarmonyPatch_ShowHairWithHats.cs
/Source/Patches/OtherModPatches/HarmonyPatch_FacialAnimation.cs
/Source/Patches/OtherModPatches/HarmonyPatch_DontShaveYourHead.cs
/Source/Patches/OtherModPatches/HarmonyPatch_CSL.cs
/Source/Patches/OtherModPatches/HarmonyPatch_AlienRace.cs
/Source/Patches/ThingAnimationPatches/HarmonyPatch_ThingDrawAt.cs
/Defs/AnimationDefs/Animations_SexToys.xml
/1.5/Defs/AnimationDefs/BasicBestiality/HumanRotatedOffset.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality4v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality3v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality2v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality1v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/!BasicBestiality.xml
/1.5/Defs/TestDoNotPush
/1.5/Textures/AnimationProps/BEV

path: .gitignore


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Banana</defName>
	</PawnRenderNodeTagDef>

	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Banana</defName>
		<animPropProperties>
			<debugLabel>Banana</debugLabel>
			<tagDef>RenderNodeTag_Banana</tagDef>
			<parentTagDef>Body</parentTagDef>
			<texPath>AnimationProps/Banana/Banana</texPath>
			<overlayLayer>Head</overlayLayer>
			<baseLayer>95</baseLayer>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Hand</defName>
	</PawnRenderNodeTagDef>

	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Hand</defName>
		<animPropProperties Class="Rimworld_Animations.PawnRenderNodeProperties_GraphicVariants">
			<debugLabel>Hand</debugLabel>
			<nodeClass>Rimworld_Animations.PawnRenderNode_GraphicVariants</nodeClass>
			<workerClass>Rimworld_Animations.PawnRenderNodeWorker_GraphicVariants</workerClass>
			<tagDef>RenderNodeTag_Hand</tagDef>
			<absoluteTransform>True</absoluteTransform>
			<parentTagDef>Root</parentTagDef>
			<texPath>AnimationProps/Hand/Hand</texPath>
			<!-- for height -->
			<overlayLayer>Head</overlayLayer>
			<useSkinShader>True</useSkinShader>
			<linkedBodyPartsGroup>LeftHand</linkedBodyPartsGroup>
			<colorType>Skin</colorType>
			<baseLayer>95</baseLayer>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Knees</defName>
	</PawnRenderNodeTagDef>

	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Knees</defName>
		<animPropProperties Class="Rimworld_Animations.PawnRenderNodeProperties_BodyTypeVariants">
			<debugLabel>Knees</debugLabel>
			<nodeClass>Rimworld_Animations.PawnRenderNode_BodyTypeVariants</nodeClass>
			<workerClass>Rimworld_Animations.PawnRenderNodeWorker_BodyTypeVariants</workerClass>
			<tagDef>RenderNodeTag_Knees</tagDef>
			<absoluteTransform>False</absoluteTransform>
			<parentTagDef>Root</parentTagDef>

			<overlayLayer>Head</overlayLayer>
			<useSkinShader>True</useSkinShader>
			<colorType>Skin</colorType>
			<baseLayer>95</baseLayer>
			<bodyTypeVariantsDef>

					<bodyType>Female</bodyType>
					<texPathVariantsDef>TexPathVariants_Knees</texPathVariantsDef>

			</bodyTypeVariantsDef>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>

	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Arms</defName>
	</PawnRenderNodeTagDef>

	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Arms</defName>
		<animPropProperties Class="Rimworld_Animations.PawnRenderNodeProperties_BodyTypeVariants">
			<debugLabel>Arms</debugLabel>
			<nodeClass>Rimworld_Animations.PawnRenderNode_BodyTypeVariants</nodeClass>
			<workerClass>Rimworld_Animations.PawnRenderNodeWorker_BodyTypeVariants</workerClass>
			<tagDef>RenderNodeTag_Knees</tagDef>
			<absoluteTransform>False</absoluteTransform>
			<parentTagDef>Root</parentTagDef>
			<useSkinShader>True</useSkinShader>
			<colorType>Skin</colorType>

			<overlayLayer>Body</overlayLayer>
			<baseLayer>-1</baseLayer>
			<bodyTypeVariantsDef>

					<bodyType>Female</bodyType>
					<texPathVariantsDef>TexPathVariants_Arms</texPathVariantsDef>

			</bodyTypeVariantsDef>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>

	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Xray_Inside</defName>
	</PawnRenderNodeTagDef>

	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Xray_Inside</defName>
		<animPropProperties Class="Rimworld_Animations.PawnRenderNodeProperties_GraphicVariants">
			<debugLabel>Xray Inside</debugLabel>
			<nodeClass>Rimworld_Animations.PawnRenderNode_GraphicVariants</nodeClass>
			<workerClass>Rimworld_Animations.PawnRenderNodeWorker_GraphicVariants</workerClass>
			<tagDef>RenderNodeTag_Xray_Inside</tagDef>
			<absoluteTransform>True</absoluteTransform>
			<parentTagDef>Body</parentTagDef>
			<!-- for height -->
			<overlayLayer>Head</overlayLayer>
			<baseLayer>95</baseLayer>
			<texPathVariantsDef>TexPathVariants_Xray_Inside</texPathVariantsDef>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<PawnRenderNodeTagDef>
		<defName>RenderNodeTag_Xray_Penis</defName>
	</PawnRenderNodeTagDef>
	<Rimworld_Animations.AnimationPropDef>
		<defName>AnimationProp_Penis</defName>
		<animPropProperties Class="Rimworld_Animations.PawnRenderNodeProperties_GraphicHediffVariants">
			<debugLabel>Xray Penis</debugLabel>
			<nodeClass>Rimworld_Animations.PawnRenderNode_GraphicHediffVariants</nodeClass>
			<workerClass>Rimworld_Animations.PawnRenderNodeWorker_GraphicHediffVariants</workerClass>
			<tagDef>RenderNodeTag_Xray_Penis</tagDef>
			<parentTagDef>Body</parentTagDef>
			<texPath>AnimationProps/Banana/Banana</texPath>

			<overlayLayer>Head</overlayLayer>
			<baseLayer>95</baseLayer>
			<absoluteTransform>True</absoluteTransform>

			<texPathVariantsDef>TexPathVariants_XrayPenis_Human</texPathVariantsDef>

			<hediffVariants>

					<hediffs>

HorsePenis

RaccoonPenis

					</hediffs>
					<texPathVariantsDef>TexPathVariants_XrayPenis_Horse</texPathVariantsDef>

			</hediffVariants>
		</animPropProperties>
	</Rimworld_Animations.AnimationPropDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<Rimworld_Animations.GroupAnimationDef>
		<defName>GroupAnimation_DogBeast</defName>
		<numActors>2</numActors>
		<animationStages>

				<loops>1</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human1

DogBeast_Dog1

						</animationDefs>

				</loopOptions>

				<loops>1</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human2

DogBeast_Dog2

						</animationDefs>

				</loopOptions>

				<loops>60</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human3_1

DogBeast_Dog3_1

						</animationDefs>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human3_2

DogBeast_Dog3_2

						</animationDefs>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human3_3

DogBeast_Dog3_3

						</animationDefs>

				</loopOptions>

				<loops>1</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human4

DogBeast_Dog4

						</animationDefs>

				</loopOptions>

				<loops>30</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human5_1

DogBeast_Dog5_1

						</animationDefs>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human5_2

DogBeast_Dog5_2

						</animationDefs>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human5_3

DogBeast_Dog5_3

						</animationDefs>

				</loopOptions>

				<loops>1</loops>
				<loopOptions>

						<probability>1</probability>
						<animationDefs>

DogBeast_Human6

DogBeast_Dog6

						</animationDefs>

				</loopOptions>

		</animationStages>

		<contexts>

				<priority>1</priority>
				<actorShift>1</actorShift>
				<whitelist>

						<tests>

								<races>

Wolf_Timber

Wolf_Arctic

Warg

Husky

LabradorRetriever

								</races>

						</tests>

				</whitelist>
				<interactionDefs>

Bestiality_Vaginal

Bestiality_Anal

Bestiality_Double_Penetration_M

				</interactionDefs>

				<priority>1</priority>
				<whitelist>

						<tests>

								<races>

Wolf_Timber

Wolf_Arctic

Warg

Husky

LabradorRetriever

								</races>

						</tests>

				</whitelist>
				<interactionDefs>

Bestiality_Reverse_Vaginal

Bestiality_Reverse_Anal

Bestiality_Reverse_Double_Penetration_M

				</interactionDefs>

		</contexts>

		<offsetDefs>

Offset_Placeholder

Offset_Placeholder

		</offsetDefs>

	</Rimworld_Animations.GroupAnimationDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
  <AnimationDef>
    <defName>DogBeast_Human1</defName>
    <durationTicks>85</durationTicks>
    <animationParts>

        <key>Root</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>4</angle>
              <visible>true</visible>
              <offset>(-0.3716815, 0, 0.2713863)</offset>
              <rotation>East</rotation>

              <tick>15</tick>
              <angle>8</angle>
              <visible>true</visible>
              <offset>(-0.3539823, 0, 0.2654868)</offset>
              <rotation>East</rotation>

              <tick>25</tick>
              <angle>-14</angle>
              <visible>true</visible>
              <offset>(-0.4011799, 0, 0.2241887)</offset>
              <rotation>East</rotation>

              <tick>35</tick>
              <angle>-7</angle>
              <visible>true</visible>
              <offset>(-0.3834807, 0, 0.2359883)</offset>
              <rotation>East</rotation>

              <tick>55</tick>
              <angle>-9</angle>
              <visible>true</visible>
              <offset>(-0.4011799, 0, 0.2359883)</offset>
              <rotation>East</rotation>

              <tick>85</tick>
              <angle>-7</angle>
              <visible>true</visible>
              <offset>(-0.3893807, 0, 0.2359883)</offset>
              <rotation>East</rotation>

          </keyframes>
        </value>

        <key>Head</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>East</rotation>

              <tick>25</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>South</rotation>

          </keyframes>
        </value>

        <key>RenderNodeTag_Hand</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.1946907, 1, 0.4483777)</offset>
              <rotation>North</rotation>

              <tick>5</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.235988, 1, 0.4365782)</offset>
              <rotation>North</rotation>

              <tick>10</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.1238946, 1, 0.377581)</offset>
              <rotation>North</rotation>

              <tick>13</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.1710922, 1, 0.3952804)</offset>
              <rotation>North</rotation>

              <tick>17</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.05899817, 1, 0.4660767)</offset>
              <rotation>North</rotation>

              <tick>25</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(-0.1828904, 1, 0.4896755)</offset>
              <rotation>North</rotation>

              <tick>35</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(-0.1356928, 1, 0.4778759)</offset>
              <rotation>North</rotation>

          </keyframes>
        </value>

    </animationParts>
  </AnimationDef>
  <AnimationDef>
    <defName>DogBeast_Dog1</defName>
    <durationTicks>85</durationTicks>
    <animationParts>

        <key>Root</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0.6666666, 1, 0.1120945)</offset>
              <rotation>West</rotation>

              <tick>15</tick>
              <angle>-5</angle>
              <visible>true</visible>
              <offset>(0.5722714, 1, 0.06489658)</offset>
              <scale>(1, 1, 0.9351034)</scale>
              <rotation>West</rotation>

              <tick>25</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <scale>(0.9999999, 1, 1.053097)</scale>
              <rotation>West</rotation>

              <tick>35</tick>
              <angle>31</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>45</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>55</tick>
              <angle>31</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>65</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>75</tick>
              <angle>31</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>85</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

          </keyframes>
        </value>

        <key>Head</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>North</rotation>

          </keyframes>
        </value>

        <key>AnimalPenis</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0, -2, 0)</offset>
              <rotation>North</rotation>

          </keyframes>
        </value>

    </animationParts>
  </AnimationDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
  <AnimationDef>
    <defName>DogBeast_Human2</defName>
    <durationTicks>105</durationTicks>
    <animationParts>

        <key>Root</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>-7</angle>
              <visible>true</visible>
              <offset>(-0.3893807, 0, 0.2359883)</offset>
              <rotation>East</rotation>

              <tick>19</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(-0.5073746, 0, 0.2300885)</offset>
              <rotation>South</rotation>

              <tick>25</tick>
              <angle>-13</angle>
              <visible>true</visible>
              <offset>(-0.5899706, 0, 0.2005899)</offset>
              <rotation>West</rotation>

              <tick>45</tick>
              <angle>-65</angle>
              <visible>true</visible>
              <offset>(-0.737463, 0, 0.02359879)</offset>
              <scale>(0.9764012, 1, 1)</scale>
              <rotation>West</rotation>

              <tick>55</tick>
              <angle>-52</angle>
              <visible>true</visible>
              <offset>(-0.6843658, 0, 0.08849573)</offset>
              <rotation>West</rotation>

              <tick>75</tick>
              <angle>-56</angle>
              <visible>true</visible>
              <offset>(-0.702065, 0, 0.07079637)</offset>
              <rotation>West</rotation>

              <tick>85</tick>
              <angle>-56</angle>
              <visible>true</visible>
              <offset>(-0.702065, 0, 0.07079637)</offset>
              <rotation>West</rotation>

              <tick>90</tick>
              <angle>-46</angle>
              <visible>true</visible>
              <offset>(-0.743363, 0, 0.1474925)</offset>
              <rotation>West</rotation>
              <voice>Grunt</voice>

              <tick>95</tick>
              <angle>-48</angle>
              <visible>true</visible>
              <offset>(-0.7315634, 0, 0.1415931)</offset>
              <rotation>West</rotation>

              <tick>100</tick>
              <angle>-45</angle>
              <visible>true</visible>
              <offset>(-0.737463, 0, 0.1533923)</offset>
              <rotation>West</rotation>

              <tick>105</tick>
              <angle>-52</angle>
              <visible>true</visible>
              <offset>(-0.7197642, 0, 0.1120945)</offset>
              <rotation>West</rotation>

          </keyframes>
        </value>

        <key>Head</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>South</rotation>

              <tick>20</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>South</rotation>

              <tick>90</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>West</rotation>

          </keyframes>
        </value>

        <key>RenderNodeTag_Hand</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(-0.1356928, 1, 0.4778759)</offset>
              <rotation>North</rotation>

              <tick>20</tick>
              <angle>0</angle>
              <visible>false</visible>
              <offset>(-0.5309731, -2, -0.06489676)</offset>
              <rotation>North</rotation>

          </keyframes>
        </value>

    </animationParts>
  </AnimationDef>
  <AnimationDef>
    <defName>DogBeast_Dog2</defName>
    <durationTicks>105</durationTicks>
    <animationParts>

        <key>Root</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>10</tick>
              <angle>39</angle>
              <visible>true</visible>
              <offset>(0.1651919, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>20</tick>
              <angle>35</angle>
              <visible>true</visible>
              <offset>(0.1356932, 1, 0.2477875)</offset>
              <rotation>West</rotation>

              <tick>30</tick>
              <angle>29</angle>
              <visible>true</visible>
              <offset>(-0.01769918, 1, 0.259587)</offset>
              <rotation>West</rotation>

              <tick>40</tick>
              <angle>24</angle>
              <visible>true</visible>
              <offset>(-0.07079639, 1, 0.2300885)</offset>
              <scale>(1, 1, 0.9528024)</scale>
              <rotation>West</rotation>

              <tick>50</tick>
              <angle>33</angle>
              <visible>true</visible>
              <offset>(-0.117994, 1, 0.3008848)</offset>
              <rotation>West</rotation>

              <tick>55</tick>
              <angle>28</angle>
              <visible>true</visible>
              <offset>(-0.06489678, 1, 0.2713863)</offset>
              <rotation>West</rotation>

              <tick>60</tick>
              <angle>33</angle>
              <visible>true</visible>
              <offset>(-0.1238939, 1, 0.2772861)</offset>
              <rotation>West</rotation>

              <tick>65</tick>
              <angle>28</angle>
              <visible>true</visible>
              <offset>(-0.06489678, 1, 0.259587)</offset>
              <rotation>West</rotation>

              <tick>70</tick>
              <angle>36</angle>
              <visible>true</visible>
              <offset>(-0.1769911, 1, 0.3008848)</offset>
              <rotation>West</rotation>

              <tick>75</tick>
              <angle>33</angle>
              <visible>true</visible>
              <offset>(-0.1651916, 1, 0.3008848)</offset>
              <rotation>West</rotation>

              <tick>80</tick>
              <angle>33</angle>
              <visible>true</visible>
              <offset>(-0.1415928, 1, 0.2890856)</offset>
              <rotation>West</rotation>

              <tick>83</tick>
              <angle>31</angle>
              <visible>true</visible>
              <offset>(-0.1356932, 1, 0.2772861)</offset>
              <rotation>West</rotation>

              <tick>90</tick>
              <angle>37</angle>
              <visible>true</visible>
              <offset>(-0.2595871, 1, 0.342183)</offset>
              <rotation>West</rotation>
              <sound>Fuck</sound>

              <tick>95</tick>
              <angle>31</angle>
              <visible>true</visible>
              <offset>(-0.2123895, 1, 0.3303834)</offset>
              <rotation>West</rotation>

              <tick>100</tick>
              <angle>37</angle>
              <visible>true</visible>
              <offset>(-0.2654867, 1, 0.342183)</offset>
              <rotation>West</rotation>
              <sound>Fuck</sound>

              <tick>105</tick>
              <angle>34</angle>
              <visible>true</visible>
              <offset>(-0.2123895, 1, 0.3126844)</offset>
              <rotation>West</rotation>

          </keyframes>
        </value>

        <key>Head</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <rotation>North</rotation>

          </keyframes>
        </value>

        <key>AnimalPenis</key>
        <value>
          <workerClass>Rimworld_Animations.AnimationWorker_KeyframesExtended</workerClass>
          <keyframes>

              <tick>0</tick>
              <angle>0</angle>
              <visible>true</visible>
              <offset>(0, -2, 0)</offset>
              <rotation>North</rotation>

          </keyframes>
        </value>

    </animationParts>
  </AnimationDef>
</Defs>