From 19968b1167a194a16cbaf3110c877f8644d953ec Mon Sep 17 00:00:00 2001
From: CloudyCoffee <megieboy@gmail.com>
Date: Sat, 4 Sep 2021 23:59:18 +0200
Subject: [PATCH] - New qaudruped interface and changes. - Quadrupedal limbs
 are locked behind a prosthetic lab upgrade. - fixes

---
 .../slave variables documentation.md          |   1 +
 devTools/types/FC/gameState.d.ts              |   1 +
 devTools/types/FC/human.d.ts                  |   2 +-
 devTools/types/FC/medicine.d.ts               |   4 +-
 js/003-data/slaveProstheticsData.js           |  44 +++----
 .../backwardsCompatibility.js                 |   2 +-
 src/events/REFS/refsWarhound.js               |   2 +-
 src/facilities/penthouse/penthousePassage.js  |  10 +-
 src/interaction/prostheticConfig.js           |  18 +--
 src/interaction/prostheticLab.js              |   9 +-
 src/interaction/prostheticLabPassage.js       |  46 ++++---
 src/js/SlaveState.js                          |   6 +-
 src/js/statsChecker/limbChecker.js            |  31 +++--
 src/js/statsChecker/statsChecker.js           |   2 +-
 src/npc/children/ChildState.js                | 114 ++++++++++--------
 src/npc/descriptions/descriptionWidgets.js    |  58 +++++----
 src/npc/descriptions/ears.js                  |   8 +-
 src/npc/descriptions/limbs.js                 |  14 ++-
 src/npc/startingGirls/startingGirls.js        |  13 +-
 src/player/js/PlayerState.js                  |   1 +
 20 files changed, 197 insertions(+), 189 deletions(-)

diff --git a/devNotes/legacy files/slave variables documentation.md b/devNotes/legacy files/slave variables documentation.md
index 4daddafe301..669b2081a5b 100644
--- a/devNotes/legacy files/slave variables documentation.md	
+++ b/devNotes/legacy files/slave variables documentation.md	
@@ -2119,6 +2119,7 @@ What level of prosthetic interface she has installed
 0 - no interface
 1 - basic interface
 2 - advanced interface
+3 - quadruped interface
 
 heels:
 
diff --git a/devTools/types/FC/gameState.d.ts b/devTools/types/FC/gameState.d.ts
index 4037fdb6bf6..370812e47dd 100644
--- a/devTools/types/FC/gameState.d.ts
+++ b/devTools/types/FC/gameState.d.ts
@@ -140,5 +140,6 @@ declare namespace FC {
 
 	export interface GameVariables extends DefaultGameStateVariables, ResetOnNGPVariables,
 		DeprecatedGameVariables, TemporaryVariablesInTheGameState {
+		oldLimbs: any;
 	}
 }
