diff --git a/src/js/extendedFamilyModeJS.js b/src/js/extendedFamilyModeJS.js index 7477d6885a68e7bb72d3bdc2cd9a4a9f5f651def..039b4afeb1144756326ac87bf4a50c881fd68d95 100644 --- a/src/js/extendedFamilyModeJS.js +++ b/src/js/extendedFamilyModeJS.js @@ -1,17 +1,48 @@ /* see documentation for details /devNotes/Extended Family Mode Explained.txt */ +/** @typedef Relative + * An object that represents a entity in a family tree. Represents a group of common properties shared by SlaveState, InfantState, and PlayerState, as well as genepool objects. + * @type {object} + * @property {number} ID + * @property {number} mother + * @property {number} father + * @property {number} actualAge + * @property {number} birthWeek + * @property {string} genes + */ + +/** Returns true if mother is the mother of daughter + * @param {Relative} daughter + * @param {Relative} mother + * @returns {boolean} + */ globalThis.isMotherP = function(daughter, mother) { return daughter.mother === mother.ID; }; +/** Returns true if father is the father of daughter + * @param {Relative} daughter + * @param {Relative} father + * @returns {boolean} + */ globalThis.isFatherP = function(daughter, father) { return daughter.father === father.ID; }; +/** Returns true if parent is either the father or mother of daughter + * @param {Relative} daughter + * @param {Relative} parent + * @returns {boolean} + */ globalThis.isParentP = function(daughter, parent) { return isMotherP(daughter, parent) || isFatherP(daughter, parent); }; +/** Returns true if grandmother is the grandmother of granddaughter + * @param {Relative} granddaughter + * @param {Relative} grandmother + * @returns {boolean} + */ globalThis.isGrandmotherP = function(granddaughter, grandmother) { let father; let mother; @@ -21,6 +52,11 @@ globalThis.isGrandmotherP = function(granddaughter, grandmother) { return false; }; +/** Returns true if grandfather is the grandfather of granddaughter + * @param {Relative} granddaughter + * @param {Relative} grandfather + * @returns {boolean} + */ globalThis.isGrandfatherP = function(granddaughter, grandfather) { let father; let mother; @@ -30,10 +66,20 @@ globalThis.isGrandfatherP = function(granddaughter, grandfather) { return false; }; +/** Returns true if grandparent is the either the grandmother or grandfather of granddaughter + * @param {Relative} granddaughter + * @param {Relative} grandparent + * @returns {boolean} + */ globalThis.isGrandparentP = function(granddaughter, grandparent) { return isGrandmotherP(granddaughter, grandparent) || isGrandfatherP(granddaughter, grandparent); }; +/** Returns true if slave1 and slave2 share the same father + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {boolean} + */ globalThis.sameDad = function(slave1, slave2) { if ((slave1.father === slave2.father) && (specificDad(slave1))) { return true; @@ -41,6 +87,11 @@ globalThis.sameDad = function(slave1, slave2) { return false; }; +/** Returns true if slave1 and slave2 share the same mother + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {boolean} + */ globalThis.sameMom = function(slave1, slave2) { if ((slave1.mother === slave2.mother) && (specificMom(slave1))) { return true; @@ -48,21 +99,45 @@ globalThis.sameMom = function(slave1, slave2) { return false; }; +/** Returns true if slave1 and slave2 have at least one common parent + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {boolean} + */ globalThis.sameParent = function(slave1, slave2) { return sameMom(slave1, slave2) || sameDad(slave1, slave2); }; +/** Returns true if ID represents a unique, specific, and traceable character; false if it represents a nonspecific or group ID. + * @param {number} ID + * @returns {boolean} + */ +globalThis.specificCharacterID = function(ID) { + return (ID > 0 // active slave + || ID < -20 // missing slave + || ID === -1 // player + || ID === -3); // player's old master +}; + +/** Returns true if the father of slave is a specific, unique character (current or former slave, or the PC) + * @param {Relative} slave + * @returns {boolean} + */ globalThis.specificDad = function(slave) { - return (slave.father !== 0 && slave.father !== -2 && slave.father !== -4 && slave.father !== -5 && slave.father !== -6 && slave.father !== -7 && slave.father !== -8 && slave.father !== -9); + return specificCharacterID(slave.father); }; +/** Returns true if the mother of slave is a specific, unique character (current or former slave, or the PC) + * @param {Relative} slave + * @returns {boolean} + */ globalThis.specificMom = function(slave) { - return (slave.mother !== 0 && slave.mother !== -2 && slave.mother !== -4 && slave.mother !== -5 && slave.mother !== -6 && slave.mother !== -7 && slave.mother !== -8 && slave.mother !== -9); + return specificCharacterID(slave.mother); }; -/** - * @param {App.Entity.SlaveState} niece - * @param {App.Entity.SlaveState} aunt +/** Returns true if aunt is the aunt of niece + * @param {Relative} niece + * @param {Relative} aunt * @returns {boolean} */ globalThis.isAunt = function(niece, aunt) { @@ -96,31 +171,10 @@ globalThis.sameTParent = function(slave1, slave2) { return 0; }; -/* -globalThis.sameTParent = function(slave1, slave2) { - if ((slave1.mother === slave2.father || slave1.father === slave2.mother) && (slave1.mother !== 0 && slave1.mother !== -2 && slave1.father !== 0 && slave1.father !== -2)) { - return true; //testtest catches the case if a mother is a father or a father a mother - } else { - return false; - } -}; -*/ - -globalThis.areTwins = function(slave1, slave2) { - if (!sameDad(slave1, slave2)) { - return false; - } else if (!sameMom(slave1, slave2)) { - return false; - } else if (slave1.actualAge === slave2.actualAge && slave1.birthWeek === slave2.birthWeek) { - return true; - } - return false; -}; - -/** - * @param {App.Entity.SlaveState} slave1 - * @param {App.Entity.SlaveState} slave2 - * @returns {number} +/** Returns the degree of siblinghood shared by two entities + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {number} - 0: not siblings; 1: twins; 2: full siblings; 3: half-siblings */ globalThis.areSisters = function(slave1, slave2) { if (slave1.ID === slave2.ID) { @@ -148,9 +202,9 @@ globalThis.areSisters = function(slave1, slave2) { } }; -/** - * @param {App.Entity.SlaveState} slave1 - * @param {App.Entity.SlaveState} slave2 +/** Returns true if two entities are simple first cousins (i.e. at least one parent of one slave is a full sibling of at least one parent of the other slave) + * @param {Relative} slave1 + * @param {Relative} slave2 * @returns {boolean} */ globalThis.areCousins = function(slave1, slave2) { @@ -180,18 +234,17 @@ globalThis.areCousins = function(slave1, slave2) { return false; }; -/** - * Returns whether two slaves are *closely* related (i.e. siblings or parent/child). +/** Returns whether two entities are *closely* related (i.e. siblings or parent/child). * Distant relatives are not checked by this function. - * @param {App.Entity.SlaveState} slave1 - * @param {App.Entity.SlaveState} slave2 + * @param {Relative} slave1 + * @param {Relative} slave2 * @returns {boolean} */ globalThis.areRelated = function(slave1, slave2) { return (slave1.father === slave2.ID || slave1.mother === slave2.ID || slave2.father === slave1.ID || slave2.mother === slave1.ID || areSisters(slave1, slave2) > 0); }; -/** +/** Returns the total number of relatives that a slave has * @param {App.Entity.SlaveState} slave * @returns {number} */ @@ -212,9 +265,9 @@ globalThis.totalRelatives = function(slave) { return relatives; }; -/** - * @param {App.Entity.SlaveState} slave1 - * @param {App.Entity.SlaveState} slave2 +/** Returns the slaves which are mutual children of two entities + * @param {Relative} slave1 + * @param {Relative} slave2 * @param {App.Entity.SlaveState[]} slaves * @returns {number} */ @@ -224,123 +277,88 @@ globalThis.mutualChildren = function(slave1, slave2, slaves) { }).length; }; -/** - * @param {App.Entity.SlaveState} slave +/** Returns a random slave related to a given entity + * @param {Relative} slave * @param {function(App.Entity.SlaveState): boolean} filterFunction * @returns {App.Entity.SlaveState} */ globalThis.randomRelatedSlave = function(slave, filterFunction) { - if (!slave || !SugarCube) { + if (!slave) { return undefined; } if (typeof filterFunction !== 'function') { - filterFunction = function( /* s, index, array*/ ) { - return true; - }; + filterFunction = () => true; } - const arr = V.slaves.filter(filterFunction); - arr.shuffle(); - return arr.find(function(s) { - return areSisters(slave, s) || - slave.ID === s.mother || - slave.ID === s.father || - s.ID === slave.mother || - s.ID === slave.father; - }); + const arr = V.slaves.filter((s) => areRelated(slave, s) && filterFunction(s)); + return arr.random(); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomRelatedAvailableSlave = function(slave) { - return randomRelatedSlave(slave, function(s) { - return isSlaveAvailable(s); - }); + return randomRelatedSlave(slave, (s) => isSlaveAvailable(s)); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomSister = function(slave) { - return randomRelatedSlave(slave, function(s) { - return areSisters(slave, s); - }); + return randomRelatedSlave(slave, (s) => areSisters(slave, s) > 0); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomTwinSister = function(slave) { - return randomRelatedSlave(slave, function(s) { - return areSisters(slave, s); - }); + return randomRelatedSlave(slave, (s) => areSisters(slave, s) === 1); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomAvailableSister = function(slave) { - return randomRelatedSlave(slave, function(s) { - return isSlaveAvailable(s) && areSisters(slave, s); - }); -}; - -/** - * @param {App.Entity.SlaveState} slave - * @returns {App.Entity.SlaveState} - */ -globalThis.randomAvailableTwinSister = function(slave) { - return randomRelatedSlave(slave, function(s) { - return isSlaveAvailable(s) && areSisters(slave, s); - }); + return randomRelatedSlave(slave, (s) => isSlaveAvailable(s) && areSisters(slave, s) > 0); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomDaughter = function(slave) { - return randomRelatedSlave(slave, function(s) { - return s.mother === slave.ID || s.father === slave.ID; - }); + return randomRelatedSlave(slave, (s) => isParentP(s, slave)); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomAvailableDaughter = function(slave) { - return randomRelatedSlave(slave, function(s) { - return isSlaveAvailable(s) && (s.mother === slave.ID || s.father === slave.ID); - }); + return randomRelatedSlave(slave, (s) => isSlaveAvailable(s) && isParentP(s, slave)); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomParent = function(slave) { - return randomRelatedSlave(slave, function(s) { - return s.ID === slave.mother || s.ID === slave.father; - }); + return randomRelatedSlave(slave, (s) => isParentP(slave, s)); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {App.Entity.SlaveState} */ globalThis.randomAvailableParent = function(slave) { - return randomRelatedSlave(slave, function(s) { - return isSlaveAvailable(s) && (s.ID === slave.mother || s.ID === slave.father); - }); + return randomRelatedSlave(slave, (s) => isSlaveAvailable(s) && isParentP(slave, s)); }; /** - * @param {App.Entity.SlaveState} slave + * @param {Relative} slave * @returns {object} */ globalThis.availableRelatives = function(slave) { @@ -400,8 +418,8 @@ globalThis.totalPlayerRelatives = function(pc) { /** * Returns the term for slave2's relationship to slave1 (i.e. "daughter" if slave2 is slave1's daughter). * Performs distant relative checking if enabled. - * @param {App.Entity.SlaveState} slave1 - * @param {App.Entity.SlaveState} slave2 + * @param {Relative} slave1 + * @param {Relative} slave2 * @returns {string|null} - returns null if the slaves are not related, even distantly. */ globalThis.relativeTerm = function(slave1, slave2) { @@ -492,17 +510,15 @@ globalThis.resetFamilyCounters = function() { } }; -/** Set any missing parents to a known ID for the given slave (usually so that a sibling can be safely generated) - * @param {App.Entity.SlaveState} slave +/** Set any missing parents to a known ID for the given entity (usually so that a sibling can be safely generated) + * @param {Relative} slave */ globalThis.setMissingParents = function(slave) { - function untraceableParentID(ID) { return ID === 0 || (ID < -1 && ID >= -20 && ID !== -3); } - - if (untraceableParentID(slave.mother)) { + if (!specificMom(slave)) { slave.mother = V.missingParentID; V.missingParentID--; } - if (untraceableParentID(slave.father)) { + if (!specificDad(slave)) { slave.father = V.missingParentID; V.missingParentID--; }