diff --git a/src/004-base/proxies.js b/src/004-base/proxies.js index 9ce94c7366f622e34bf28802f616152f3dc35320..e271aa87d62bcb6d30f0f25ed1fc674a65537614 100644 --- a/src/004-base/proxies.js +++ b/src/004-base/proxies.js @@ -1,36 +1,93 @@ window.createReadonlyProxy = function(target) { if (target.__isReadonlyProxy) { return target; } - return new Proxy(target, { - get: function(o, prop) { - if (prop == '__isReadonlyProxy') { return true; } - return createReadonlyProxy(o[prop]); - }, - set: function(o, prop, value) { - return false; - }, - deleteProperty: function(o, prop) { - return false; - } - }); + if (_.isArray(target)) { + return new Proxy(target, { + get:function(o, prop) { + const val = o[prop]; + if (typeof val === 'function') { + if (['push', 'unshift', 'pop'].includes(prop)) { + return function () { + throw Error("Cannot modify a readonly array"); + } + } + return val.bind(target); + } + return createReadonlyProxy(val); + }, + set:function(o, prop, value) { + return true; + }, + deleteProperty:function(o, prop) { + return true; + } + }); + } + if (_.isObject(target)) { + return new Proxy(target, { + get:function(o, prop) { + if(prop == '__isReadonlyProxy') { return true; } + return createReadonlyProxy(o[prop]); + }, + set:function(o, prop, value) { + return true; + }, + deleteProperty:function(o, prop) { + return true; + } + }); + } + return target; }; window.createCheatProxy = function(target) { if (target.__isCheatProxy) { return target; } - return new Proxy(target, { - get: function(o, prop) { - if (prop == '__isCheatProxy') { return true; } - return createCheatProxy(o[prop]); - }, - set: function(o, prop, value) { - o[prop] = value; - State.variables.cheater = 1; - return true; - }, - deleteProperty: function(o, prop) { - delete o[prop]; - State.variables.cheater = 1; - return false; - } - }); + if (_.isArray(target)) { + return new Proxy(target, { + get:function(o, prop) { + const val = o[prop]; + if (typeof val === 'function') { + if (['push', 'unshift', 'pop'].includes(prop)) { + return function (el) { + const retval = Array.prototype[prop].apply(o, arguments); + //Make sure we set cheater after calling the function + State.variables.cheater = 1; + return retval; + } + } + return val.bind(target); + } + return createCheatProxy(val); + }, + set:function(o, prop, value) { + o[prop] = value; + State.variables.cheater = 1; + return true; + }, + deleteProperty:function(o, prop) { + delete o[prop]; + State.variables.cheater = 1; + return true; + } + }); + } + if (_.isObject(target)) { + return new Proxy(target, { + get:function(o, prop) { + if(prop == '__isCheatProxy') { return true; } + return createCheatProxy(o[prop]); + }, + set:function(o, prop, value) { + o[prop] = value; + State.variables.cheater = 1; + return true; + }, + deleteProperty:function(o, prop) { + delete o[prop]; + State.variables.cheater = 1; + return true; + } + }); + } + return target; }; Object.defineProperty(window, "V", { get: function() { @@ -38,18 +95,20 @@ Object.defineProperty(window, "V", { return State.variables; } }); +//This should be used if the user might use V under normal, non-cheating circumstances but shouldn't be punished for accidentally setting the value. The attempt to make the change will simply be disregarded. window.runWithReadonlyProxy = function(callback) { window.storyProxy = createReadonlyProxy(State.variables); try { - callback(); + return callback(); } finally { window.storyProxy = null; } }; +// This should be used if setting values would constitute cheating. For example, a debug view that shows all variables in an editable form; showing isn't cheating, but making a change would be. window.runWithCheatProxy = function(callback) { window.storyProxy = createCheatProxy(State.variables); try { - callback(); + return callback(); } finally { window.storyProxy = null; } diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js index ad0ec052a1263b54b0acff0a2969e7f30c922c34..2e340cab4cff0ed901d637e1ad7c54ee46e08fcd 100644 --- a/src/js/DefaultRules.js +++ b/src/js/DefaultRules.js @@ -16,7 +16,8 @@ window.DefaultRules = (function() { if (slave.useRulesAssistant === 0) { return r; } // exempted r = ""; ({he, him, his} = getPronouns(slave)); - let rule = MergeRules(slave); + let slaveReadOnly = createReadonlyProxy(slave); + let rule = runWithReadonlyProxy(()=>MergeRules(slaveReadOnly)); if (Object.keys(rule).length === 0) { return r; } // no rules apply AssignJobToSlave(slave, rule);