Skip to content
Snippets Groups Projects
20-canvasmodel-combat-pc.js 61 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jimmys's avatar
    Jimmys committed
    // @ts-check
    /* global CombatRenderer, CombatPlayerOptions, PlayerCombatMapper, PlayerCanvasHelper */
    
    /**
     * @type {CanvasModelOptions<CombatPlayerOptions>}
     */
    const combatMainPc = {
    	name: "combatMainPc",
    	width: 256,
    	height: 256,
    
    xao's avatar
    xao committed
    	scale: true,
    
    Jimmys's avatar
    Jimmys committed
    	frames: 4,
    	/*
    	 * http://patorjk.com/software/taag/#p=display&c=c&f=ANSI%20Regular&t=generated
    	 *	 ██████  ███████ ███    ██ ███████ ██████   █████  ████████ ███████ ██████
    	 *	██       ██      ████   ██ ██      ██   ██ ██   ██    ██    ██      ██   ██
    	 *	██   ███ █████   ██ ██  ██ █████   ██████  ███████    ██    █████   ██   ██
    	 *	██    ██ ██      ██  ██ ██ ██      ██   ██ ██   ██    ██    ██      ██   ██
    	 *	 ██████  ███████ ██   ████ ███████ ██   ██ ██   ██    ██    ███████ ██████
    	 */
    	generatedOptions() {
    		/* Ask Aim about how this could be used? */
    		return [];
    	},
    	/*
    	 *	██████  ███████ ███████  █████  ██    ██ ██      ████████ ███████
    	 *	██   ██ ██      ██      ██   ██ ██    ██ ██         ██    ██
    	 *	██   ██ █████   █████   ███████ ██    ██ ██         ██    ███████
    	 *	██   ██ ██      ██      ██   ██ ██    ██ ██         ██         ██
    	 *	██████  ███████ ██      ██   ██  ██████  ███████    ██    ███████
    	 */
    	defaultOptions() {
    
    		const options = { ...PlayerCombatMapper.generateOptions(), ...this.metadata };
    		return options;
    
    Jimmys's avatar
    Jimmys committed
    	},
    	/*
    	 *	██████  ██████  ███████ ██████  ██████   ██████   ██████ ███████ ███████ ███████
    	 *	██   ██ ██   ██ ██      ██   ██ ██   ██ ██    ██ ██      ██      ██      ██
    	 *	██████  ██████  █████   ██████  ██████  ██    ██ ██      █████   ███████ ███████
    	 *	██      ██   ██ ██      ██      ██   ██ ██    ██ ██      ██           ██      ██
    	 *	██      ██   ██ ███████ ██      ██   ██  ██████   ██████ ███████ ███████ ███████
    	 */
    	preprocess(options) {
    		PlayerCombatMapper.mapPlayerToOptions(options);
    
    		if (V.debug) {
    			// Save options for easy lookup
    			CombatRenderer.options[this.name] = options;
    		}
    
    Jimmys's avatar
    Jimmys committed
    	},
    	layers: {
    		/*
    		 *    ██████  ███████ ██████  ██    ██  ██████
    		 *    ██   ██ ██      ██   ██ ██    ██ ██
    		 *    ██   ██ █████   ██████  ██    ██ ██   ███
    		 *    ██   ██ ██      ██   ██ ██    ██ ██    ██
    		 *    ██████  ███████ ██████   ██████   ██████
    		 */
    		frameCount: {
    			srcfn(options) {
    				const frames = PlayerCombatMapper.getPcAnimationFrameCount(options);
    				return `${options.root}${frames}f.png`;
    			},
    			showfn(options) {
    
    				const frames = PlayerCombatMapper.getPcAnimationFrameCount(options);
    				return options.isDebugging && (frames === 2 || frames === 4);
    
    Jimmys's avatar
    Jimmys committed
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.near,
    		},
    		/*
    		 *    ██    ██  ██████  ██████  ███████
    		 *    ██    ██ ██    ██ ██   ██ ██
    		 *    ██    ██ ██    ██ ██████  █████
    		 *     ██  ██  ██    ██ ██   ██ ██
    		 *      ████    ██████  ██   ██ ███████
    		 */
    		voreBack: {
    			srcfn(options) {
    				return `${options.src}vore/back-${options.vore.stage}.png`;
    			},
    			showfn(options) {
    				return !!options.vore.show;
    			},
    			z: CombatRenderer.indices.far,
    		},
    		voreFront: {
    			srcfn(options) {
    				return `${options.src}vore/front-${options.vore.stage}.png`;
    			},
    			showfn(options) {
    				return !!options.vore.show;
    			},
    			z: CombatRenderer.indices.near,
    		},
    		/*
    		 *    ██████  ██████   ██████  ██████  ███████
    		 *    ██   ██ ██   ██ ██    ██ ██   ██ ██
    		 *    ██████  ██████  ██    ██ ██████  ███████
    		 *    ██      ██   ██ ██    ██ ██           ██
    		 *    ██      ██   ██  ██████  ██      ███████
    		 */
    
    		wall: {
    			srcfn(options) {
    				return `${options.root}prop/wall/wall.png`;
    			},
    			showfn(options) {
    				return !!options.props.wall.show;
    			},
    			z: CombatRenderer.indices.near,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		bench: {
    			srcfn(options) {
    				return `${options.root}prop/bench/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.bench.show;
    			},
    			z: 5,
    		},
    		examTable: {
    			srcfn(options) {
    				return `${options.root}prop/exam-table/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.examTable.show;
    			},
    			z: 5,
    		},
    		haybale: {
    			srcfn(options) {
    				return `${options.root}prop/haybale/haybale.png`;
    			},
    			showfn(options) {
    				return !!options.props.haybale.show;
    			},
    			z: 5,
    		},
    		hospitalBed: {
    			srcfn(options) {
    				return `${options.root}prop/hospital-bed/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.hospitalBed.show;
    			},
    			z: 5,
    		},
    		hospitalBedRails: {
    			srcfn(options) {
    				return `${options.root}prop/hospital-bed/${options.position}-rails.png`;
    			},
    			showfn(options) {
    				return !!options.props.hospitalBed.show;
    			},
    			z: 95,
    		},
    		ivBag: {
    			srcfn(options) {
    				return `${options.root}prop/iv-bag/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.ivBag.show;
    			},
    			z: 5,
    		},
    		milkTank: {
    			srcfn(options) {
    				const tank = options.props.milkTank;
    				if (tank.isFull) {
    					return `${options.root}prop/milk-tank/tank-full.png`;
    				}
    				return `${options.root}prop/milk-tank/tank.png`;
    			},
    			showfn(options) {
    				return !!options.props.milkTank.show;
    			},
    			animation: "prop-4f-tank",
    			z: 1,
    		},
    		milkTankVolume: {
    			srcfn(options) {
    				const tank = options.props.milkTank;
    				return `${options.root}prop/milk-tank/${tank.volume}.png`;
    			},
    			showfn(options) {
    				return !!options.props.milkTank.show;
    			},
    			animation: "prop-4f-tank",
    			z: 3,
    		},
    		semenTank: {
    			srcfn(options) {
    				if (options.props.semenTank.isFull) {
    					return `${options.root}prop/semen-tank/semen-full.png`;
    				}
    				return `${options.root}prop/semen-tank/semen.png`;
    			},
    			showfn(options) {
    				const show = options.props.semenTank.show;
    				return !!show;
    			},
    			animation: "prop-4f-tank",
    			z: 2,
    		},
    		semenTankVolume: {
    			srcfn(options) {
    				const tank = options.props.semenTank;
    				return `${options.root}prop/semen-tank/${tank.volume}.png`;
    			},
    			showfn(options) {
    				const show = options.props.semenTank.show;
    				return !!show;
    			},
    			animation: "prop-4f-tank",
    			z: 2,
    		},
    		table: {
    			srcfn(options) {
    				return `${options.root}prop/table/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.table.show;
    			},
    			z: 5,
    		},
    		leash: {
    			srcfn(options) {
    				return `${options.root}prop/leash/${options.position}.png`;
    			},
    			showfn(options) {
    				return !!options.props.leash.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.base,
    		},
    
    Purity's avatar
    Purity committed
    		neck_shackle: {
    			srcfn(options) {
    				return `${options.root}prop/shackles/${options.position}/neck.png`;
    			},
    			showfn(options) {
    				return !!options.props.neck_shackle.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.base,
    		},
    		arm_shackle: {
    			srcfn(options) {
    				if (options.armBackPosition === "bound") return `${options.root}prop/shackles/${options.position}/arms_bound.png`;
    				return `${options.root}prop/shackles/${options.position}/arms.png`;
    			},
    			showfn(options) {
    				return !!options.props.arm_shackle.show || !!options.machines.arm_chains.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.frontArm + 1,
    		},
    		leg_shackle: {
    			srcfn(options) {
    				return `${options.root}prop/shackles/${options.position}/legs_${options.legFrontPosition}.png`;
    			},
    			showfn(options) {
    				return !!options.props.leg_shackle.show || !!options.machines.leg_chains.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.frontLeg + 1,
    		},
    		rail: {
    			srcfn(options) {
    				return `${options.root}prop/rail/rails.png`;
    			},
    			showfn(options) {
    				return !!options.props.rail.show;
    			},
    			z: CombatRenderer.indices.base,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		pilloryBack: {
    			srcfn(options) {
    				const pillory = options.props.pillory;
    				if (pillory.isDirty) {
    					return `${options.root}prop/pillory/back-dirty.png`;
    				}
    				return `${options.root}prop/pillory/back-clean.png`;
    			},
    			showfn(options) {
    				const pillory = options.props.pillory;
    				return pillory.show && !pillory.hasHorse;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    
    Jimmys's avatar
    Jimmys committed
    		},
    		pilloryFront: {
    			srcfn(options) {
    				const pillory = options.props.pillory;
    				if (pillory.hasHorse) {
    					return `${options.root}prop/pillory/front-horse.png`;
    				}
    				if (pillory.isDirty) {
    					return `${options.root}prop/pillory/front-dirty.png`;
    				}
    				return `${options.root}prop/pillory/front-clean.png`;
    			},
    			showfn(options) {
    				return options.props.pillory.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    
    Jimmys's avatar
    Jimmys committed
    		},
    		pilloryTomatoes: {
    			srcfn(options) {
    				const pillory = options.props.pillory;
    				return `${options.root}prop/pillory/tomato/${pillory.tomatoes}.png`;
    			},
    			showfn(options) {
    				const pillory = options.props.pillory;
    				return pillory.show && pillory.isDirty && [1, 2, 3, 4].includes(pillory.tomatoes);
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.head + 1,
    		},
    		/*
    		 *    ███    ███  █████   ██████ ██   ██ ██ ███    ██ ███████ ███████
    		 *    ████  ████ ██   ██ ██      ██   ██ ██ ████   ██ ██      ██
    		 *    ██ ████ ██ ███████ ██      ███████ ██ ██ ██  ██ █████   ███████
    		 *    ██  ██  ██ ██   ██ ██      ██   ██ ██ ██  ██ ██ ██           ██
    		 *    ██      ██ ██   ██  ██████ ██   ██ ██ ██   ████ ███████ ███████
    		 */
    		breastMilker: {
    			srcfn(options) {
    				const size = Math.clamp(options.breastSize, 1, 4);
    				return `${options.root}machine/milker/${options.position}/breasts-${size}.png`;
    			},
    			showfn(options) {
    				return !!options.machines.breastMilker.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			zfn(options) {
    				if (options.position === "doggy") {
    					return CombatRenderer.indices.base + 10;
    				}
    				return CombatRenderer.indices.base + 12;
    			},
    		},
    		breastMilkerVolume: {
    			srcfn(options) {
    				const size = Math.clamp(options.breastSize, 1, 4);
    				return `${options.root}machine/milker/${options.position}/breasts-${size}-milk.png`;
    			},
    			showfn(options) {
    				return !!options.machines.breastMilker.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			zfn(options) {
    				if (options.position === "doggy") {
    					return CombatRenderer.indices.base + 9;
    				}
    				return CombatRenderer.indices.base + 11;
    			},
    		},
    		penisMilker: {
    			srcfn(options) {
    				return `${options.root}machine/milker/${options.position}/penis.png`;
    			},
    			showfn(options) {
    				return !!options.machines.penisMilker.show;
    			},
    			animationfn(options) {
    				return options.machineAnimKey;
    			},
    			z: CombatRenderer.indices.base + 7,
    		},
    		penisMilkerVolume: {
    			srcfn(options) {
    				return `${options.root}machine/milker/${options.position}/penis-semen.png`;
    			},
    			showfn(options) {
    				return !!options.machines.penisMilker.show;
    			},
    			animationfn(options) {
    				return options.machineAnimKey;
    			},
    			z: CombatRenderer.indices.base + 4,
    		},
    
    Purity's avatar
    Purity committed
    		tattooMachine: {
    			srcfn(options) {
    
    Jimmys's avatar
    Jimmys committed
    				return `${options.root}machine/tattoo/${options.position}/${options.machines.tattoo.use}.png`;
    
    Purity's avatar
    Purity committed
    			},
    			showfn(options) {
    				return !!options.machines.tattoo.show;
    			},
    			animationfn(options) {
    				return options.machineAnimKey;
    			},
    
    Purity's avatar
    Purity committed
    			zfn(options) {
    				if (
    					options.machines.tattoo.use === "forehead" ||
    					(options.machines.tattoo.use === "right_cheek" && options.position === "missionary") ||
    					(options.machines.tattoo.use === "left_cheek" && options.position === "doggy")
    				) {
    					return CombatRenderer.indices.head + 1;
    				}
    				if (
    					(options.machines.tattoo.use === "right_bottom" && options.position === "missionary") ||
    					(options.machines.tattoo.use === "left_bottom" && options.position === "doggy")
    				) {
    					return CombatRenderer.indices.frontLeg + 5;
    				}
    				return CombatRenderer.indices.base + 4;
    			},
    
    Purity's avatar
    Purity committed
    		},
    		dildoVaginal: {
    			srcfn(options) {
    				return `${options.root}machine/z-vaginal/${options.position}/${options.machines.vaginal.state}.png`;
    			},
    			showfn(options) {
    				return !!options.machines.vaginal.show;
    			},
    			animationfn(options) {
    				return options.machineAnimKey;
    			},
    
    Purity's avatar
    Purity committed
    			z: CombatRenderer.indices.base - 1,
    
    Purity's avatar
    Purity committed
    		},
    		dildoAnal: {
    			srcfn(options) {
    				return `${options.root}machine/z-anal/${options.position}/${options.machines.anal.state}.png`;
    			},
    			showfn(options) {
    				return !!options.machines.anal.show;
    			},
    			animationfn(options) {
    				return options.machineAnimKey;
    			},
    
    Purity's avatar
    Purity committed
    			z: CombatRenderer.indices.base - 1,
    
    Purity's avatar
    Purity committed
    		},
    
    Jimmys's avatar
    Jimmys committed
    		/*
    		 *    ████████ ███████ ███    ██ ████████  █████   ██████ ██      ███████ ███████
    		 *       ██    ██      ████   ██    ██    ██   ██ ██      ██      ██      ██
    		 *       ██    █████   ██ ██  ██    ██    ███████ ██      ██      █████   ███████
    		 *       ██    ██      ██  ██ ██    ██    ██   ██ ██      ██      ██           ██
    		 *       ██    ███████ ██   ████    ██    ██   ██  ██████ ███████ ███████ ███████
    		 */
    		tentacleAnal: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.anus.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.anus.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: 49,
    		},
    
    Purity's avatar
    Purity committed
    		tentacleAnalCum: {
    			srcfn(options) {
    				return `${options.src}body/anal/analcum.png`;
    			},
    			showfn(options) {
    				return options.tentacles.anus.show && V.anusstate === "tentacledeep";
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: 50,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		tentacleBreasts: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.breasts.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.breasts.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: 49,
    		},
    		tentacleFeet: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.feet.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.feet.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: 49,
    		},
    
    		tentacleBackArm: {
    
    Jimmys's avatar
    Jimmys committed
    			srcfn(options) {
    
    				if (options.tentacles.backArm.isBound) {
    					return `${options.src}tentacles/back-arm-bound.png`;
    				}
    
    Jimmys's avatar
    Jimmys committed
    				return `${options.src}tentacles/${options.tentacles.backArm.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.backArm.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    
    			z: CombatRenderer.indices.backArm + 5,
    
    Jimmys's avatar
    Jimmys committed
    		},
    
    		tentacleFrontArm: {
    
    Jimmys's avatar
    Jimmys committed
    			srcfn(options) {
    
    				if (options.tentacles.frontArm.isBound) {
    					return `${options.src}tentacles/front-arm-bound.png`;
    				}
    
    Jimmys's avatar
    Jimmys committed
    				return `${options.src}tentacles/${options.tentacles.frontArm.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.frontArm.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    
    			z: CombatRenderer.indices.frontArm + 5,
    
    Jimmys's avatar
    Jimmys committed
    		},
    
    		tentacleBackLeg: {
    
    Jimmys's avatar
    Jimmys committed
    			srcfn(options) {
    
    				return `${options.src}tentacles/back-leg-bound.png`;
    
    Jimmys's avatar
    Jimmys committed
    			},
    			showfn(options) {
    
    				return options.tentacles.backLeg.show && options.tentacles.backLeg.isBound;
    
    Jimmys's avatar
    Jimmys committed
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    
    			z: CombatRenderer.indices.backLeg + 5,
    
    Jimmys's avatar
    Jimmys committed
    		},
    
    		tentacleFrontLeg: {
    
    Jimmys's avatar
    Jimmys committed
    			srcfn(options) {
    
    				return `${options.src}tentacles/front-leg-bound.png`;
    
    Jimmys's avatar
    Jimmys committed
    			},
    			showfn(options) {
    
    				return options.tentacles.frontLeg.show && options.tentacles.frontLeg.isBound;
    
    Jimmys's avatar
    Jimmys committed
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    
    			z: CombatRenderer.indices.frontLeg + 5,
    
    Jimmys's avatar
    Jimmys committed
    		},
    		tentacleOral: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.mouth.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.mouth.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: 72,
    		},
    
    Purity's avatar
    Purity committed
    		tentacleOralCum: {
    			srcfn(options) {
    				return `${options.src}body/oral/oralcum.png`;
    			},
    			showfn(options) {
    				return options.tentacles.mouth.show && V.mouthstate === "tentacledeep";
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: 73,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		tentaclePenis: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.penis.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.penis.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: CombatRenderer.indices.frontLowerOverwear + 1,
    		},
    		tentacleVagina: {
    			srcfn(options) {
    				return `${options.src}tentacles/${options.tentacles.vagina.state}.png`;
    			},
    			showfn(options) {
    				return options.tentacles.vagina.show;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["tentacles"],
    			z: 49,
    		},
    
    Purity's avatar
    Purity committed
    		tentacleVaginaCum: {
    			srcfn(options) {
    
    Jimmys's avatar
    Jimmys committed
    				return `${options.src}tentacles/vagina-sperm.png`;
    
    Purity's avatar
    Purity committed
    			},
    			showfn(options) {
    				return options.tentacles.vagina.show && V.vaginastate === "tentacledeep";
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: 50,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		/*
    		 *	██████   █████  ███████ ███████
    		 *	██   ██ ██   ██ ██      ██
    		 *	██████  ███████ ███████ █████
    		 *	██   ██ ██   ██      ██ ██
    		 *	██████  ██   ██ ███████ ███████
    		 */
    		backarm: {
    			srcfn(options) {
    				if (options.position === "doggy" && PlayerCanvasHelper.isBestialHandjob(options, "back")) {
    					return `${options.src}body/arms/back-default.png`;
    				}
    				return `${options.src}body/arms/back-${options.armBackPosition}.png`;
    			},
    			showfn(options) {
    				if (!options.showPlayer) return false;
    				if (options.position === "missionary" && options.armBackPosition === "default") return false;
    				if (options.armBackPosition === "bound") return false;
    				return true;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.backArm,
    		},
    		backthigh: {
    			srcfn(options) {
    				return `${options.src}body/thighs/back-${options.legBackPosition}.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.backThigh,
    		},
    		backleg: {
    			srcfn(options) {
    				return `${options.src}body/legs/back-${options.legBackPosition}.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.backLeg,
    		},
    		base: {
    			srcfn(options) {
    				return `${options.src}body/base.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.base,
    		},
    		frontthigh: {
    			srcfn(options) {
    				return `${options.src}body/thighs/front-${options.legFrontPosition}.png`;
    			},
    			showfn(options) {
    				const result = options.showPlayer;
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.frontThigh,
    		},
    		frontleg: {
    			srcfn(options) {
    				return `${options.src}body/legs/front-${options.legFrontPosition}.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.frontLeg,
    		},
    		frontarm: {
    			srcfn(options) {
    				// Find target of hand if any, if bestial (pig) swap out sprite.
    
    Jimmys's avatar
    Jimmys committed
    				if (options.position === "doggy" && PlayerCanvasHelper.isBestialHandjob(options, "front")) {
    
    Jimmys's avatar
    Jimmys committed
    					return `${options.src}body/arms/front-${options.armFrontPosition}-bestial.png`;
    				}
    				// Generic position.
    				return `${options.src}body/arms/front-${options.armFrontPosition}.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    
    Jimmys's avatar
    Jimmys committed
    			zfn(options) {
    
    				if (options.props.pillory.show) {
    					return CombatRenderer.indices.frontArm - 2;
    				}
    
    Jimmys's avatar
    Jimmys committed
    				if (options.armFrontPosition === "bound") {
    					return CombatRenderer.indices.frontBoundArms;
    				}
    				return CombatRenderer.indices.frontArm;
    			},
    
    Jimmys's avatar
    Jimmys committed
    		},
    		frontbreast: {
    			srcfn(options) {
    				return `${options.src}body/breasts/${options.breastSize}.png`;
    			},
    			showfn(options) {
    				if (!options.breastSize) return false;
    				const result = options.showPlayer && options.breastsExposed;
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.base + 10,
    		},
    		penetrator: {
    			srcfn(options) {
    
    Jimmys's avatar
    Jimmys committed
    				if (V.player.virginity.penile === true) {
    
    					return `${options.src}body/penetrator/default-virgin.png`;
    				}
    
    Jimmys's avatar
    Jimmys committed
    				return `${options.src}body/penetrator/default-default.png`;
    			},
    			showfn(options) {
    				const penetrator = options.penetrator;
    				const result = options.showPlayer && penetrator?.show;
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.backLowerOverwear + 1,
    		},
    		penetratorEjaculate: {
    			srcfn(options) {
    				const penetrator = options.penetrator;
    				return `${options.src}body/penetrator/default-default-${penetrator?.ejaculate.type}.png`;
    			},
    			showfn(options) {
    				const penetrator = options.penetrator;
    				if (options.machines.penisMilker.show) return false;
    				const result = options.showPlayer && penetrator?.show && penetrator?.isEjaculating;
    				return !!result;
    			},
    			animationfn(options) {
    				return "sex-4f-vfast";
    			},
    			z: CombatRenderer.indices.backLowerOverwear + 1,
    		},
    
    xao's avatar
    xao committed
    		pregnantBelly: {
    
    			srcfn(options) {
    
    xao's avatar
    xao committed
    				return `${options.src}body/pregnantBelly/${options.pregnantBellyPath}/${options.bellySize}.png`;
    
    			},
    			showfn(options) {
    				if (!options.bellySize || !options.bellyState) return false;
    				const result = options.showPlayer && options.bellyState !== "hidden";
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    
    xao's avatar
    xao committed
    			filtersfn(options) {
    
    Purity's avatar
    Purity committed
    				return options.bellyState !== "clothed" ? ["body"] : [{ blend: options.filters.worn_upper_main.blend, blendMode: "hard-light" }];
    			},
    			zfn(options) {
    				return options.position === "doggy"
    					? CombatRenderer.indices.base + 24
    					: options.position === "missionary" && options.bellyState === "clothed"
    					? CombatRenderer.indices.base + 24
    					: CombatRenderer.indices.base + 14;
    
    xao's avatar
    xao committed
    			},
    
    xao's avatar
    xao committed
    		pregnantBellyOverlay: {
    
    			srcfn(options) {
    				return options.bellyState === "exposed" && options.position === "doggy"
    
    xao's avatar
    xao committed
    					? `${options.src}body/pregnantBelly/base/overlay_exposed.png`
    					: `${options.src}body/pregnantBelly/${options.pregnantBellyPath}/overlay.png`;
    
    			},
    			showfn(options) {
    				if (!options.bellySize || !options.bellyState) return false;
    				const result = options.showPlayer && options.bellyState !== "hidden";
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    
    xao's avatar
    xao committed
    			filtersfn(options) {
    
    Purity's avatar
    Purity committed
    				return options.bellyState !== "clothed" ? ["body"] : [{ blend: options.filters.worn_upper_main.blend, blendMode: "hard-light" }];
    			},
    			zfn(options) {
    				return options.position === "doggy"
    					? CombatRenderer.indices.base + 23
    					: options.position === "missionary" && options.bellyState === "clothed"
    					? CombatRenderer.indices.base + 23
    					: CombatRenderer.indices.base + 13;
    			},
    		},
    		pregnantBellyLowerMask: {
    			srcfn(options) {
    				return PlayerCanvasHelper.genClothingLayerLowerSrc("lower", "back", false, options);
    			},
    			showfn(options) {
    				if (!options.bellySize || !options.bellyState) return false;
    
    Jimmys's avatar
    Jimmys committed
    				if (options.bellyState === "hidden") {
    					return false;
    				}
    				return PlayerCanvasHelper.genClothingLayerLowerShow(options, "lower", "back", false);
    
    Purity's avatar
    Purity committed
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filtersfn(options) {
    				return [{ blend: options.filters.worn_lower_main.blend, blendMode: "hard-light" }];
    			},
    			zfn(options) {
    				return CombatRenderer.indices.base + 26;
    			},
    			masksrcfn(options) {
    				return `${options.src}body/pregnantBelly/${options.pregnantBellyPath}/${options.bellySize}.png`;
    			},
    		},
    		pregnantBellyLowerMaskOverlay: {
    			srcfn(options) {
    				return PlayerCanvasHelper.genClothingLayerLowerSrc("lower", "back", false, options);
    			},
    			showfn(options) {
    				if (!options.bellySize || !options.bellyState) return false;
    
    Jimmys's avatar
    Jimmys committed
    				if (options.bellyState === "hidden") {
    					return false;
    				}
    				return PlayerCanvasHelper.genClothingLayerLowerShow(options, "lower", "back", false);
    
    Purity's avatar
    Purity committed
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filtersfn(options) {
    				return [{ blend: options.filters.worn_lower_main.blend, blendMode: "hard-light" }];
    			},
    			zfn(options) {
    				return CombatRenderer.indices.base + 25;
    			},
    			masksrcfn(options) {
    				return `${options.src}body/pregnantBelly/${options.pregnantBellyPath}/overlay.png`;
    
    xao's avatar
    xao committed
    			},
    
    Jimmys's avatar
    Jimmys committed
    		/*
    		 *	██   ██ ███████  █████  ██████
    		 *	██   ██ ██      ██   ██ ██   ██
    		 *	███████ █████   ███████ ██   ██
    		 *	██   ██ ██      ██   ██ ██   ██
    		 *	██   ██ ███████ ██   ██ ██████
    		 */
    		head: {
    			srcfn(options) {
    				return `${options.src}body/head/head.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.head,
    		},
    
    		sclera: {
    			srcfn(options) {
    				return `${options.src}body/head/sclera-red.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer && !!options.showFace && !!options.sclera;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			z: CombatRenderer.indices.head + 2,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		frontEye: {
    			srcfn(options) {
    
    Purity's avatar
    Purity committed
    				return options.trauma ? `${options.src}body/head/eyesempty.png` : `${options.src}body/head/eyes.png`;
    
    Jimmys's avatar
    Jimmys committed
    			},
    			showfn(options) {
    				return !!options.showPlayer && !!options.showFace;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filtersfn(options) {
    				return [options.position === "missionary" ? "rightEye" : "leftEye"];
    			},
    			z: CombatRenderer.indices.head + 1,
    		},
    		eyelid: {
    			srcfn(options) {
    				return `${options.src}body/head/eyelids.png`;
    			},
    			showfn(options) {
    				const result = options.showPlayer && !!options.showFace;
    				return !!result;
    			},
    			animationfn(options) {
    
    Purity's avatar
    Purity committed
    				if (PlayerCombatMapper.getPcAnimationFrameCount(options) === 2 && options.position === "doggy") {
    					return "eyelids-doggy-idle";
    				}
    
    Jimmys's avatar
    Jimmys committed
    				if (combat.isActive()) {
    					return options.animKey;
    				}
    				return "sex-1f2-idle";
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.head + 2,
    		},
    		eyelashes: {
    			srcfn(options) {
    				return `${options.src}body/head/lashes.png`;
    			},
    			showfn(options) {
    				return !!options.showPlayer && !!options.showFace;
    			},
    			animationfn(options) {
    
    Purity's avatar
    Purity committed
    				if (PlayerCombatMapper.getPcAnimationFrameCount(options) === 2 && options.position === "doggy") {
    					return "eyelashes-doggy-idle";
    				}
    
    Jimmys's avatar
    Jimmys committed
    				if (combat.isActive()) {
    					return options.animKey;
    				}
    				return "sex-1f2-idle";
    			},
    			filters: ["hair"],
    			z: CombatRenderer.indices.head + 3,
    		},
    		blush: {
    			srcfn(options) {
    				return `${options.src}body/head/blush/${options.blush}.png`;
    			},
    			showfn(options) {
    				const result = options.showPlayer && !!options.showFace && options.blush > 0;
    				return !!result;
    			},
    			animationfn(options) {
    				return options.animKey;
    			},
    			filters: ["body"],
    			z: CombatRenderer.indices.head + 1,
    		},
    
    Jimmys's avatar
    Jimmys committed
    		freckles: {
    			srcfn(options) {
    				return `${options.src}body/head/freckles.png`;
    			},
    			showfn(options) {
    				const result = options.showPlayer && !!options.showFace && !!options.freckles;