diff --git a/src/js/SetBellySize.js b/src/js/SetBellySize.tw similarity index 100% rename from src/js/SetBellySize.js rename to src/js/SetBellySize.tw diff --git a/src/js/assignJS.js b/src/js/assignJS.tw similarity index 100% rename from src/js/assignJS.js rename to src/js/assignJS.tw diff --git a/src/js/raJS.tw b/src/js/raJS.tw deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/js/rulesAssistant.js b/src/js/rulesAssistant.js deleted file mode 100644 index 85931119d7b75a3965a19c0cced8c0c524fc0051..0000000000000000000000000000000000000000 --- a/src/js/rulesAssistant.js +++ /dev/null @@ -1,663 +0,0 @@ -:: rulesAssistant [script] - -// RAJS - -window.ruleApplied = function(slave, ID) { - if (!slave || !slave.currentRules) - return null; - return slave.currentRules.includes(ID); -}; - -window.ruleSlaveSelected = function(slave, rule) { - if (!slave || !rule || !rule.selectedSlaves) - return false; - return rule.selectedSlaves.includes(slave.ID); -}; - -window.ruleSlaveExcluded = function(slave, rule) { - if (!slave || !rule || !rule.excludedSlaves) - return false; - return rule.excludedSlaves.includes(slave.ID); -}; - -window.ruleAssignmentSelected = function(slave, rule) { - if (!slave || !rule || (!rule.assignment && !rule.facility)) - return false; - var assignment = rule.assignment.concat(expandFacilityAssignments(rule.facility)); - return assignment.includes(slave.assignment); -} - -window.ruleAssignmentExcluded = function(slave, rule) { - if (!slave || !rule || (!rule.excludeAssignment && !rule.excludeFacility)) - return false; - var excludeAssignment = rule.excludeAssignment.concat(expandFacilityAssignments(rule.excludeFacility)); - return excludeAssignment.includes(slave.assignment); -} - -window.hasSurgeryRule = function(slave, rules) { - if (!slave || !rules || !slave.currentRules) - return false; - - for (var d = rules.length-1; d >= 0; d--) { - if (ruleApplied(slave, rules[d].ID)) { - if (rules[d].autoSurgery > 0) { - return true; - } - } - } - return false; -}; - -window.hasRuleFor = function(slave, rules, what) { - if (!slave || !rules || !slave.currentRules) - return false; - - for (var d = rules.length-1; d >= 0; d--) { - if (ruleApplied(slave, rules[d].ID)) { - if (rules[d][what] !== "no default setting") { - return true; - } - } - } - return false; -}; - -window.hasHColorRule = function(slave, rules) { - return hasRuleFor(slave, rules, "hColor"); -} - -window.hasHStyleRule = function(slave, rules) { - return hasRuleFor(slave, rules, "hStyle"); -}; - -window.hasEyeColorRule = function(slave, rules) { - return hasRuleFor(slave, rules, "eyeColor"); -}; - -window.lastPregRule = function(slave, rules) { - if (!slave || !rules) - return null; - if (!slave.currentRules) - return false; - - for (var d = rules.length-1; d >= 0; d--) { - if (ruleApplied(slave, rules[d].ID)) { - if (rules[d].preg == -1) { - return true; - } - } - } - - return null; -}; - -window.autoSurgerySelector = function(slave, ruleset) { - - var appRules = ruleset.filter(function(rule){ - return (rule.autoSurgery == 1) && this.currentRules.contains(rule.ID); - }, slave); - - var surgery = {eyes: "no default setting", lactation: "no default setting", cosmetic: "nds", accent: "no default setting", shoulders: "no default setting", shouldersImplant: "no default setting", boobs: "no default setting", hips: "no default setting", hipsImplant: "no default setting", butt: "no default setting", faceShape: "no default setting", lips: "no default setting", holes: "nds", bodyhair: "nds", hair: "nds", bellyImplant: "no default setting"}; - var i, key, ruleSurgery; - - for (i in appRules) - { - ruleSurgery = appRules[i].surgery; - for (key in ruleSurgery) - { - if (ruleSurgery[key] != "no default setting" || ruleSurgery[key] != "nds") - { - surgery[key] = ruleSurgery[key]; - } - } - } - - return surgery; -} - -window.mergeRules = function(rules) { - var combinedRule = {}; - - for (var i = 0; i < rules.length; i++) { - for (var prop in rules[i]) { - // A rule overrides any preceding ones if, - // * there are no preceding ones, - // * or it sets autoBrand, - // * or it does not set autoBrand and is not "no default setting" - var applies = ( - combinedRule[prop] === undefined - || (prop === "autoBrand" && rules[i][prop]) - || (prop !== "autoBrand" && rules[i][prop] !== "no default setting") - ); - - if (applies) - { - - //Objects in JS in operations "=" pass by reference, so we need a completely new object to avoid messing up previous rules. - if ("object" == typeof rules[i][prop] && "object" != typeof combinedRule[prop]) - combinedRule[prop] = new Object(); - - //If we already have object - now we will process its properties, but object itself should be skipped. - if ("object" != typeof combinedRule[prop]) - combinedRule[prop] = rules[i][prop]; - - /*Some properties of rules now have second level properties. We need to check it, and change ones in combinedRule. (Good example - growth drugs. Breasts, butt, etc...) */ - if ( "object" == typeof rules[i][prop]) - { - for (var subprop in rules[i][prop]) - { - var subapplies = ( - combinedRule[prop][subprop] === undefined - || (rules[i][prop][subprop] !== "no default setting") - ); - - if (subapplies) - combinedRule[prop][subprop] = rules[i][prop][subprop]; - } - - } - } - - } - - } - return combinedRule; -} - -// rulesassistant.tw - -function panic(message) { - message = message || "panic"; - if (typeof Error !== "undefined") { - throw new Error(message); - } - throw message; -} - -function assert(condition, message) { - if (!condition) - panic(message || "Assertion failed"); -} - - -window.isSimpleCondition = function(expr, validNames) { - assert(validNames, "validNames was not given"); - - switch (expr.id) { - case "true": case "false": - return true; - - case "<": case "<=": case ">": case ">=": - return ( - expr.first.id === "(name)" // first operand should be a name - && validNames.includes(expr.first.name) // among the valid ones - && expr.second.id === "(number)" // and second should be a literal - ); - - case "&&": case "||": - return ( - (expr.first.id == "<" || expr.first.id == "<=") - && (expr.second.id == ">" || expr.second.id == ">=") - && isSimpleCondition(expr.first, validNames) - && isSimpleCondition(expr.second, validNames) - && expr.first.first.name === expr.second.first.name - ); - } - return false; -} - - -window.getVariable = function(expr) { - switch (expr.id) { - case "true": - return "always"; - case "false": - return "none"; - case "<": case "<=": case ">": case ">=": - return expr.first.name === "energy" ? "sex drive" : expr.first.name; - case "&&": case "||": - return getVariable(expr.first); - } -} - -window.changeVariable = function(expr, newVar) { - //assert(isSimpleCondition(expr), "expr is not simple"); - - switch (expr.id) { - case "true": case "false": - return { - id: "<", - first: {id: "(name)", name: newVar}, - second: {id: "(number)", value: 0} - }; - - case "<": case "<=": case ">": case ">=": - expr.first.name = newVar; - return expr; - - case "&&": case "||": - expr.first.first.name = newVar; - expr.second.first.name = newVar; - return expr; - } -} - - -window.changeComparison = function(expr, newComparison) { - assert(expr.id !== "true" && expr.id !== "false", "expr is constant"); - //assert(isSimpleCondition(expr), "expr is not simple"); - - var newOperand = { - id: newComparison, - first: {id: "(name)", name: expr.first.name}, - second: {id: "(number)", value: 0} - }; - - if (newComparison === "<" || newComparison === "<=") { - switch (expr.id) { - case "<": case "<=": - expr.id = newComparison; - return expr; - case ">": case ">=": - return { id: "&&", first: newOperand, second: expr }; - case "&&": case "||": - expr.first.id = newComparison; - return expr; - } - } else { - switch (expr.id) { - case "<": case "<=": - return { id: "&&", first: expr, second: newOperand }; - case ">": case ">=": - expr.id = newComparison; - return expr; - case "&&": case "||": - expr.second.id = newComparison; - return expr; - } - } -} - -window.removeComparison = function(expr, comparisonType) { - assert(expr.id !== "true" && expr.id !== "false", "expr is constant"); - //assert(isSimpleCondition(expr), "expr is not simple"); - assert(comparisonType === "lower" || comparisonType === "upper", - "invalid comparisonType '" + comparisonType + "'"); - - if (comparisonType === "lower") { - switch (expr.id) { - case "<": case "<=": - return expr; - case ">": case ">=": - return { id: "true" }; - case "&&": case "||": - return expr.first; - } - } else { - switch (expr.id) { - case "<": case "<=": - return { id: "false" }; - case ">": case ">=": - return expr; - case "&&": case "||": - return expr.second; - } - } -} - - -window.changeConnective = function(expr, newConnective) { - switch (expr.id) { - case "true": case "false": - case "<": case "<=": case ">": case ">=": - return expr; - case "&&": case "||": - expr.id = newConnective; - return expr; - } -} - - -window.unparseExpr = function(expr) { - switch (expr.id) { - - // literals - case "true": - return "true"; - case "false": - return "false"; - case "(number)": case "(string)": - return expr.value; - - // names - case "(name)": - return expr.name; - - // logical infix operators - case "&&": case "||": - // numerical infix comperators - case "<": case "<=": - case ">": case ">=": - case "=": case "!=": - // numerical infix operators (excluding minus) - case "+": case "*": case "/": case "^": - return [unparseExpr(expr.first), - expr.id, - unparseExpr(expr.second)].join(" "); - - // unary/prefix operators - case "!": - return expr.id + unparseExpr(expr.first); - - case "-": - if (expr.second !== undefined) { - return unparseExpr(expr.first) + " - " + unparseExpr(expr.second); - } else { - return "-" + unparseExpr(expr.first); - } - - // parentheses - case "(": - return "(" + unparseExpr(expr.first) + ")"; - } - - panic("how did I get here? unknown expr.id: " + expr.id); -} - -window.typeExpr = function(expr, env) { - switch (expr.id) { - - case "true": case "false": - return "bool"; - case "(number)": - return "number"; - case "(string)": - return "string"; - - case "(name)": - return env[expr.name]; - - case "&&": case "||": - return "bool"; - - case "<": case "<=": - case ">": case ">=": - return "bool"; - case "=": case "!=": - return "bool"; - - case "*": case "/": case "^": - return "number"; - - case "+": - return typeExpr(expr.first, env) === "string" ? "string" : "number"; - - case "!": - return typeExpr(expr.first, env); - case "-": - if (expr.second !== undefined) { - return "number"; - } else { - return typeExpr(expr.first, env); - } - - case "(": - return typeExpr(expr.first, env); - } -} - -window.optimizeExpr = function(expr) { - switch (expr.id) { - case "true": case "false": - case "(number)": case "(string)": - case "(name)": - return expr; - - case "-": - // The only "optimization" we are doing. Obviously, this is not done - // for the sake of speed, rather, to make the UI and isSimpleCondition - // simpler, since they don't have to explicitly check for negative - // numbers. - if (expr.second === undefined && expr.first.id === "(number)") - return {id: "(number)", value: evalExpr(expr)}; - // fallthrough, if the minus was not unary - - case "&&": case "||": - case "<": case "<=": case ">": case ">=": - case "=": case "!=": - case "+": case "*": case "/": case "^": - return { - id: expr.id, - first: optimizeExpr(expr.first), - second: optimizeExpr(expr.second) - }; - - case "!": case "(": - return { - id: expr.id, - first: optimizeExpr(expr.first) - }; - } -} - -window.printError = function(exprStr, error) { - var result = [], - inError = false; - for (var i = 0; i < exprStr.length; i++) { - if (i === error.index) - result.push("@@.red;"); - result.push(exprStr[i]); - } - result.push("@@"); - return result.join(""); -} - -window.CheckAutoRulesActivate = function CheckAutoRulesActivate(slave) { - const V = State.variables - let r = "" - - if (slave.currentRules === undefined || slave.currentRules.length < 1) - slave.currentRules = [] - - - V.defaultRules.forEach(rule => { - let applies = evalExpr(rule, slave) - if (applies) { - if (!ruleApplied(slave, rule.ID)) { - slave.currentRules.push(rule.ID) - r += `<br><span class="tan">Rule ${rule.ID} (${rule.name}) now applies to ${slave.slaveName}, who is assigned to ${slave.assignment}</span>\n` - } - } else if (ruleApplied(slave, rule.ID)) { - r += RARemoveRule(slave, rule) - } - }) - return r -} - -window.RARemoveRule = function RARemoveRule(slave, rule) { - let r = "" - const idx = slave.currentRules.indexOf( - slave.currentRules.some((rule, i) => { - if (rule.ID === rule) { - slave.currentRules.splice(i, 1) - r += `<br><span class="tan">Rule ${rule.ID} (${rule.name}) no longer applies to {slave.slaveName}, who is assignmed to ${slave.assignment}</span>` - r += RAFacilityRemove(slave, rule) - return true - } else return false - }) - return r -} - -window.RAFacilityRemove = function RAFacilityRemove(slave, rule) { - const V = State.variables - let r = "" - if (!rule.facilityRemove) return r - switch(rule.setAssignment) { - case "be confined in the arcade": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.arcadeName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "work in the brothel": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.brothelName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "serve in the club": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${clubName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "work in the dairy": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.dairyName} and has been assigned to $rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "rest in the spa": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.spaName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "get treatment in the clinic": - if (slave.assignment === rule.setAssignment) { - r += `<br>{slave.slaveName} has been removed from ${V.clinicName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "serve in the master suite": - if (slave.assignment === rule.setAssignment) { - r += `<br>{slave.slaveName} has been removed from ${V.masterSuiteName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "live with your Head Girl": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${HGSuiteName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "work as a servant": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.servantsQuartersName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "learn in the schoolroom": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.schoolroomName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - - case "be confined in the cellblock": - if (slave.assignment === rule.setAssignment) { - r += `<br>${slave.slaveName} has been removed from ${V.cellblockName} and has been assigned to ${rule.removalAssignment}.` - assignJob(slave, rule.removalAssignment) - } - break; - } -} - -window.evalExpr = function(rule, slave) { - let flag - const expr = rule.condition - - switch (expr.id) { - case "true": - flag = true; - break - case "false": - flag = false; - break - case "(number)": - flag = expr.value; - break - case "(string)": - if(expr.value.startsWith('"') && expr.value.endsWith('"')) { - flag = JSON.parse(expr.value); - } else { - flag = expr.value; - } - break - case "(name)": - flag = slave[expr.name]; - break - case "&&": - flag = evalExpr(expr.first, slave) && evalExpr(expr.second, slave); - break - case "||": - flag = evalExpr(expr.first, slave) || evalExpr(expr.second, slave); - break - case "<": - flag = evalExpr(expr.first, slave) < evalExpr(expr.second, slave); - break - case "<=": - flag = evalExpr(expr.first, slave) <= evalExpr(expr.second, slave); - break - case ">": - flag = evalExpr(expr.first, slave) > evalExpr(expr.second, slave); - break - case ">=": - flag = evalExpr(expr.first, slave) >= evalExpr(expr.second, slave); - break - case "=": - flag = evalExpr(expr.first, slave) == evalExpr(expr.second, slave); - break - case "!=": - flag = evalExpr(expr.first, slave) != evalExpr(expr.second, slave); - break - case "+": - flag = evalExpr(expr.first, slave) + evalExpr(expr.second, slave); - break - case "*": - flag = evalExpr(expr.first, slave) * evalExpr(expr.second, slave); - break - case "/": - flag = evalExpr(expr.first, slave) / evalExpr(expr.second, slave); - break - case "^": - flag = Math.pow(evalExpr(expr.first, slave), evalExpr(expr.second, slave)); - break - case "!": - flag = !evalExpr(expr.first, slave); - break - case "-": - if (expr.second !== undefined) { - flag = evalExpr(expr.first, slave) - evalExpr(expr.second, slave); - } else { - flag = -evalExpr(expr.first, slave); - } - break - case "(": - flag = evalExpr(expr.first, slave); - break - } - - if (flag && rule.excludeSpecialSlaves && isLeaderP(slave)) - flag = false - else if (flag) { - if (rule.assignment.length > 0 || rule.facility.length > 0) - flag = ruleAssignmentSelected(slave, rule) - else if (rule.excludeAssignment.length > 0 || rule.excludeFacility.length > 0) - flag = !ruleAssignmentExcluded(slave, rule) - if (rule.selectedSlaves.length > 0) - flag = ruleSlaveSelected(slave, rule) - else if (rule.excludedSlaves.length > 0) - flag = !ruleSlaveExcluded(slave, rule) - } - return flag -} diff --git a/src/js/rulesAssistant.tw b/src/js/rulesAssistant.tw index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..85931119d7b75a3965a19c0cced8c0c524fc0051 100644 --- a/src/js/rulesAssistant.tw +++ b/src/js/rulesAssistant.tw @@ -0,0 +1,663 @@ +:: rulesAssistant [script] + +// RAJS + +window.ruleApplied = function(slave, ID) { + if (!slave || !slave.currentRules) + return null; + return slave.currentRules.includes(ID); +}; + +window.ruleSlaveSelected = function(slave, rule) { + if (!slave || !rule || !rule.selectedSlaves) + return false; + return rule.selectedSlaves.includes(slave.ID); +}; + +window.ruleSlaveExcluded = function(slave, rule) { + if (!slave || !rule || !rule.excludedSlaves) + return false; + return rule.excludedSlaves.includes(slave.ID); +}; + +window.ruleAssignmentSelected = function(slave, rule) { + if (!slave || !rule || (!rule.assignment && !rule.facility)) + return false; + var assignment = rule.assignment.concat(expandFacilityAssignments(rule.facility)); + return assignment.includes(slave.assignment); +} + +window.ruleAssignmentExcluded = function(slave, rule) { + if (!slave || !rule || (!rule.excludeAssignment && !rule.excludeFacility)) + return false; + var excludeAssignment = rule.excludeAssignment.concat(expandFacilityAssignments(rule.excludeFacility)); + return excludeAssignment.includes(slave.assignment); +} + +window.hasSurgeryRule = function(slave, rules) { + if (!slave || !rules || !slave.currentRules) + return false; + + for (var d = rules.length-1; d >= 0; d--) { + if (ruleApplied(slave, rules[d].ID)) { + if (rules[d].autoSurgery > 0) { + return true; + } + } + } + return false; +}; + +window.hasRuleFor = function(slave, rules, what) { + if (!slave || !rules || !slave.currentRules) + return false; + + for (var d = rules.length-1; d >= 0; d--) { + if (ruleApplied(slave, rules[d].ID)) { + if (rules[d][what] !== "no default setting") { + return true; + } + } + } + return false; +}; + +window.hasHColorRule = function(slave, rules) { + return hasRuleFor(slave, rules, "hColor"); +} + +window.hasHStyleRule = function(slave, rules) { + return hasRuleFor(slave, rules, "hStyle"); +}; + +window.hasEyeColorRule = function(slave, rules) { + return hasRuleFor(slave, rules, "eyeColor"); +}; + +window.lastPregRule = function(slave, rules) { + if (!slave || !rules) + return null; + if (!slave.currentRules) + return false; + + for (var d = rules.length-1; d >= 0; d--) { + if (ruleApplied(slave, rules[d].ID)) { + if (rules[d].preg == -1) { + return true; + } + } + } + + return null; +}; + +window.autoSurgerySelector = function(slave, ruleset) { + + var appRules = ruleset.filter(function(rule){ + return (rule.autoSurgery == 1) && this.currentRules.contains(rule.ID); + }, slave); + + var surgery = {eyes: "no default setting", lactation: "no default setting", cosmetic: "nds", accent: "no default setting", shoulders: "no default setting", shouldersImplant: "no default setting", boobs: "no default setting", hips: "no default setting", hipsImplant: "no default setting", butt: "no default setting", faceShape: "no default setting", lips: "no default setting", holes: "nds", bodyhair: "nds", hair: "nds", bellyImplant: "no default setting"}; + var i, key, ruleSurgery; + + for (i in appRules) + { + ruleSurgery = appRules[i].surgery; + for (key in ruleSurgery) + { + if (ruleSurgery[key] != "no default setting" || ruleSurgery[key] != "nds") + { + surgery[key] = ruleSurgery[key]; + } + } + } + + return surgery; +} + +window.mergeRules = function(rules) { + var combinedRule = {}; + + for (var i = 0; i < rules.length; i++) { + for (var prop in rules[i]) { + // A rule overrides any preceding ones if, + // * there are no preceding ones, + // * or it sets autoBrand, + // * or it does not set autoBrand and is not "no default setting" + var applies = ( + combinedRule[prop] === undefined + || (prop === "autoBrand" && rules[i][prop]) + || (prop !== "autoBrand" && rules[i][prop] !== "no default setting") + ); + + if (applies) + { + + //Objects in JS in operations "=" pass by reference, so we need a completely new object to avoid messing up previous rules. + if ("object" == typeof rules[i][prop] && "object" != typeof combinedRule[prop]) + combinedRule[prop] = new Object(); + + //If we already have object - now we will process its properties, but object itself should be skipped. + if ("object" != typeof combinedRule[prop]) + combinedRule[prop] = rules[i][prop]; + + /*Some properties of rules now have second level properties. We need to check it, and change ones in combinedRule. (Good example - growth drugs. Breasts, butt, etc...) */ + if ( "object" == typeof rules[i][prop]) + { + for (var subprop in rules[i][prop]) + { + var subapplies = ( + combinedRule[prop][subprop] === undefined + || (rules[i][prop][subprop] !== "no default setting") + ); + + if (subapplies) + combinedRule[prop][subprop] = rules[i][prop][subprop]; + } + + } + } + + } + + } + return combinedRule; +} + +// rulesassistant.tw + +function panic(message) { + message = message || "panic"; + if (typeof Error !== "undefined") { + throw new Error(message); + } + throw message; +} + +function assert(condition, message) { + if (!condition) + panic(message || "Assertion failed"); +} + + +window.isSimpleCondition = function(expr, validNames) { + assert(validNames, "validNames was not given"); + + switch (expr.id) { + case "true": case "false": + return true; + + case "<": case "<=": case ">": case ">=": + return ( + expr.first.id === "(name)" // first operand should be a name + && validNames.includes(expr.first.name) // among the valid ones + && expr.second.id === "(number)" // and second should be a literal + ); + + case "&&": case "||": + return ( + (expr.first.id == "<" || expr.first.id == "<=") + && (expr.second.id == ">" || expr.second.id == ">=") + && isSimpleCondition(expr.first, validNames) + && isSimpleCondition(expr.second, validNames) + && expr.first.first.name === expr.second.first.name + ); + } + return false; +} + + +window.getVariable = function(expr) { + switch (expr.id) { + case "true": + return "always"; + case "false": + return "none"; + case "<": case "<=": case ">": case ">=": + return expr.first.name === "energy" ? "sex drive" : expr.first.name; + case "&&": case "||": + return getVariable(expr.first); + } +} + +window.changeVariable = function(expr, newVar) { + //assert(isSimpleCondition(expr), "expr is not simple"); + + switch (expr.id) { + case "true": case "false": + return { + id: "<", + first: {id: "(name)", name: newVar}, + second: {id: "(number)", value: 0} + }; + + case "<": case "<=": case ">": case ">=": + expr.first.name = newVar; + return expr; + + case "&&": case "||": + expr.first.first.name = newVar; + expr.second.first.name = newVar; + return expr; + } +} + + +window.changeComparison = function(expr, newComparison) { + assert(expr.id !== "true" && expr.id !== "false", "expr is constant"); + //assert(isSimpleCondition(expr), "expr is not simple"); + + var newOperand = { + id: newComparison, + first: {id: "(name)", name: expr.first.name}, + second: {id: "(number)", value: 0} + }; + + if (newComparison === "<" || newComparison === "<=") { + switch (expr.id) { + case "<": case "<=": + expr.id = newComparison; + return expr; + case ">": case ">=": + return { id: "&&", first: newOperand, second: expr }; + case "&&": case "||": + expr.first.id = newComparison; + return expr; + } + } else { + switch (expr.id) { + case "<": case "<=": + return { id: "&&", first: expr, second: newOperand }; + case ">": case ">=": + expr.id = newComparison; + return expr; + case "&&": case "||": + expr.second.id = newComparison; + return expr; + } + } +} + +window.removeComparison = function(expr, comparisonType) { + assert(expr.id !== "true" && expr.id !== "false", "expr is constant"); + //assert(isSimpleCondition(expr), "expr is not simple"); + assert(comparisonType === "lower" || comparisonType === "upper", + "invalid comparisonType '" + comparisonType + "'"); + + if (comparisonType === "lower") { + switch (expr.id) { + case "<": case "<=": + return expr; + case ">": case ">=": + return { id: "true" }; + case "&&": case "||": + return expr.first; + } + } else { + switch (expr.id) { + case "<": case "<=": + return { id: "false" }; + case ">": case ">=": + return expr; + case "&&": case "||": + return expr.second; + } + } +} + + +window.changeConnective = function(expr, newConnective) { + switch (expr.id) { + case "true": case "false": + case "<": case "<=": case ">": case ">=": + return expr; + case "&&": case "||": + expr.id = newConnective; + return expr; + } +} + + +window.unparseExpr = function(expr) { + switch (expr.id) { + + // literals + case "true": + return "true"; + case "false": + return "false"; + case "(number)": case "(string)": + return expr.value; + + // names + case "(name)": + return expr.name; + + // logical infix operators + case "&&": case "||": + // numerical infix comperators + case "<": case "<=": + case ">": case ">=": + case "=": case "!=": + // numerical infix operators (excluding minus) + case "+": case "*": case "/": case "^": + return [unparseExpr(expr.first), + expr.id, + unparseExpr(expr.second)].join(" "); + + // unary/prefix operators + case "!": + return expr.id + unparseExpr(expr.first); + + case "-": + if (expr.second !== undefined) { + return unparseExpr(expr.first) + " - " + unparseExpr(expr.second); + } else { + return "-" + unparseExpr(expr.first); + } + + // parentheses + case "(": + return "(" + unparseExpr(expr.first) + ")"; + } + + panic("how did I get here? unknown expr.id: " + expr.id); +} + +window.typeExpr = function(expr, env) { + switch (expr.id) { + + case "true": case "false": + return "bool"; + case "(number)": + return "number"; + case "(string)": + return "string"; + + case "(name)": + return env[expr.name]; + + case "&&": case "||": + return "bool"; + + case "<": case "<=": + case ">": case ">=": + return "bool"; + case "=": case "!=": + return "bool"; + + case "*": case "/": case "^": + return "number"; + + case "+": + return typeExpr(expr.first, env) === "string" ? "string" : "number"; + + case "!": + return typeExpr(expr.first, env); + case "-": + if (expr.second !== undefined) { + return "number"; + } else { + return typeExpr(expr.first, env); + } + + case "(": + return typeExpr(expr.first, env); + } +} + +window.optimizeExpr = function(expr) { + switch (expr.id) { + case "true": case "false": + case "(number)": case "(string)": + case "(name)": + return expr; + + case "-": + // The only "optimization" we are doing. Obviously, this is not done + // for the sake of speed, rather, to make the UI and isSimpleCondition + // simpler, since they don't have to explicitly check for negative + // numbers. + if (expr.second === undefined && expr.first.id === "(number)") + return {id: "(number)", value: evalExpr(expr)}; + // fallthrough, if the minus was not unary + + case "&&": case "||": + case "<": case "<=": case ">": case ">=": + case "=": case "!=": + case "+": case "*": case "/": case "^": + return { + id: expr.id, + first: optimizeExpr(expr.first), + second: optimizeExpr(expr.second) + }; + + case "!": case "(": + return { + id: expr.id, + first: optimizeExpr(expr.first) + }; + } +} + +window.printError = function(exprStr, error) { + var result = [], + inError = false; + for (var i = 0; i < exprStr.length; i++) { + if (i === error.index) + result.push("@@.red;"); + result.push(exprStr[i]); + } + result.push("@@"); + return result.join(""); +} + +window.CheckAutoRulesActivate = function CheckAutoRulesActivate(slave) { + const V = State.variables + let r = "" + + if (slave.currentRules === undefined || slave.currentRules.length < 1) + slave.currentRules = [] + + + V.defaultRules.forEach(rule => { + let applies = evalExpr(rule, slave) + if (applies) { + if (!ruleApplied(slave, rule.ID)) { + slave.currentRules.push(rule.ID) + r += `<br><span class="tan">Rule ${rule.ID} (${rule.name}) now applies to ${slave.slaveName}, who is assigned to ${slave.assignment}</span>\n` + } + } else if (ruleApplied(slave, rule.ID)) { + r += RARemoveRule(slave, rule) + } + }) + return r +} + +window.RARemoveRule = function RARemoveRule(slave, rule) { + let r = "" + const idx = slave.currentRules.indexOf( + slave.currentRules.some((rule, i) => { + if (rule.ID === rule) { + slave.currentRules.splice(i, 1) + r += `<br><span class="tan">Rule ${rule.ID} (${rule.name}) no longer applies to {slave.slaveName}, who is assignmed to ${slave.assignment}</span>` + r += RAFacilityRemove(slave, rule) + return true + } else return false + }) + return r +} + +window.RAFacilityRemove = function RAFacilityRemove(slave, rule) { + const V = State.variables + let r = "" + if (!rule.facilityRemove) return r + switch(rule.setAssignment) { + case "be confined in the arcade": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.arcadeName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "work in the brothel": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.brothelName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "serve in the club": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${clubName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "work in the dairy": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.dairyName} and has been assigned to $rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "rest in the spa": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.spaName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "get treatment in the clinic": + if (slave.assignment === rule.setAssignment) { + r += `<br>{slave.slaveName} has been removed from ${V.clinicName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "serve in the master suite": + if (slave.assignment === rule.setAssignment) { + r += `<br>{slave.slaveName} has been removed from ${V.masterSuiteName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "live with your Head Girl": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${HGSuiteName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "work as a servant": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.servantsQuartersName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "learn in the schoolroom": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.schoolroomName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + + case "be confined in the cellblock": + if (slave.assignment === rule.setAssignment) { + r += `<br>${slave.slaveName} has been removed from ${V.cellblockName} and has been assigned to ${rule.removalAssignment}.` + assignJob(slave, rule.removalAssignment) + } + break; + } +} + +window.evalExpr = function(rule, slave) { + let flag + const expr = rule.condition + + switch (expr.id) { + case "true": + flag = true; + break + case "false": + flag = false; + break + case "(number)": + flag = expr.value; + break + case "(string)": + if(expr.value.startsWith('"') && expr.value.endsWith('"')) { + flag = JSON.parse(expr.value); + } else { + flag = expr.value; + } + break + case "(name)": + flag = slave[expr.name]; + break + case "&&": + flag = evalExpr(expr.first, slave) && evalExpr(expr.second, slave); + break + case "||": + flag = evalExpr(expr.first, slave) || evalExpr(expr.second, slave); + break + case "<": + flag = evalExpr(expr.first, slave) < evalExpr(expr.second, slave); + break + case "<=": + flag = evalExpr(expr.first, slave) <= evalExpr(expr.second, slave); + break + case ">": + flag = evalExpr(expr.first, slave) > evalExpr(expr.second, slave); + break + case ">=": + flag = evalExpr(expr.first, slave) >= evalExpr(expr.second, slave); + break + case "=": + flag = evalExpr(expr.first, slave) == evalExpr(expr.second, slave); + break + case "!=": + flag = evalExpr(expr.first, slave) != evalExpr(expr.second, slave); + break + case "+": + flag = evalExpr(expr.first, slave) + evalExpr(expr.second, slave); + break + case "*": + flag = evalExpr(expr.first, slave) * evalExpr(expr.second, slave); + break + case "/": + flag = evalExpr(expr.first, slave) / evalExpr(expr.second, slave); + break + case "^": + flag = Math.pow(evalExpr(expr.first, slave), evalExpr(expr.second, slave)); + break + case "!": + flag = !evalExpr(expr.first, slave); + break + case "-": + if (expr.second !== undefined) { + flag = evalExpr(expr.first, slave) - evalExpr(expr.second, slave); + } else { + flag = -evalExpr(expr.first, slave); + } + break + case "(": + flag = evalExpr(expr.first, slave); + break + } + + if (flag && rule.excludeSpecialSlaves && isLeaderP(slave)) + flag = false + else if (flag) { + if (rule.assignment.length > 0 || rule.facility.length > 0) + flag = ruleAssignmentSelected(slave, rule) + else if (rule.excludeAssignment.length > 0 || rule.excludeFacility.length > 0) + flag = !ruleAssignmentExcluded(slave, rule) + if (rule.selectedSlaves.length > 0) + flag = ruleSlaveSelected(slave, rule) + else if (rule.excludedSlaves.length > 0) + flag = !ruleSlaveExcluded(slave, rule) + } + return flag +}