diff --git a/devTools/types/FC/RA.d.ts b/devTools/types/FC/RA.d.ts index d9c75fff29ffea78e0b574b757ee501e71f33834..4552816cf06dc92914abb6a88d289ca20914f201 100644 --- a/devTools/types/FC/RA.d.ts +++ b/devTools/types/FC/RA.d.ts @@ -8,8 +8,12 @@ declare namespace FC { type NumericTarget = GenericNumericTarget<number>; type ExpressiveNumericTarget = GenericNumericTarget<number | string>; - interface RuleConditions { + interface RuleConditionEditorArguments { activation: PostFixRule; + advancedMode: boolean; + } + + interface RuleConditions extends RuleConditionEditorArguments{ selectedSlaves: number[]; excludedSlaves: number[]; applyRuleOnce: boolean; diff --git a/devTools/types/FC/human.d.ts b/devTools/types/FC/human.d.ts index 23ef528c941aa38f747d8df9d0c601c561d67594..b7bf00245fce571609aec683c6c1bf0bee64c77c 100644 --- a/devTools/types/FC/human.d.ts +++ b/devTools/types/FC/human.d.ts @@ -464,6 +464,8 @@ declare global { } //#endregion + type LimbType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 + type ArmState = InstanceType<typeof App.Entity.ArmState>; type LegState = InstanceType<typeof App.Entity.LegState>; @@ -476,7 +478,7 @@ declare global { left: LegState, right: LegState; }; - PLimb: number; + PLimb: 0 | 1 | 2 | 3; } interface PregnancyData { diff --git a/js/rulesAssistant/conditionEditorSimple.js b/js/rulesAssistant/conditionEditorSimple.js new file mode 100644 index 0000000000000000000000000000000000000000..0dd064745d83a4d77e11f0c6ebbde7184a18acc2 --- /dev/null +++ b/js/rulesAssistant/conditionEditorSimple.js @@ -0,0 +1,335 @@ +/** + * All functions should only be called from z1-conditionEditorController.js + */ +App.RA.Activation.SimpleEditor = (function() { + /** + * @typedef {object} RuleState + * @property {"always"|"never"|"boolean"|"number"|"string"} activeRuleType + * @property {string} boolGetter + * @property {boolean} negateBool + * @property {string} stringGetter + * @property {string} stringComparator + * @property {string} stringValue + * @property {string} numberGetter + * @property {number} numberUpperValue + * @property {"lt"|"lte"|""} numberUpperComparator + * @property {number} numberLowerValue + * @property {"gt"|"gte"|""} numberLowerComparator + */ + + /** + * @type {HTMLDivElement} + */ + let editorNode = null; + /** + * @type {RuleState} + */ + let currentRule = null; + + /** + * @param {FC.RA.PostFixRule} rule + * @param {HTMLDivElement}parent + */ + function editor(rule, parent) { + currentRule = deserializeRule(rule); + editorNode = parent; + editorNode.append(buildEditor()); + } + + function refreshEditor() { + if (editorNode !== null) { + $(editorNode).empty().append(buildEditor()); + } + } + + /** + * If the rule is valid, returns the serialized rule, otherwise null. + * + * @returns {FC.RA.PostFixRule} + */ + function saveEditor() { + if (currentRule == null) { + return null; + } + return serializeRule(currentRule); + } + + function resetEditor() { + currentRule = null; + editorNode = null; + } + + /** + * @returns {HTMLElement} + */ + function buildEditor() { + const outerDiv = document.createElement("div"); + // selector + const selectorDiv = document.createElement("div"); + selectorDiv.classList.add("button-group"); + outerDiv.append(selectorDiv); + App.UI.DOM.appendNewElement("button", selectorDiv, "Always", currentRule.activeRuleType === "always" ? ["selected", "disabled"] : []).onclick = () => { + currentRule.activeRuleType = "always"; + refreshEditor(); + }; + App.UI.DOM.appendNewElement("button", selectorDiv, "Never", currentRule.activeRuleType === "never" ? ["selected", "disabled"] : []).onclick = () => { + currentRule.activeRuleType = "never"; + refreshEditor(); + }; + App.UI.DOM.appendNewElement("button", selectorDiv, "Boolean", currentRule.activeRuleType === "boolean" ? ["selected", "disabled"] : []).onclick = () => { + currentRule.activeRuleType = "boolean"; + refreshEditor(); + }; + App.UI.DOM.appendNewElement("button", selectorDiv, "Number", currentRule.activeRuleType === "number" ? ["selected", "disabled"] : []).onclick = () => { + currentRule.activeRuleType = "number"; + refreshEditor(); + }; + App.UI.DOM.appendNewElement("button", selectorDiv, "String", currentRule.activeRuleType === "string" ? ["selected", "disabled"] : []).onclick = () => { + currentRule.activeRuleType = "string"; + refreshEditor(); + }; + + // add bool + if (currentRule.activeRuleType === "boolean") { + const boolDiv = document.createElement("div"); + boolDiv.classList.add("button-group"); + outerDiv.append(boolDiv); + /** + * @type {selectOption[]} + */ + const options = []; + App.RA.Activation.getterManager.booleanGetters.forEach((getter, key) => { + if (!getter.visible || getter.visible()) { + options.push({ + key: key, name: getter.name, enabled: !getter.enabled || getter.enabled() + }); + } + }); + boolDiv.append(App.UI.DOM.makeSelect(options, currentRule.boolGetter, key => { + currentRule.boolGetter = key; + refreshEditor(); + })); + boolDiv.append(" should be "); + App.UI.DOM.appendNewElement("button", boolDiv, "True", currentRule.negateBool ? [] : ["selected", "disabled"]).onclick = () => { + currentRule.negateBool = false; + refreshEditor(); + }; + App.UI.DOM.appendNewElement("button", boolDiv, "False", currentRule.negateBool ? ["selected", "disabled"] : []).onclick = () => { + currentRule.negateBool = true; + refreshEditor(); + }; + } else if (currentRule.activeRuleType === "number") { + const numberDiv = document.createElement("div"); + outerDiv.append(numberDiv); + /** + * @type {selectOption[]} + */ + const options = []; + App.RA.Activation.getterManager.numberGetters.forEach((getter, key) => { + if (!getter.visible || getter.visible()) { + options.push({ + key: key, name: getter.name, enabled: !getter.enabled || getter.enabled() + }); + } + }); + numberDiv.append(App.UI.DOM.makeSelect(options, currentRule.numberGetter, key => { + currentRule.numberGetter = key; + refreshEditor(); + })); + numberDiv.append(" should be "); + numberDiv.append(App.UI.DOM.makeSelect( + [{key: "gt", name: "greater than"}, {key: "gte", name: "greater than or equal to"}, + {key: "", name: "ignored"}], + currentRule.numberLowerComparator, key => { + currentRule.numberLowerComparator = key; + refreshEditor(); + })); + if (currentRule.numberLowerComparator !== "") { + numberDiv.append(" ", App.UI.DOM.makeTextBox(currentRule.numberLowerValue, val => { + currentRule.numberLowerValue = val; + }, true)); + } + numberDiv.append(" and "); + numberDiv.append(App.UI.DOM.makeSelect( + [{key: "lt", name: "less than"}, {key: "lte", name: "less than or equal to"}, + {key: "", name: "ignored"}], + currentRule.numberUpperComparator, key => { + currentRule.numberUpperComparator = key; + refreshEditor(); + })); + if (currentRule.numberUpperComparator !== "") { + numberDiv.append(" ", App.UI.DOM.makeTextBox(currentRule.numberUpperValue, val => { + currentRule.numberUpperValue = val; + }, true)); + } + } else if (currentRule.activeRuleType === "string") { + const stringDiv = document.createElement("div"); + outerDiv.append(stringDiv); + /** + * @type {selectOption[]} + */ + const options = []; + App.RA.Activation.getterManager.stringGetters.forEach((getter, key) => { + if (!getter.visible || getter.visible()) { + options.push({ + key: key, name: getter.name, enabled: !getter.enabled || getter.enabled() + }); + } + }); + stringDiv.append(App.UI.DOM.makeSelect(options, currentRule.stringGetter, key => { + currentRule.stringGetter = key; + refreshEditor(); + })); + stringDiv.append(" should "); + stringDiv.append(App.UI.DOM.makeSelect( + [{key: "eqstr", name: "equal"}, {key: "substr", name: "contain"}, {key: "match", name: "match"}], + currentRule.stringComparator, key => { + currentRule.stringComparator = key; + refreshEditor(); + })); + stringDiv.append(" ", App.UI.DOM.makeTextBox(currentRule.stringValue, val => { + currentRule.stringValue = val; + })); + } + + return outerDiv; + } + + /** + * @param {FC.RA.PostFixRule} rule + * @returns {RuleState} + */ + function deserializeRule(rule) { + // About the TS errors in this function: we can assume a lot about the rule composition because we know it's in + // the simple format. The rule itself is still a normal FC.RA.PostFixRule which would allow a lot more. + // Therefore, TS is not happy even though we now everything's fine. + /** + * @type {RuleState} + */ + const ruleState = { + activeRuleType: "always", + boolGetter: "isfertile", + negateBool: false, + stringGetter: "label", + stringValue: "", + stringComparator: "eqstr", + numberGetter: "devotion", + numberUpperValue: 100, + numberUpperComparator: "", + numberLowerValue: -100, + numberLowerComparator: "" + }; + // we know there is only one rule. + let i = 0; + const rulePart = rule[i]; + if (rulePart === true) { + ruleState.activeRuleType = "always"; + } else if (rulePart === false) { + ruleState.activeRuleType = "never"; + } else if (App.RA.Activation.getterManager.isBoolean(rulePart)) { + ruleState.activeRuleType = "boolean"; + ruleState.boolGetter = rulePart; + if (rule[i + 1] === "not") { + ruleState.negateBool = true; + i++; + } + } else if (App.RA.Activation.getterManager.isNumber(rulePart)) { + ruleState.activeRuleType = "number"; + ruleState.numberGetter = rulePart; + // check if there is a lower rule: + if (rule[i + 2].startsWith("g")) { + ruleState.numberLowerValue = rule[i + 1]; + ruleState.numberLowerComparator = rule[i + 2]; + // check if there is also an upper value: + if (rule[i + 3] === ruleState.numberGetter) { + ruleState.numberUpperValue = rule[i + 4]; + ruleState.numberUpperComparator = rule[i + 5]; + i += 3; + } + } else { + ruleState.numberUpperValue = rule[i + 1]; + ruleState.numberUpperComparator = rule[i + 2]; + } + i += 2; + } else if (App.RA.Activation.getterManager.isString(rulePart)) { + ruleState.activeRuleType = "string"; + ruleState.stringGetter = rulePart; + ruleState.stringValue = rule[i + 1].slice(1); + ruleState.stringComparator = rule[i + 2]; + i += 2; + } else { + throw new Error("Rule is not in simple mode format!"); + } + i++; + + return ruleState; + } + + /** + * Expects a valid RulePart structure + * + * @param {RuleState} ruleState + * @returns {FC.RA.PostFixRule} + */ + function serializeRule(ruleState) { + /** + * @type {FC.RA.PostFixRule} + */ + const rule = []; + let counter = 0; + + switch (ruleState.activeRuleType) { + case "always": + rule.push(true); + counter++; + break; + case "never": + rule.push(false); + counter++; + break; + case "boolean": + rule.push(ruleState.boolGetter); + if (ruleState.negateBool) { + rule.push("not"); + } + counter++; + break; + case "number": + // eslint-disable-next-line no-case-declarations + let any = false; + if (ruleState.numberLowerComparator !== "") { + any = true; + rule.push(ruleState.numberGetter); + rule.push(ruleState.numberLowerValue); + rule.push(ruleState.numberLowerComparator); + counter++; + } + if (ruleState.numberUpperComparator !== "") { + any = true; + rule.push(ruleState.numberGetter); + rule.push(ruleState.numberUpperValue); + rule.push(ruleState.numberUpperComparator); + counter++; + } + if (!any) { + rule.push(true); + counter++; + } + break; + case "string": + rule.push(ruleState.stringGetter); + rule.push("!"+ruleState.stringValue); + rule.push(ruleState.stringComparator); + counter++; + break; + } + rule.push(counter, "and"); + return rule; + } + + return { + build: editor, + save: saveEditor, + reset: resetEditor, + }; +})(); diff --git a/js/rulesAssistant/conditionEditor.js b/js/rulesAssistant/conditionEditorTree.js similarity index 98% rename from js/rulesAssistant/conditionEditor.js rename to js/rulesAssistant/conditionEditorTree.js index e6f4ec6be0004e6857e6223bd5ec849d95752fb4..87b8831b4c84ac0ce7038e4de05b276dee6b1f02 100644 --- a/js/rulesAssistant/conditionEditor.js +++ b/js/rulesAssistant/conditionEditorTree.js @@ -1,4 +1,7 @@ -App.RA.Activation.Editor = (function() { +/** + * All functions should only be called from z1-conditionEditorController.js + */ +App.RA.Activation.TreeEditor = (function() { /** * @type {HTMLDivElement} */ @@ -14,14 +17,13 @@ App.RA.Activation.Editor = (function() { /** * @param {FC.RA.PostFixRule} rule - * @returns {HTMLDivElement} + * @param {HTMLDivElement}parent */ - function editor(rule) { + function editor(rule, parent) { rulePartMap = new Map(); currentRule = deserializeRule(rule); - editorNode = document.createElement("div"); + editorNode = parent; editorNode.append(buildEditor()); - return editorNode; } function refreshEditor() { @@ -31,18 +33,19 @@ App.RA.Activation.Editor = (function() { } /** - * Save the rule, if it is valid. + * If the rule is valid, returns the serialized rule, otherwise null. * - * @param {(rule:FC.RA.PostFixRule)=>void} callback + * @returns {FC.RA.PostFixRule} */ - function saveEditor(callback) { + function saveEditor() { if (currentRule == null) { - return; + return null; } const error = currentRule.validate([]) === "error"; if (!error) { - callback(serializeRule(currentRule)); + return serializeRule(currentRule); } + return null; } function resetEditor() { diff --git a/js/rulesAssistant/z1-conditionEditorController.js b/js/rulesAssistant/z1-conditionEditorController.js new file mode 100644 index 0000000000000000000000000000000000000000..a45aa13f4624221969686df21c0af27483cf14bb --- /dev/null +++ b/js/rulesAssistant/z1-conditionEditorController.js @@ -0,0 +1,98 @@ +App.RA.Activation.Editor = (function() { + /** + * Should the advanced mode (tree editor) be used? + * @type {boolean} + */ + let advanced = false; + /** + * Keep a reference to the outermost node, so we can refresh it when needed. + * @type {HTMLDivElement} + */ + let outerNode = null; + + /** + * @param {FC.RA.RuleConditionEditorArguments} args + * @returns {HTMLDivElement} + */ + function editor(args) { + outerNode = document.createElement("div"); + fillOuterNode(args); + return outerNode; + } + + /** + * @param {FC.RA.RuleConditionEditorArguments} args + */ + function fillOuterNode(args) { + advanced = args.advancedMode; + let editorNode = document.createElement("div"); + if (advanced) { + outerNode.append(App.UI.DOM.link("Reset to simple mode", () => { + if (SugarCube.Dialog.isOpen()) { + SugarCube.Dialog.close(); + } + SugarCube.Dialog.setup("Reset RA to simple mode"); + $(SugarCube.Dialog.body()).empty().append( + "<p>Resetting will delete your current conditions. Do you want to continue?</p>", + App.UI.DOM.link("Yes, delete conditions.", () => { + args.advancedMode = false; + args.activation = App.RA.newRule.conditions().activation; + SugarCube.Dialog.close(); + $(outerNode).empty(); + fillOuterNode(args); + }), " ", + App.UI.DOM.makeElement("p", App.UI.DOM.link("Abort.", () => { + SugarCube.Dialog.close(); + }))); + SugarCube.Dialog.open(); + })); + App.RA.Activation.TreeEditor.build(args.activation, editorNode); + } else { + outerNode.append(App.UI.DOM.link("Switch to advanced mode", () => { + args.advancedMode = true; + $(outerNode).empty(); + fillOuterNode(args); + })); + App.RA.Activation.SimpleEditor.build(args.activation, editorNode); + } + outerNode.append(editorNode); + } + + /** + * Save the rule, if it is valid. + * + * @param {FC.RA.RuleConditionEditorArguments} args + */ + function saveEditor(args) { + if (advanced) { + let rule = App.RA.Activation.TreeEditor.save(); + if (rule == null) { + return; + } + args.advancedMode = advanced; + args.activation = rule; + } else { + let rule = App.RA.Activation.SimpleEditor.save(); + if (rule == null) { + return; + } + args.advancedMode = advanced; + args.activation = rule; + } + } + + function resetEditor() { + App.RA.Activation.TreeEditor.reset(); + App.RA.Activation.SimpleEditor.reset(); + outerNode = null; + advanced = false; + } + + return { + build: editor, + save: saveEditor, + reset: resetEditor, + // Because of this reference we need to load after conditionEditorTree.js + validateRule: App.RA.Activation.TreeEditor.validateRule, + }; +})(); diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js index af14361979dfaa92ceca64a05095bf32b9010935..6c747999a1efdcf08bf950f35cb13c77f66a18a0 100644 --- a/src/data/backwardsCompatibility/datatypeCleanup.js +++ b/src/data/backwardsCompatibility/datatypeCleanup.js @@ -2305,6 +2305,10 @@ App.Entity.Utils.RARuleDatatypeCleanup = function() { }; return jobs[assignment]; } + + if (!cond.hasOwnProperty("advancedMode")) { + cond.advancedMode = true; + } } /** @param {object} o */ diff --git a/src/endWeek/saWorkTheFarm.js b/src/endWeek/saWorkTheFarm.js index bc5d56aa3274bf1c5226ebed6ef9f5d9bb784814..b4b15afad14b8ded7c24ad7498bf4e3b33213cdc 100644 --- a/src/endWeek/saWorkTheFarm.js +++ b/src/endWeek/saWorkTheFarm.js @@ -59,7 +59,9 @@ App.SlaveAssignment.workTheFarm = function(slave) { V.mods.food.total += foodAmount; incomeStats.food += foodAmount; - return App.Facilities.Farmyard.produceFood(slave); + if (V.farmyardShows !== 2) { + return App.Facilities.Farmyard.produceFood(slave); + } } } diff --git a/src/facilities/farmyard/food/saProduceFood.js b/src/facilities/farmyard/food/saProduceFood.js index d5e89f19b891d8ab4d4a6289e0ec380fb9fbef8e..26fe219c3ba808d73ab2772e1d905ba8fa94e844 100644 --- a/src/facilities/farmyard/food/saProduceFood.js +++ b/src/facilities/farmyard/food/saProduceFood.js @@ -123,7 +123,7 @@ App.Facilities.Farmyard.produceFood = function(slave) { } function shows() { - if (V.farmyardShows > 0) { + if (V.farmyardShows === 1) { return `Since ${he} also has to put on shows for your citizens, ${he} can only work on food production for half of ${his} shift, cutting down on the amount of food ${he} would have otherwise produced.`; } } diff --git a/src/facilities/farmyard/shows/saFarmyardShows.js b/src/facilities/farmyard/shows/saFarmyardShows.js index 46a9d47fdf56971855290299a1da50b6057e5094..99d5431d120ea3503f8dcdf387774b4a8ae949eb 100644 --- a/src/facilities/farmyard/shows/saFarmyardShows.js +++ b/src/facilities/farmyard/shows/saFarmyardShows.js @@ -385,18 +385,20 @@ App.Facilities.Farmyard.putOnShows = function(slave) { } function energy() { - if (slave.energy > 95) { - return `The fact that ${he} is a nymphomaniac helps ${him} to go for longer, allowing ${him} to really put on an amazing show.`; - } else if (slave.energy > 80) { - return `The fact that ${his} sex drive is so powerful helps ${him} to really put on good shows.`; - } else if (slave.energy > 60) { - return `The fact that ${his} sex drive is so good helps ${him} to put on good shows.`; - } else if (slave.energy > 40) { - return `${His} average sex drive allows ${him} to put on a decent show.`; - } else if (slave.energy > 20) { - return `The fact that ${his} sex drive is so poor affects ${his} performance.`; - } else { - return `The fact that ${his} sex drive is nonexistent really hinders ${his} ability to put on a decent show.`; + if (V.seeBestiality) { + if (slave.energy > 95) { + return `The fact that ${he} is a nymphomaniac helps ${him} to go for longer, allowing ${him} to really put on an amazing show.`; + } else if (slave.energy > 80) { + return `The fact that ${his} sex drive is so powerful helps ${him} to really put on good shows.`; + } else if (slave.energy > 60) { + return `The fact that ${his} sex drive is so good helps ${him} to put on good shows.`; + } else if (slave.energy > 40) { + return `${His} average sex drive allows ${him} to put on a decent show.`; + } else if (slave.energy > 20) { + return `The fact that ${his} sex drive is so poor affects ${his} performance.`; + } else { + return `The fact that ${his} sex drive is nonexistent really hinders ${his} ability to put on a decent show.`; + } } } diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js index 6f9d03512a02cd1c0a095ea400605db0ab10584d..73c08b7a4727cfe8974467c90f3ad4387bf408a7 100644 --- a/src/js/DefaultRules.js +++ b/src/js/DefaultRules.js @@ -1610,17 +1610,17 @@ globalThis.DefaultRules = (function() { if (((slave.weight > 95) || ((slave.weight > 30) && (slave.hips < 2)))) { if (slave.diet !== "restricted") { slave.diet = "restricted"; - r += `<br>${slave.slaveName} is too fat so ${his} diet has been set to restricted.`; + r += `<br>${slave.slaveName} is unreasonably fat so ${his} diet has been set to restricted.`; dietPills(slave); } } else if (((slave.weight < -95) || ((slave.weight < -30) && (slave.hips > -2)))) { if (slave.diet !== "fattening") { slave.diet = "fattening"; - r += `<br>${slave.slaveName} is too skinny so ${his} diet has been set to fattening.`; + r += `<br>${slave.slaveName} is unreasonably skinny so ${his} diet has been set to fattening.`; dietPills(slave); } } else if (["restricted", "fattening"].includes(slave.diet)) { - r += `<br>${slave.slaveName} is at the target weight, so ${his} diet has been normalized.`; + r += `<br>${slave.slaveName} is at an acceptable weight, so ${his} diet has been normalized.`; slave.diet = "healthy"; dietPills(slave); muscleRule(slave, rule); diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js index 75095eb87f0af5da6859cd4a825ee9f882ea2805..497f5b99ea8e80adfc2eb7df0b3e48584370ab6d 100644 --- a/src/js/SlaveState.js +++ b/src/js/SlaveState.js @@ -512,6 +512,7 @@ App.Entity.LimbState = class LimbState { * * 4: advanced - Beauty * * 5: advanced - Combat * * 6: cybernetic + * @type {FC.LimbType} */ this.type = 1; /** diff --git a/src/js/rulesAssistant.js b/src/js/rulesAssistant.js index 66be45512f37b12852935668bed9da7d7b17b256..c247619be5f65642ac3fad48be8fef87ac24ab7f 100644 --- a/src/js/rulesAssistant.js +++ b/src/js/rulesAssistant.js @@ -178,6 +178,7 @@ App.RA.newRule = function() { function emptyConditions() { return { activation: ["devotion", 20, "gt", 1, "and"], + advancedMode: false, selectedSlaves: [], excludedSlaves: [], applyRuleOnce: false, diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js index d47e17530bd85da037165d1c16ba5bef4abd7997..ebc47642f3788a91202122c1cd5244b7f69d6fca 100644 --- a/src/js/rulesAssistantOptions.js +++ b/src/js/rulesAssistantOptions.js @@ -105,7 +105,7 @@ App.RA.options = (function() { * Save the settings for this rule. */ function saveSettings() { - App.RA.Activation.Editor.save(cond => current_rule.condition.activation = cond); + App.RA.Activation.Editor.save(current_rule.condition); } const parse = { @@ -1294,7 +1294,7 @@ App.RA.options = (function() { class ConditionBuilder extends Element { render() { - return App.RA.Activation.Editor.build(current_rule.condition.activation); + return App.RA.Activation.Editor.build(current_rule.condition); } } diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js index d2948e0bb7395ea70b8ebcef092ebc8f368d8652..46732c9459f6e39a3bb112e645ced181ced4ec1f 100644 --- a/src/npc/descriptions/descriptionWidgets.js +++ b/src/npc/descriptions/descriptionWidgets.js @@ -1111,6 +1111,14 @@ App.Desc.shortLimbs = function(slave) { return limb + "Combat P-Limb "; case 6: return limb + "Cyber P-Limb "; + case 7: + return limb + "Quad-Feline "; + case 8: + return limb + "Quad-Canine "; + case 9: + return limb + "Combat Feline "; + case 10: + return limb + "Combat Canine "; default: return "unknown ID: " + id; } @@ -1240,6 +1248,18 @@ App.Desc.longLimbs = function(slave) { case 6: r += "cyber "; break; + case 7: + r += "quadruped feline "; + break; + case 8: + r += "quadruped canine "; + break; + case 9: + r += "feline combat "; + break; + case 10: + r += "canine combat "; + break; } if (count > 1) { r += "prosthetic limbs. ";