diff --git a/devTools/types/FC/human.d.ts b/devTools/types/FC/human.d.ts
index 739cc96e85b..9c0ad1d005b 100644
--- a/devTools/types/FC/human.d.ts
+++ b/devTools/types/FC/human.d.ts
@@ -258,7 +258,7 @@ declare global {
 
 		type EarWear = WithNone<"hearing aids" | "muffling ear plugs" | "deafening ear plugs">;
 		type EarShape = WithNone<"damaged" | "normal" | "pointy" | "elven" | "cow" | "robot" | "orcish" | "sheep" | "deer" | "gazelle" | "bird" | "dragon">;
-		type EarT = WithNone<"normal" | "cat" | "leopard" | "tiger" | "jaguar" | "lion" | "dog" | "wolf" | "jackal" | "fox" | "raccoon" | "rabbit" | "squirrel"| "horse">;
+		type EarTopType = WithNone<"normal" | "cat" | "leopard" | "tiger" | "jaguar" | "lion" | "dog" | "wolf" | "jackal" | "fox" | "raccoon" | "rabbit" | "squirrel"| "horse">;
 		type EyebrowStyle = "bald" | "curved" | "elongated" | "high-arched" | "natural" | "rounded" | "shaved" | "shortened" |
 			"slanted inwards" | "slanted outwards" | "straight";
 		type EyebrowThickness = "pencil-thin" | "thin" | "threaded" | "natural" | "tapered" | "thick" | "bushy";
diff --git a/devTools/types/FC/medicine.d.ts b/devTools/types/FC/medicine.d.ts
index aa7abc61dda..62fe597b017 100644
--- a/devTools/types/FC/medicine.d.ts
+++ b/devTools/types/FC/medicine.d.ts
@@ -1,9 +1,9 @@
 declare namespace FC {
-    type prostheticID = "interfaceP1" | "interfaceP2" | "basicL" | "sexL" | "beautyL" | "combatL" | "felidaeL" | "canidaeL" | "felidaeCL" | "canidaeCL" | "cyberneticL" 
+    type prostheticID = "interfaceP1" | "interfaceP2" | "interfaceP3" | "basicL" | "sexL" | "beautyL" | "combatL" | "felidaeL" | "canidaeL" | "felidaeCL" | "canidaeCL" | "cyberneticL" 
     | "ocular" | "cochlear" | "electrolarynx" | "interfaceTail" | "modT" | "sexT" | "combatT" | "combatT2" |/* "erectile" |*/ "interfaceBack" | "modW" | "flightW" 
     | "sexA" | "combatW" | "combatA1" | "combatA2";
 
-    type prostheticName = "basic prosthetic interface" | "advanced prosthetic interface" | "set of basic prosthetic limbs" | "set of advanced sex limbs" 
+    type prostheticName = "basic prosthetic interface" | "advanced prosthetic interface" | "advanced quadrupedal prosthetic interface" | "set of basic prosthetic limbs" | "set of advanced sex limbs" 
     | "set of advanced beauty limbs" | "set of advanced combat limbs" | "set of quadruped feline limbs" | "set of quadruped canine limbs" | "set of feline combat limbs" 
     | "set of canine combat limbs" | "set of cybernetic limbs" | "ocular implant" | "cochlear implant" | "electrolarynx" | "prosthetic tail interface" | "modular tail" 
     | "pleasure tail" | "combat tail" | `combat tail, type "Stinger"` | "prosthetic back interface" | "modular pair of wings" | "pair of flight capable wings" 
diff --git a/js/003-data/slaveProstheticsData.js b/js/003-data/slaveProstheticsData.js
index b105b00f4b5..f73fb6b58ac 100644
--- a/js/003-data/slaveProstheticsData.js
+++ b/js/003-data/slaveProstheticsData.js
@@ -4,7 +4,7 @@
  * @type {FC.prostheticID[]}
  */
 App.Data.prostheticIDs =
-	["interfaceP1", "interfaceP2", "basicL", "sexL", "beautyL", "combatL", "felidaeL", "canidaeL", "felidaeCL", "canidaeCL", "cyberneticL", "ocular", "cochlear",
+	["interfaceP1", "interfaceP2", "interfaceP3", "basicL", "sexL", "beautyL", "combatL", "felidaeL", "canidaeL", "felidaeCL", "canidaeCL", "cyberneticL", "ocular", "cochlear",
 		"electrolarynx", "interfaceTail", "modT", "sexT", "combatT", "combatT2", /* "erectile",*/"interfaceBack", "modW", "flightW", "sexA", "combatW", "combatA1", "combatA2"];
 /**
  * @typedef {object} prosthetics
@@ -18,7 +18,7 @@ App.Data.prostheticIDs =
  * For all time values: 10 = 1 week without upgrades
  */
 
-/** 
+/**
  * @type {Object<FC.prostheticID, prosthetics>}
  */
 App.Data.prosthetics = {
@@ -38,6 +38,14 @@ App.Data.prosthetics = {
 		level: 2,
 		costs: 10000
 	},
+	interfaceP3: {
+		name: "advanced quadrupedal prosthetic interface",
+		adjust: 60,
+		craft: 60,
+		research: 140,
+		level: 4,
+		costs: 10000
+	},
 	basicL: {
 		name: "set of basic prosthetic limbs",
 		adjust: 40,
@@ -75,7 +83,7 @@ App.Data.prosthetics = {
 		adjust: 60,
 		craft: 70,
 		research: 140,
-		level: 2,
+		level: 4,
 		costs: 20000
 	},
 	canidaeL: {
@@ -83,7 +91,7 @@ App.Data.prosthetics = {
 		adjust: 60,
 		craft: 80,
 		research: 140,
-		level: 2,
+		level: 4,
 		costs: 15000
 	},
 	felidaeCL: {
@@ -91,7 +99,7 @@ App.Data.prosthetics = {
 		adjust: 100,
 		craft: 80,
 		research: 180,
-		level: 2,
+		level: 4,
 		costs: 15000
 	},
 	canidaeCL: {
@@ -99,7 +107,7 @@ App.Data.prosthetics = {
 		adjust: 100,
 		craft: 80,
 		research: 180,
-		level: 2,
+		level: 4,
 		costs: 20000
 	},
 	cyberneticL: {
@@ -271,22 +279,6 @@ App.Data.modTails = new Map([
 	["succubus", {animal: "Succubus", desc: "a long, slender succubus tail"}],
 	["dragon", {animal: "Dragon", desc: "a long, thick dragon tail"}]
 ]);
-
-App.Data.modWings = new Map([
-	["angel", {animal: "Angel", desc: "a pair of elegant angelic wings"}],
-	["seraph", {animal: "Seraph", desc: "three pairs of majestic-looking angels wings"}],
-	["demon", {animal: "Demon", desc: "a pair of sexy and sleek demonic wings"}],
-	["dragon", {animal: "Dragon", desc: "a pair of imposing draconic wings"}],
-	["phoenix", {animal: "Phoenix", desc: "a pair of magnificent, luminiscent phoenix wings"}],
-	["bird", {animal: "Bird", desc: "a pair of feathered wings"}],
-	["fairy", {animal: "Fairy", desc: "a pair of translucent, leaf-like fairy wings"}],
-	["butterfly", {animal: "Butterfly", desc: "a pair of beautiful butterfly wings"}],
-	["moth", {animal: "Moth", desc: "a pair of soft moth wings"}],
-	["insect", {animal: "Insect", desc: "a pair of transparent insect wings"}],
-	["evil", {animal: "Fiend", desc: "a pair of fiendish wings"}]
-	
-	
-]);
 /**
  * @type {Map<FC.WingsShape, {animal: string, desc: string}>}
  */
@@ -345,22 +337,22 @@ App.Data.prostheticLimbs = new Map([
 	[7, {
 		short: "quadruped feline",
 		prostheticKey: "felidaeL",
-		minimumInterface: 1,
+		minimumInterface: 3,
 	}],
 	[8, {
 		short: "quadruped canine",
 		prostheticKey: "canidaeL",
-		minimumInterface: 1,
+		minimumInterface: 3,
 	}],
 	[9, {
 		short: "feline combat",
 		prostheticKey: "felidaeCL",
-		minimumInterface: 1,
+		minimumInterface: 3,
 	}],
 	[10, {
 		short: "canine combat",
 		prostheticKey: "canidaeCL",
-		minimumInterface: 1,
+		minimumInterface: 3,
 	}],
 ]);
 
diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index c2572868890..46ec9a8e7e3 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -1554,7 +1554,6 @@ App.Update.oldVersions = function(node) {
 	/* unify cybermod & vanilla */
 	/* limbs */
 	if (V.releaseID < 1045) {
-		if (V.adjustProsthetics === "undefined") { V.adjustProsthetics = []; }
 		if (typeof V.limbs !== "undefined") {
 			V.adjustProsthetics = [];
 			V.limbs.forEach((l) => {
@@ -1826,6 +1825,7 @@ App.Update.oldVersions = function(node) {
 				delete V.researchLab.erectileImplant;
 			}
 		}
+		if (V.adjustProsthetics === "undefined") { V.adjustProsthetics = []; }
 	}
 	if (V.releaseID < 1047) {
 		if (V.loliGrow > 0) {
diff --git a/src/events/REFS/refsWarhound.js b/src/events/REFS/refsWarhound.js
index 2af988fc23c..1909a15a4b6 100644
--- a/src/events/REFS/refsWarhound.js
+++ b/src/events/REFS/refsWarhound.js
@@ -68,7 +68,7 @@ App.Events.refsWarhound = class refsWarhound extends App.Events.BaseEvent {
 		slave.preg = -1;
 		slave.collar = "tight steel";
 		slave.canRecruit = 0;
-		slave.custom.tattoo = `${He} has the Imperial symbol of your arcology tattooed on ${his} neck above a barcode.`;
+		slave.custom.tattoo = `$He has the Imperial symbol of your arcology tattooed above a barcode on $his neck.`;
 		setHealth(slave, jsRandom(30, 60), 0, 0, 0, 0);
 
 		App.Events.drawEventArt(node, slave);
diff --git a/src/facilities/penthouse/penthousePassage.js b/src/facilities/penthouse/penthousePassage.js
index b75b18b876c..1ab227c6bbe 100644
--- a/src/facilities/penthouse/penthousePassage.js
+++ b/src/facilities/penthouse/penthousePassage.js
@@ -291,7 +291,15 @@ App.UI.managePenthouse = function() {
 				}
 				break;
 			case 3:
-				r.push(`You have basic equipment for attaching and maintenance of prosthetics and a contract to guarantee the availability of high-tech prosthetics.`);
+				r.push(`You have advanced equipment for attaching and maintenance of prosthetics and a contract to guarantee the availability of high-tech prosthetics.`);
+				if (V.rep > 18000) {
+					r.push(makeLink("Buy a contract for quadruped prosthetics", () => { V.prostheticsUpgrade = 4; }, 100000));
+				} else {
+					r.push(App.UI.DOM.makeElement("span", "You lack the reputation to get a contract for qaudruped prosthetics.", "note"));
+				}
+				break;
+			case 4:
+				r.push(`You have advanced equipment for attaching and maintenance of prosthetics and a contract to guarantee the availability of qaudruped prosthetics.`);
 		}
 		App.Events.addNode(el, r, "div");
 
diff --git a/src/interaction/prostheticConfig.js b/src/interaction/prostheticConfig.js
index cc3cd847252..6b562cd8ced 100644
--- a/src/interaction/prostheticConfig.js
+++ b/src/interaction/prostheticConfig.js
@@ -659,23 +659,11 @@ App.UI.prostheticsConfigPassage = function() {
 			V.nextLink = "Prosthetics Configuration";
 			App.UI.DOM.appendNewElement("div", node, `Attaching ${his} tail is a simple procedure, you simply push the connector into a socket, right where ${his} tailbone ends, until the lock engages.`);
 			r.push(`When you are done, ${he} looks back and`);
-			if (slave.tailShape === "cat") {
+			if (slave.tailShape === "cat" || slave.tailShape === "leopard" || slave.tailShape === "tiger" || slave.tailShape === "jaguar" || slave.tailShape === "lion") {
 				r.push(`sways ${his} tail side to side enigmatically.`);
-			} else if (slave.tailShape === "leopard") {
-				r.push(`sways ${his} tail side to side enigmatically.`);
-			} else if (slave.tailShape === "tiger") {
-				r.push(`sways ${his} tail side to side enigmatically.`);
-			} else if (slave.tailShape === "jaguar") {
-				r.push(`sways ${his} tail side to side enigmatically.`);
-			} else if (slave.tailShape === "Lion") {
-				r.push(`sways ${his} tail side to side enigmatically.`);
-			} else if (slave.tailShape === "dog") {
-				r.push(`wags ${his} tail side to side energetically.`);
-			} else if (slave.tailShape === "wolf") {
-				r.push(`wags ${his} tail side to side energetically.`);
-			} else if (slave.tailShape === "jackal") {
+			} else if (slave.tailShape === "dog" || slave.tailShape === "wolf" || slave.tailShape === "jackal") {
 				r.push(`wags ${his} tail side to side energetically.`);
-			} else if (slave.tailShape === "fox") {
+			}else if (slave.tailShape === "fox") {
 				r.push(`slowly sways ${his} tail feeling the soft fur brush against ${his} skin.`);
 			} else if (slave.tailShape === "kitsune") {
 				r.push(`slowly sways ${his} tails luxuriating in the incredibly soft, fluffy fur brushing against ${his} skin.`);
diff --git a/src/interaction/prostheticLab.js b/src/interaction/prostheticLab.js
index 9613a4573c0..f083e24b6e6 100644
--- a/src/interaction/prostheticLab.js
+++ b/src/interaction/prostheticLab.js
@@ -3,9 +3,10 @@
  */
 
 globalThis.getProstheticsStockpile = function() {
-	return `<div>Prosthetics interfaces: ${num(V.prosthetics.interfaceP1.amount + V.prosthetics.interfaceP2.amount)}</div>` +
+	return `<div>Prosthetics interfaces: ${num(V.prosthetics.interfaceP1.amount + V.prosthetics.interfaceP2.amount + V.prosthetics.interfaceP3.amount)}</div>` +
 		`<div class="choices">Basic: ${V.prosthetics.interfaceP1.amount}</div>` +
 		`<div class="choices">Advanced: ${V.prosthetics.interfaceP2.amount}</div>` +
+		`<div class="choices">Quadruped: ${V.prosthetics.interfaceP3.amount}</div>` +
 		`<div>Limbs: ${num(V.prosthetics.basicL.amount + V.prosthetics.sexL.amount + V.prosthetics.beautyL.amount +
 			V.prosthetics.combatL.amount + V.prosthetics.cyberneticL.amount + V.prosthetics.felidaeL.amount + V.prosthetics.canidaeL.amount + V.prosthetics.felidaeCL.amount + V.prosthetics.canidaeCL.amount)}</div>` +
 		`<div class="choices">Basic: ${V.prosthetics.basicL.amount}</div>` +
@@ -30,9 +31,9 @@ globalThis.getProstheticsStockpile = function() {
 		`<div class="choices">Combat Tail, "Stinger": ${V.prosthetics.combatT2.amount}</div>` +
 		`<div>Back interface: ${V.prosthetics.interfaceBack.amount}</div>` +
 		`<div>Appedages: ${num(V.prosthetics.modW.amount + V.prosthetics.flightW.amount + V.prosthetics.sexA.amount + V.prosthetics.combatW.amount + V.prosthetics.combatA1.amount + V.prosthetics.combatA2.amount)}</div>` +
-		`<div class="choices">ModularWings: ${V.prosthetics.modW.amount}</div>` +
-		`<div class="choices">AerialWings: ${V.prosthetics.flightW.amount}</div>` +
-		`<div class="choices">PleasureAppendages: ${V.prosthetics.sexA.amount}</div>` +
+		`<div class="choices">Modular Wings: ${V.prosthetics.modW.amount}</div>` +
+		`<div class="choices">Aerial Wings: ${V.prosthetics.flightW.amount}</div>` +
+		`<div class="choices">Pleasure Appendages: ${V.prosthetics.sexA.amount}</div>` +
 		`<div class="choices">Combat Wings, "Falcon": ${V.prosthetics.combatW.amount}</div>` +
 		`<div class="choices">Combat Appendages, "Arachnid": ${V.prosthetics.combatA1.amount}</div>` +
 		`<div class="choices">Combat Appendages, "Kraken": ${V.prosthetics.combatA2.amount}</div>`;
diff --git a/src/interaction/prostheticLabPassage.js b/src/interaction/prostheticLabPassage.js
index bc3b76f0ee5..1733e9a84b0 100644
--- a/src/interaction/prostheticLabPassage.js
+++ b/src/interaction/prostheticLabPassage.js
@@ -237,22 +237,20 @@ App.UI.prostheticLab = function() {
 		App.UI.DOM.appendNewElement("div", node, `Available research projects:`);
 		for (let p of App.Data.prostheticIDs) {
 			if (V.prosthetics[p].research === 0) {
-				if (p !== "erectile") { /* excludes erectile */
-					// <div class="indent">
-					if (App.Data.prosthetics[p].level <= V.prostheticsUpgrade) {
-						App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
-							`Reverse engineer ${addA(App.Data.prosthetics[p].name)}`,
-							() => {
-								cashX(forceNeg(App.Data.prosthetics[p].costs), "labResearch");
-								V.prosthetics[p].research = -1;
-								V.researchLab.tasks.push({type: "research", id: p, workLeft: App.Data.prosthetics[p].research});
-								App.UI.reload();
-							}, [], "",
-							`Costs ${cashFormat(App.Data.prosthetics[p].costs)} of initial investment.`
-						), "indent");
-					} else {
-						App.UI.DOM.appendNewElement("div", node, `You need better contracts to get the required research material for reverse engineering ${addA(App.Data.prosthetics[p].name)}.`, "note");
-					}
+				// <div class="indent">
+				if (App.Data.prosthetics[p].level <= V.prostheticsUpgrade) {
+					App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
+						`Reverse engineer ${addA(App.Data.prosthetics[p].name)}`,
+						() => {
+							cashX(forceNeg(App.Data.prosthetics[p].costs), "labResearch");
+							V.prosthetics[p].research = -1;
+							V.researchLab.tasks.push({type: "research", id: p, workLeft: App.Data.prosthetics[p].research});
+							App.UI.reload();
+						}, [], "",
+						`Costs ${cashFormat(App.Data.prosthetics[p].costs)} of initial investment.`
+					), "indent");
+				} else {
+					App.UI.DOM.appendNewElement("div", node, `You need better contracts to get the required research material for reverse engineering ${addA(App.Data.prosthetics[p].name)}.`, "note");
 				}
 			}
 		}
@@ -262,15 +260,13 @@ App.UI.prostheticLab = function() {
 		r.push(`Available building projects:`);
 		for (let p of App.Data.prostheticIDs) {
 			if (V.prosthetics[p].research === 1) {
-				if (p !== "erectile") { /* excludes erectile*/
-					App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
-						`Build ${addA(App.Data.prosthetics[p].name)}`,
-						() => {
-							V.researchLab.tasks.push({type: "craft", id: p, workLeft: App.Data.prosthetics[p].craft});
-							App.UI.reload();
-						}
-					), "indent");
-				}
+				App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
+					`Build ${addA(App.Data.prosthetics[p].name)}`,
+					() => {
+						V.researchLab.tasks.push({type: "craft", id: p, workLeft: App.Data.prosthetics[p].craft});
+						App.UI.reload();
+					}
+				), "indent");
 			}
 		}
 
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index 324e7bfe06c..035efa33c1f 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -728,9 +728,9 @@ App.Entity.SlaveState = class SlaveState {
 		 * @type {FC.EarShape} */
 		this.earShape = "normal";
 		/** type of top ears if any
-		 * @type {FC.EarT}*/
+		 * @type {FC.EarTopType}*/
 		this.earT = "none";
-		/** kemonomimi ear color
+		/** top ear color
 		 * "hairless" */
 		this.earTColor = "hairless";
 		/** sense of smell
@@ -746,7 +746,6 @@ App.Entity.SlaveState = class SlaveState {
 		/** horn color */
 		this.hornColor = "none";
 		/** type of tail installed
-		 * "none", "mod", "combat", "sex"
 		 * @type {FC.TailType}*/
 		this.tail = "none";
 		/**
@@ -840,6 +839,7 @@ App.Entity.SlaveState = class SlaveState {
 		 * * 0: no interface
 		 * * 1: basic interface
 		 * * 2: advanced interface
+		 * * 3: quadruped interface
 		 */
 		this.PLimb = 0;
 		/*
diff --git a/src/js/statsChecker/limbChecker.js b/src/js/statsChecker/limbChecker.js
index 5c4cfdf7ff6..a323dad04b7 100644
--- a/src/js/statsChecker/limbChecker.js
+++ b/src/js/statsChecker/limbChecker.js
@@ -360,13 +360,13 @@ globalThis.idToDescription = function(id) {
 		case 6:
 			return "highly advanced cybernetic";
 		case 7:
-			return "advanced, quadruped feline prosthetic";
+			return "feline prosthetic";
 		case 8:
-			return "advanced, quadruped canine prosthetic";
+			return "canine prosthetic";
 		case 9:
-			return "advanced, feline combat-adapted prosthetic";
+			return "feline combat-adapted prosthetic";
 		case 10:
-			return "advanced, canine combat-adapted prosthetic";
+			return "canine combat-adapted prosthetic";
 		default:
 			return "unknown id: " + id;
 	}
@@ -380,7 +380,7 @@ globalThis.idToDescription = function(id) {
  * * 3: sex
  * * 4: beauty
  * * 5: combat
- * *6: cybernetic
+ * * 6: cybernetic
  * * 7: felidaeL
  * * 8: canidaeL
  * * 9: felidaeCL
@@ -392,10 +392,9 @@ globalThis.idToDescription = function(id) {
  * * 103: sex-prosthetic
  * * 104: beauty-prosthetic
  * * 105: combat-prosthetic
- * * 106: feline-structural
- * * 107: canine-structural
- * * 108: feline-combat
- * * 109: canine-combat
+ * * 106: quadruped
+ * * 107: feline
+ * * 108: canine
  * *
  *
  * !!! I think I adjusted this right but i didn't spend too long on it so it might be wrong. !!!
@@ -427,19 +426,17 @@ globalThis.getLimbCount = function(slave, id = 101) {
 		case 102:
 			return getLimbCount(slave, 2) + getLimbCount(slave, 3) + getLimbCount(slave, 4) + getLimbCount(slave, 5) + getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
 		case 103:
-			return getLimbCount(slave, 3) + getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
+			return getLimbCount(slave, 3) + getLimbCount(slave, 6);
 		case 104:
-			return getLimbCount(slave, 4) + getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
+			return getLimbCount(slave, 4) + getLimbCount(slave, 6);
 		case 105:
-			return getLimbCount(slave, 5) + getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
+			return getLimbCount(slave, 5) + getLimbCount(slave, 6) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
 		case 106:
-			return getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
-		case 107:
 			return getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
+		case 107:
+			return getLimbCount(slave, 7) + getLimbCount(slave, 9);
 		case 108:
-			return getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
-		case 109:
-			return getLimbCount(slave, 9) + getLimbCount(slave, 10);
+			return getLimbCount(slave, 8) + getLimbCount(slave, 10);
 	}
 	// unknown id defaults to 0
 	return 0;
diff --git a/src/js/statsChecker/statsChecker.js b/src/js/statsChecker/statsChecker.js
index 299b3620a85..37e904045ba 100644
--- a/src/js/statsChecker/statsChecker.js
+++ b/src/js/statsChecker/statsChecker.js
@@ -776,7 +776,7 @@ globalThis.canHold = function(slave) {
 	if (!slave) {
 		return null;
 	} else if (hasAnyQuadrupedArms) {
-		return null;
+		return false;
 	}
 	return hasAnyArms(slave);
 };
diff --git a/src/npc/children/ChildState.js b/src/npc/children/ChildState.js
index a8ab3526636..857d2f24d81 100644
--- a/src/npc/children/ChildState.js
+++ b/src/npc/children/ChildState.js
@@ -228,55 +228,71 @@ App.Facilities.Nursery.ChildState = class ChildState {
 		/** Is there an inner ear implant device
 		 * 0: no; 1: yes */
 		this.earImplant = 0;
-		/** The shape of their outer ears
-		 * "none", "damaged", "normal", "pointy", "elven", "ushi" */
-		this.earShape = "normal";
-		/** Type of kemonomimi ears if any
-		 * "neko", "inu", "kit", "tanuki", "usagi" */
-		this.earT = "none";
-		/** Kemonomimi ear color
-		 * "hairless" */
-		this.earTColor = "hairless";
-		/** Sense of smell
-		0 - yes, -1 - no */
-		this.smells = 0;
-		/** Sense of taste
-		0 - yes, -1 - no */
-		this.tastes = 0;
-		/** Horn type if any
-		 * "none", "curved succubus horns", "backswept horns", "cow horns", "one long oni horn", "two long oni horns", "small horns" */
-		this.horn = "none";
-		/** Horn color */
-		this.hornColor = "none";
-		/** Type of tail installed
-		 * "none", "mod", "combat", "sex"*/
-		this.tail = "none";
-		/**
-		 * Does she have a tail interface installed
-		 * * 0: no
-		 * * 1: yes
-		 * @type {FC.Bool}
-		 */
-		this.PTail = 0;
-		/** The current shape of their modular tail
-		 * "none", "neko", "inu", "kit", "kitsune", "tanuki", "ushi", "usagi", "risu", "uma" */
-		this.tailShape = "none";
-		/** Tail color */
-		this.tailColor = "none";
-		/** Child's original hair color, defaults to their initial hair color. */
-		this.origHColor = "brown";
-		/** Hair color */
-		this.hColor = "brown";
-		/** Pubic hair color */
-		this.pubicHColor = "brown";
-		/** Armpit hair style */
-		this.underArmHColor = "brown";
-		/** eyebrowHColor*/
-		this.eyebrowHColor = "brown";
-		/** Child's original skin color. */
-		this.origSkin = "light";
-		/** Skin color */
-		this.skin = "light";
+		/** the shape of their outer ears
+		 * @type {FC.EarShape} */
+		 this.earShape = "normal";
+		 /** type of top ears if any
+		  * @type {FC.EarTopType}*/
+		 this.earT = "none";
+		 /** top ear color
+		  * "hairless" */
+		 this.earTColor = "hairless";
+		 /** sense of smell
+		 0 - yes, -1 - no */
+		 this.smells = 0;
+		 /** sense of taste
+		 0 - yes, -1 - no */
+		 this.tastes = 0;
+		 /** horn type if any
+		  * "none", "curved succubus horns", "backswept horns", "cow horns", "one long oni horn", "two long oni horns", "small horns"
+		  * @type {FC.HornType} */
+		 this.horn = "none";
+		 /** horn color */
+		 this.hornColor = "none";
+		 /** type of tail installed
+		  * @type {FC.TailType}*/
+		 this.tail = "none";
+		 /**
+		  * Does she have a tail interface installed
+		  * * 0: no
+		  * * 1: yes
+		  * @type {FC.Bool}
+		  */
+		 this.PTail = 0;
+		 /** the current shape of their modular tail
+		  * @type {FC.TailShape} */
+		 this.tailShape = "none";
+		 /** tail color */
+		 this.tailColor = "none";
+		 /** type of dorsal appendages installed
+		  * @type {FC.AppendagesType}*/
+		 this.appendages = "none";
+		 /**
+		  * Does she have a back interface installed
+		  * * 0: no
+		  * * 1: yes
+		  * @type {FC.Bool}
+		  */
+		 this.PBack = 0;
+		 /** the current shape of their modular wings
+		  * @type {FC.WingsShape} */
+		 this.wingsShape = "none";
+		 /** tail color */
+		 this.appendagesColor = "none";
+		 /** slave's original hair color, defaults to their initial hair color. */
+		 this.origHColor = "brown";
+		 /** hair color */
+		 this.hColor = "brown";
+		 /** pubic hair color */
+		 this.pubicHColor = "brown";
+		 /** armpit hair style */
+		 this.underArmHColor = "brown";
+		 /** eyebrowHColor*/
+		 this.eyebrowHColor = "brown";
+		 /** Slave's original skin color. */
+		 this.origSkin = "light";
+		 /** skin color */
+		 this.skin = "light";
 		/**
 		 * hair length
 		 * * 150: calf-length
diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js
index 53760cb94bb..5e322fd71e7 100644
--- a/src/npc/descriptions/descriptionWidgets.js
+++ b/src/npc/descriptions/descriptionWidgets.js
@@ -861,67 +861,63 @@ App.Desc.limbs = function(slave) {
 			r += `${He} has ${idToDescription(getLeftArmID(slave))} limbs, making ${him} quadrupedal.`;
 		}
 	} else {
-		if (!hasAnyArms(slave) && !isQuadrupedal(slave)) {
+		if (!hasAnyArms(slave)) {
 			r += `Both of ${his} arms have been amputated`;
-		} else if (!hasBothArms(slave)) {
+		} else if (!hasBothArms(slave) && !hasAnyQuadrupedArms(slave)) {
 			if (hasLeftArm(slave)) {
 				r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left arm, but ${his} right has been amputated,`;
 			} else {
 				r += `${He} has ${addA(idToDescription(getRightArmID(slave)))} right arm, but ${his} left has been amputated,`;
 			}
-		} else if (hasAnyQuadrupedArms(slave) && !isQuadrupedal(slave)) {
-			if (getLeftArmID(slave) > 6 && getRightArmID(slave) < 6) {
-				r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left foreleg, but a mismatched ${addA(idToDescription(getRightArmID(slave)))} right arm that hinders ${him},`;
-			} else if (getLeftArmID(slave) < 6 && getRightArmID(slave) > 6) {
-				r += `${He} has ${addA(idToDescription(getRightArmID(slave)))} right foreleg, but a mismatched ${addA(idToDescription(getLeftArmID(slave)))} left arm that hinders ${him},`;
-			}
-		} else if (hasBothQuadrupedArms(slave) && isQuadrupedal(slave)) {
-			r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left foreleg, ${addA(idToDescription(getRightArmID(slave)))} right foreleg,`;
-		} else if (hasBothQuadrupedArms(slave) && !isQuadrupedal(slave)) {
-			if (getLeftArmID(slave) === getRightArmID(slave)) {
-				r += `${He} has ${idToDescription(getLeftLegID(slave))} forelegs, with which she can't grab or hold anything,`;
+		} else if (!hasBothArms(slave) && hasAnyQuadrupedArms(slave)) {
+			if (hasLeftArm(slave)) {
+				r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left foreleg, but ${his} right has been amputated,`;
 			} else {
-				r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left foreleg and ${addA(idToDescription(getRightArmID(slave)))} right foreleg, with which she can't grab or hold anything,`;
+				r += `${He} has ${addA(idToDescription(getRightArmID(slave)))} right foreleg, but ${his} left has been amputated,`;
 			}
+		} else if (hasBothQuadrupedArms(slave) && isQuadrupedal(slave) && !(getLeftArmID(slave) === getRightArmID(slave))) {
+			r += `${He} has ${addA(idToDescription(getLeftArmID(slave)))} left foreleg, ${addA(idToDescription(getRightArmID(slave)))} right foreleg,`;
+		} else if (hasBothQuadrupedArms(slave) && isQuadrupedal(slave) && (getLeftArmID(slave) === getRightArmID(slave))) {
+			r += `${He} has ${idToDescription(getLeftLegID(slave))} forelegs, with which she can't grab or hold anything,`;
 		} else {
-			if (getLeftArmID(slave) === getRightArmID(slave) && !isQuadrupedal(slave)) {
+			if (getLeftArmID(slave) === getRightArmID(slave) && !hasAnyQuadrupedArms(slave)) {
 				r += `${He} has ${idToDescription(getLeftArmID(slave))} arms`;
 			} else {
 				r += `${He} has ${addA(idToDescription(getRightArmID(slave)))} right arm, but ${addA(idToDescription(getLeftArmID(slave)))} left arm`;
 			}
 		}
 		r += ` and `;
-		if (!hasAnyLegs(slave) && !isQuadrupedal(slave)) {
+		if (!hasAnyLegs(slave)) {
 			r += `both of ${his} legs have been amputated.`;
-		} else if (!hasBothLegs(slave)) {
+		} else if (!hasBothLegs(slave) && !hasAnyQuadrupedLegs(slave)) {
 			if (hasLeftLeg(slave)) {
 				r += `${he} has ${addA(idToDescription(getLeftLegID(slave)))} left leg, but ${his} right has been amputated.`;
 			} else {
 				r += `${he} has ${addA(idToDescription(getRightLegID(slave)))} right leg, but ${his} left has been amputated.`;
 			}
-		} else if (hasAnyQuadrupedLegs(slave) && !isQuadrupedal(slave)) {
-			if (getLeftLegID(slave) > 6 && getRightLegID(slave) < 6) {
-				r += `${he} has ${addA(idToDescription(getLeftLegID(slave)))} left backleg, but a mismatched ${addA(idToDescription(getRightLegID(slave)))} right leg that hinders ${him} greatly,`;
-			} else if (getLeftArmID(slave) < 6 && getRightArmID(slave) > 6) {
-				r += `${he} has ${addA(idToDescription(getRightLegID(slave)))} right backleg, but a mismatched ${addA(idToDescription(getLeftLegID(slave)))} left leg that hinders ${him} greatly,`;
-			}
-		} else if (hasBothQuadrupedLegs(slave) && isQuadrupedal(slave)) {
-			r += ` ${addA(idToDescription(getLeftLegID(slave)))} left backleg, ${addA(idToDescription(getRightLegID(slave)))} right backleg.`;
-		} else if (hasBothQuadrupedLegs(slave) && !isQuadrupedal(slave)) {
-			if (getLeftLegID(slave) === getRightLegID(slave)) {
-				r += `${he} has ${idToDescription(getLeftLegID(slave))} backlegs, that make it difficult to walk.`;
+		} else if (!hasBothLegs(slave) && hasAnyQuadrupedLegs(slave)) {
+			if (hasLeftLeg(slave)) {
+				r += `${he} has ${addA(idToDescription(getLeftLegID(slave)))} left backleg, but ${his} right has been amputated.`;
 			} else {
-				 r += `${he} has ${addA(idToDescription(getLeftLegID(slave)))} left backleg and ${addA(idToDescription(getRightLegID(slave)))} right backleg, that make it difficult to walk.`;
+				r += `${he} has ${addA(idToDescription(getRightLegID(slave)))} right backleg, but ${his} left has been amputated.`;
 			}
+		} else if (hasBothQuadrupedLegs(slave) && isQuadrupedal(slave) && !(getLeftLegID(slave) === getRightLegID(slave))) {
+			r += ` ${addA(idToDescription(getLeftLegID(slave)))} left backleg, ${addA(idToDescription(getRightLegID(slave)))} right backleg.`;
+		} else if (hasBothQuadrupedLegs(slave) && isQuadrupedal(slave) && (getLeftLegID(slave) === getRightLegID(slave))) {
+			r += `${he} has ${idToDescription(getLeftLegID(slave))} backlegs.`;
 		} else {
-			if (getLeftLegID(slave) === getRightLegID(slave) && !isQuadrupedal(slave)) {
+			if (getLeftLegID(slave) === getRightLegID(slave) && !hasAnyQuadrupedLegs(slave)) {
 				r += `${he} has ${idToDescription(getLeftLegID(slave))} legs.`;
 			} else {
 				r += `${he} has ${addA(idToDescription(getRightLegID(slave)))} right leg, but ${addA(idToDescription(getLeftLegID(slave)))} left leg.`;
 			}
 		}
 	}
-
+	if (hasAnyQuadrupedLimbs(slave) && !(getLeftArmID(slave) === getRightArmID(slave) &&
+	getLeftArmID(slave) === getLeftLegID(slave) &&
+	getLeftArmID(slave) === getRightLegID(slave))){
+		r += `The nature of ${his} prosthetics force ${him} to walk like an qaudrupedal animal.`;
+	}
 	return r;
 	/*
 	if (slave.am p) {
diff --git a/src/npc/descriptions/ears.js b/src/npc/descriptions/ears.js
index 9e5103fda53..cfa6d53261a 100644
--- a/src/npc/descriptions/ears.js
+++ b/src/npc/descriptions/ears.js
@@ -66,7 +66,7 @@ App.Desc.ears = function(slave) {
 		}
 		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
 	} else if (slave.earT === "leopard") {
-		r.push(`${He} has cute leopard ears, that are covered in soft ${slave.earTColor} fur and have black leopard spots; they`);
+		r.push(`${He} has cute leopard ears, they are ${slave.earTColor === "hairless" ? `hairless.` : `covered in soft ${slave.earTColor} fur and have black leopard spots`}; they`);
 		if (slave.earImplant === 1) {
 			r.push(`perk up at`);
 			if (slave.devotion > 20) {
@@ -77,7 +77,7 @@ App.Desc.ears = function(slave) {
 		}
 		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
 	} else if (slave.earT === "tiger") {
-		r.push(`${He} has pretty tiger ears on top of ${his} head; the ears are covered in dense ${slave.earTColor} fur and have distinct vertical stripes unique to ${him}. They`);
+		r.push(`${He} has pretty tiger ears on top of ${his} head; the ears are ${slave.earTColor === "hairless" ? "hairless but" : `covered in dense ${slave.earTColor} fur and`} have distinct vertical stripes unique to ${him}. They`);
 		if (slave.earImplant === 1) {
 			r.push(`perk up at`);
 			if (slave.devotion > 20) {
@@ -88,7 +88,7 @@ App.Desc.ears = function(slave) {
 		}
 		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
 	} else if (slave.earT === "jaguar") {
-		r.push(`${He} has adorable jaguar ears on top of ${his} head; the ears are covered in ${slave.earTColor} fur and packed with black rosettes with central dots. They`);
+		r.push(`${He} has adorable jaguar ears on top of ${his} head; the ears are ${slave.earTColor === "hairless" ? `hairless` : `covered in ${slave.earTColor} fur and packed with black rosettes with central dots`}. They`);
 		if (slave.earImplant === 1) {
 			r.push(`perk up at`);
 			if (slave.devotion > 20) {
@@ -99,7 +99,7 @@ App.Desc.ears = function(slave) {
 		}
 		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
 	} else if (slave.earT === "lion") {
-		r.push(`${He} has charming lion ears on ${his} head; the ears are covered in ${slave.earTColor} fur and have ${slave.hColor} fur at their base. They`);
+		r.push(`${He} has charming lion ears on ${his} head; the ears are ${slave.earTColor === "hairless" ? `hairless` : `covered in ${slave.earTColor} fur and have ${slave.hColor} fur at their base`}. They`);
 		if (slave.earImplant === 1) {
 			r.push(`perk up at`);
 			if (slave.devotion > 20) {
diff --git a/src/npc/descriptions/limbs.js b/src/npc/descriptions/limbs.js
index 7e178a7646a..074d72ffeb5 100644
--- a/src/npc/descriptions/limbs.js
+++ b/src/npc/descriptions/limbs.js
@@ -51,6 +51,10 @@ App.Medicine.Limbs.amputate = function(slave, oldLimbs, returnTo) {
 		implant = true;
 		r.push(App.UI.DOM.makeElement("div", App.UI.DOM.link("Install advanced interface", () => install(2))));
 	}
+	if (slave.PLimb < 3 && isProstheticAvailable(slave, "interfaceP3")) {
+		implant = true;
+		r.push(App.UI.DOM.makeElement("div", App.UI.DOM.link("Install advanced quadruped interface", () => install(3))));
+	}
 
 	// check if we can install a limb interface and if yes, give player the option to do so.
 	if (implant) {
@@ -92,9 +96,9 @@ App.Medicine.Limbs.amputate = function(slave, oldLimbs, returnTo) {
 App.Medicine.Limbs.prosthetic = function(slave, oldLimbs, returnTo) {
 	if (!(isProstheticAvailable(slave, "basicL") || isProstheticAvailable(slave, "sexL")
 		|| isProstheticAvailable(slave, "beautyL") || isProstheticAvailable(slave, "combatL")
-		|| isProstheticAvailable(slave, "felidaeL") || isProstheticAvailable(slave, "canidaeL")
-		|| isProstheticAvailable(slave, "felidaeCL") || isProstheticAvailable(slave, "canidaeCL")
-		|| (isProstheticAvailable(slave, "cyberneticL") && slave.PLimb > 1))) {
+		|| (isProstheticAvailable(slave, "cyberneticL") && slave.PLimb > 1) || (isProstheticAvailable(slave, "felidaeL") && slave.PLimb > 2)
+		|| (isProstheticAvailable(slave, "canidaeL") && slave.PLimb > 2) || (isProstheticAvailable(slave, "felidaeCL") && slave.PLimb > 2)
+		|| (isProstheticAvailable(slave, "canidaeCL") && slave.PLimb > 2))) {
 		return App.Medicine.Limbs.reaction(slave, oldLimbs, returnTo);
 	}
 	const {him} = getPronouns(slave);
@@ -269,8 +273,10 @@ App.Medicine.Limbs.selector = function(slave, oldLimbs, returnTo = "") {
  */
 App.Medicine.Limbs.reaction = function(slave, oldLimbs, returnTo = "") {
 	let r = "";
-	if (oldLimbs.PLimb !== slave.PLimb) {
+	if (oldLimbs.PLimb !== slave.PLimb && slave.PLimb < 3) {
 		r += `Prosthetic interface was ${oldLimbs.PLimb === 0 ? "none" : "basic"} and is now ${slave.PLimb === 1 ? "basic" : "advanced"}. `;
+	} else if (oldLimbs.PLimb !== slave.PLimb && slave.PLimb === 3) {
+		r += `Prosthetic interface was advanced and is now advanced quadruped. `;
 	}
 	if (getLeftArmID(oldLimbs) !== getLeftArmID(slave)) {
 		r += `Left arm was ${idToDescription(getLeftArmID(oldLimbs))} and is now ${idToDescription(getLeftArmID(slave))}. `;
diff --git a/src/npc/startingGirls/startingGirls.js b/src/npc/startingGirls/startingGirls.js
index 0085a6b8812..6ec458214a0 100644
--- a/src/npc/startingGirls/startingGirls.js
+++ b/src/npc/startingGirls/startingGirls.js
@@ -768,6 +768,7 @@ App.StartingGirls.physical = function(slave, cheat = false) {
 					["None", 0],
 					["Basic", 1],
 					["Advanced", 2],
+					["Quadruped", 3],
 				]);
 		}
 
@@ -788,14 +789,18 @@ App.StartingGirls.physical = function(slave, cheat = false) {
 							["Advanced: Sex", 3],
 							["Advanced: Beauty", 4],
 							["Advanced: Combat", 5],
-							["Feline: Structural", 7],
-							["Canine: Structural", 8],
-							["Feline: Combat", 9],
-							["Canine: Combat", 10],
 						]);
 						if (slave.PLimb > 1) {
 							option.addValue("Cybernetic", 6);
 						}
+						if (slave.PLimb > 2) {
+							option.addValueList([
+								["Feline: Structural", 7],
+								["Canine: Structural", 8],
+								["Feline: Combat", 9],
+								["Canine: Combat", 10],
+							]);
+						}
 					}
 				} else {
 					options.addCustomOption(`${capFirstChar(side)} ${limb}: amputated`)
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index aad6102cbb8..be79042f7de 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -522,6 +522,7 @@ App.Entity.PlayerState = class PlayerState {
 		 * * 0: no interface
 		 * * 1: basic interface
 		 * * 2: advanced interface
+		 * * 3: quadruped interface
 		 */
 		this.PLimb = 0;
 		/**
-- 
GitLab