diff --git a/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw index 5f78ca87af81bba2e840704827e8dc9ee8fc1027..005c0e3a00652c292e405f25d89be27537382be0 100644 --- a/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw +++ b/src/cheats/PCCheatMenuCheatDatatypeCleanup.tw @@ -71,6 +71,7 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the power to reshape your body and life at will. What a cheater! <<set $PC = clone($tempSlave)>> +<<run ibc.recalculate_id(-1)>> <<run PCDatatypeCleanup()>> <<set $upgradeMultiplierArcology = upgradeMultiplier('engineering')>> <<set $upgradeMultiplierMedicine = upgradeMultiplier('medicine')>> diff --git a/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw b/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw index 9ac0b8c1f7773a1ede8b558f13872d319296675a..57449fb187f0d00d37e25675b53595d5492a6238 100644 --- a/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw +++ b/src/cheats/mod_EditChildCheatDatatypeCleanupNew.tw @@ -322,6 +322,7 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the <br><br>This slave has been changed forever and you have lost a bit of your soul, YOU CHEATER! <<set $activeSlave = clone($tempSlave)>> +<<run ibc.recalculate_coeff_id($activeSlave.ID)>> <<unset $tempSlave>> <<set _escn = $cribs.findIndex(function(s) { s.ID === $activeSlave.ID; })>> <<if def _escn>> diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw index 259d748bfbaa74c177b5e2c72ed4ca88aa5e50b6..f07890401afb50ae8ecaadb056f521e72f7fa687 100644 --- a/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw +++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanup.tw @@ -66,6 +66,7 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the <br><br>This slave has been changed forever and you have lost a bit of your soul, YOU CHEATER! <<set $activeSlave = clone($tempSlave)>> +<<run ibc.recalculate_coeff_id($activeSlave.ID)>> <<unset $tempSlave>> <<set _escdc = $slaveIndices[$activeSlave.ID]>> <<if def _escdc>> diff --git a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw index 3dd6e0e84971868e55ea3238e7bfc1ec30e46916..196caf23c024e965949b048d0024c81c1671903c 100644 --- a/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw +++ b/src/cheats/mod_EditSlaveCheatDatatypeCleanupNew.tw @@ -225,6 +225,7 @@ You perform the dark rituals, pray to the dark gods, and sell your soul for the <br><br>This slave has been changed forever and you have lost a bit of your soul, YOU CHEATER! <<set $activeSlave = clone($tempSlave)>> +<<run ibc.recalculate_coeff_id($activeSlave.ID)>> <<unset $tempSlave>> <<set _escn = $slaveIndices[$activeSlave.ID]>> <<if def _escn>> diff --git a/src/js/ibcJS.js b/src/js/ibcJS.js index 06e70855537fe7b49b8bb132d969665d4b19033f..fbd652e6f55e1031dc867ea121bd72269c471bab 100644 --- a/src/js/ibcJS.js +++ b/src/js/ibcJS.js @@ -256,7 +256,7 @@ globalThis.ibc = (() => { }; // Make nodes for an array of slaves - let nodes_slaves = slaves => { + let nodes_slaves = (slaves, ignore_coeffs=false) => { let nodes = {}; // Recursively create the nodes we need, moving upwards from the given slave @@ -293,7 +293,7 @@ globalThis.ibc = (() => { // Try to use a cached CoI for performance let sg = find_gp(s.ID); - if (sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) { + if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) { nodes[s.ID]._coeff = sg.inbreedingCoeff; } }; @@ -309,12 +309,12 @@ globalThis.ibc = (() => { // Determine the coefficients of inbreeding of an array of slaves. Returns a mapping of their // ID to their coefficient of inbreeding - let coeff_slaves = slaves => { + let coeff_slaves = (slaves, ignore_coeffs=false) => { let ret = {}; // First, pull as many existing CoI off the slaves slaves.forEach(s => { let sg = find_gp(s.ID); - if (sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) { + if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) { ret[s.ID] = sg.inbreedingCoeff; } }); @@ -322,7 +322,7 @@ globalThis.ibc = (() => { // Now do any we haven't done already slaves = slaves.filter(s => (!(s.ID in ret))); if (slaves.length > 0) { - let nodes = nodes_slaves(slaves); + let nodes = nodes_slaves(slaves, ignore_coeffs); // Compute coefficients slaves.forEach(s => { @@ -334,19 +334,20 @@ globalThis.ibc = (() => { }; // Determine the kinship between two slaves `a` and `b` - let kinship_slaves = (a, b) => { - let nodes = nodes_slaves([a, b]); + let kinship_slaves = (a, b, ignore_coeffs=false) => { + if (a === 0 || b === 0) + return 0; - return kinship(nodes[a.ID], nodes[b.ID]); + return kinship_one_many(a, [b], ignore_coeffs); }; // Determine the coefficient of inbreeding of a single slave - let coeff_slave = slave => { - if ("inbreedingCoeff" in slave && slave.inbreedingCoeff !== -1) + let coeff_slave = (slave, ignore_coeffs=false) => { + if (!ignore_coeffs && "inbreedingCoeff" in slave && slave.inbreedingCoeff !== -1) return slave.inbreedingCoeff; let gp = find_gp(slave.ID); - if (gp !== null && "inbreedingCoeff" in gp && gp.inbreedingCoeff !== -1) + if (!ignore_coeffs && gp !== null && "inbreedingCoeff" in gp && gp.inbreedingCoeff !== -1) return gp.inbreedingCoeff; return coeff_slaves([slave])[slave.ID]; @@ -354,21 +355,140 @@ globalThis.ibc = (() => { // Determine the kinship between one and many slaves. Returns an mapping from the ID of each of // the slaves in `others` to its kinship with slave `a` - let kinship_one_many = (a, others) => { - let nodes = nodes_slaves(others.concat([a])); + let kinship_one_many = (a, others, ignore_coeffs=false) => { + if (a === 0) { + let ks = {}; + others.forEach(s => { + if (s === 0) + ks[s] = 0; + else + ks[s.ID] = 0; + }); + return ks; + } + + let nodes = nodes_slaves(others.concat([a]), ignore_coeffs); let ks = {}; others.forEach(s => { - ks[s.ID] = kinship(nodes[a.ID], nodes[s.ID]); + if (s === -3) { + // Fake a slave object for the player's old master + s = {ID: -3, mother: 0, father: 0, inbreedingCoeff: 0}; + } + + if (s === 0) + ks[s] = 0; + else + ks[s.ID] = kinship(nodes[a.ID], nodes[s.ID]); }); return ks; }; + // Recalculate the inbreeding coefficient for all slaves dependent on the passed IDs (e.g. the + // slaves themselves and all of their children). This will replace the inbreeding coefficients + // wherever they exist with the computed values, ignoring all cached values. + // + // This should be called if parents are changed. + let recalculate_coeff_ids = (ids) => { + // These are all the slave-like objects, i.e. they have ID, mother, and father. There will + // be multiple elements with the same ID: we want this, since we have to replace all + // occurrences of the COI for the affected slaves + let all_slave_like = V.slaves.concat(V.genePool).concat(V.cribs).concat(V.tanks).concat(Object.values(V.missingTable)); + if (V.boomerangSlave !== 0) + all_slave_like.push(V.boomerangSlave); + if (V.traitor !== 0) + all_slave_like.push(V.traitor); + if (V.activeSlave !== 0) + all_slave_like.push(V.activeSlave); + all_slave_like.push(V.PC); + // Add a fake entry for the PC's old master + all_slave_like.push({ID: -3, mother: 0, father: 0, inbreedingCoeff: 0}); + + // Gather the genetics of all current fetuses + let all_fetuses = V.slaves.filter(s => s.preg > 0).map(s => s.womb.map(i => i.genetics)).reduce((res, cur) => res.concat(cur), []); + + // Recursively find all of the given ID's children, born and unborn + let find_children_rec = (id, cur_slaves, cur_fetuses, cur_fetus_parents) => { + // Add fetuses + all_fetuses.filter(f => (f.father === id || f.mother === id)).forEach(f => { + // We may have to manually add the parents later + let actual_f = or_null(f.father); + let actual_m = or_null(f.mother); + if (actual_f !== null) + cur_fetus_parents.add(actual_f); + if (actual_m !== null) + cur_fetus_parents.add(actual_m); + + cur_fetuses.add(f); + }); + + // Recursively add slaves + all_slave_like.filter(s => (s.father === id || s.mother === id)).forEach(s => { + if (!cur_slaves.has(s.ID)) { + cur_slaves.add(s.ID); + find_children_rec(s.ID, cur_slaves, cur_fetuses, cur_fetus_parents); + } + }); + }; + + // We only need slave IDs, since we have to update all of their entries (including GP) + let needed_slave_ids = new Set(); + // Since each fetus has a unique record, a set still suffices + let needed_fetuses = new Set(); + let needed_parent_ids = new Set(); + + // Find all the children of the IDs we need to do + ids.forEach(id => { + needed_slave_ids.add(id); + find_children_rec(id, needed_slave_ids, needed_fetuses, needed_parent_ids); + }); + + // Now we assemble the tree from the slaves + let needed_slaves = []; + needed_slave_ids.forEach(id => { + if (typeof id !== "undefined") + needed_slaves.push(all_slave_like.find(s => s.ID === id)); + }); + needed_parent_ids.forEach(id => { + if (typeof id !== "undefined" && !needed_slave_ids.has(id)) + needed_slaves.push(all_slave_like.find(s => s.ID == id)); + }); + let nodes = nodes_slaves(needed_slaves, true); + + // Now calculate the inbreeding coefficients (they're cached in the tree once calculated) + all_slave_like.filter(s => needed_slave_ids.has(s.ID)).forEach(s => { + s.inbreedingCoeff = coeff(nodes[s.ID]); + }); + + // Finally, handle all of the kinship for the fetuses + let kinship_cache = new Map(); // Manually cache it + needed_fetuses.forEach(f => { + if (or_null(f.mother) === null || or_null(f.father) === null) { + f.inbreedingCoeff = 0; + return; + } + + // Use a string of the form "parent;parent" to store the cache value; since kinship is + // commutative, the minumum parent ID will be first + let kinship_str = Math.min(f.mother, f.father) + ';' + Math.max(f.mother, f.father); + if (!kinship_cache.has(kinship_str)) + kinship_cache.set(kinship_str, kinship(nodes[f.mother], nodes[f.father])); + + f.inbreedingCoeff = kinship_cache.get(kinship_str); + }); + }; + + let recalculate_coeff_id = (id) => { + return recalculate_coeff_ids([id]); + }; + return { coeff: coeff_slave, coeff_slaves, kinship: kinship_slaves, - kinship_one_many + kinship_one_many, + recalculate_coeff_ids, + recalculate_coeff_id }; })();