@yantra-core/sutra
Version:
A JavaScript behavior tree library for easily creating and managing complex behavior patterns in game development.
1,184 lines (1,125 loc) • 55.4 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SUTRA = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "Sutra", {
enumerable: true,
get: function get() {
return _sutra["default"];
}
});
exports.createSutra = createSutra;
var _sutra = _interopRequireDefault(require("./lib/sutra.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function createSutra() {
return new _sutra["default"]();
}
},{"./lib/sutra.js":11}],2:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = evaluateCompositeCondition;
function evaluateCompositeCondition(conditionObj, data, gameState) {
var _this = this;
var targetData;
if (typeof data === 'function') {
targetData = data(gameState);
} else {
targetData = data;
}
switch (conditionObj.op) {
case 'and':
return conditionObj.conditions.every(function (cond) {
return _this.evaluateCondition(cond, targetData, gameState);
});
case 'or':
return conditionObj.conditions.some(function (cond) {
return _this.evaluateCondition(cond, targetData, gameState);
});
case 'not':
// Assuming 'not' operator has a single condition
return !this.evaluateCondition(conditionObj.conditions, targetData, gameState);
default:
return false;
}
}
},{}],3:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = evaluateCondition;
function evaluateCondition(condition, data, gameState) {
var sutra = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : this;
var targetData;
if (typeof data === 'function') {
targetData = data(gameState);
} else {
targetData = data;
}
// Use the provided Sutra's conditions or default to the current Sutra's conditions
var conditions = sutra.conditions;
if (typeof condition === 'string') {
var conditionEntry = sutra.conditions[condition];
// If not found in the subtree, check in the main Sutra
if (!conditionEntry) {
conditionEntry = this.conditions[condition];
}
if (!conditionEntry) {
// if not found, return false ( for now sub-tree issue )
// return false;
}
if (conditionEntry) {
if (Array.isArray(conditionEntry)) {
return conditionEntry.every(function (cond) {
return typeof cond.func === 'function' ? cond.func(targetData, gameState) : sutra.evaluateDSLCondition(cond.original, targetData, gameState);
});
} else if (['and', 'or', 'not'].includes(conditionEntry.op)) {
// Handling composite conditions
return sutra.evaluateCompositeCondition(conditionEntry, targetData, gameState);
} else {
return sutra.evaluateSingleCondition(conditionEntry, targetData, gameState);
}
} else {
console.log('Warning: Condition not found: ' + condition + '. About to throw an error.\nPlease define the missing condition in your sutra script.');
throw new Error("Condition \"".concat(condition, "\" not found"));
}
} else if (typeof condition === 'function') {
return condition(targetData, gameState);
} else if (Array.isArray(condition)) {
return condition.every(function (cond) {
return sutra.evaluateCondition(cond, targetData, gameState);
});
}
return false;
}
},{}],4:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = evaluateDSLCondition;
function evaluateDSLCondition(conditionObj, data, gameState) {
var _this = this;
var operator = this.resolveOperator(conditionObj.op);
var targetData;
if (typeof data === 'function') {
targetData = data(gameState);
} else {
targetData = data;
}
var targetValue;
if (conditionObj.gamePropertyPath) {
// Use getNestedValue for deeply nested properties in gameState
targetValue = this.getNestedValue(gameState, conditionObj.gamePropertyPath);
} else if (conditionObj.gameProperty) {
// Use gameState for top-level properties
targetValue = gameState[conditionObj.gameProperty];
} else {
// Use data for entity properties
targetValue = targetData[conditionObj.property];
}
if (typeof targetValue === 'undefined') {
targetValue = 0;
}
switch (operator) {
case 'lessThan':
return targetValue < conditionObj.value;
case 'greaterThan':
return targetValue > conditionObj.value;
case 'equals':
return targetValue === conditionObj.value;
case 'notEquals':
return targetValue !== conditionObj.value;
case 'lessThanOrEqual':
return targetValue <= conditionObj.value;
case 'greaterThanOrEqual':
return targetValue >= conditionObj.value;
case 'and':
return conditionObj.conditions.every(function (cond) {
return _this.evaluateDSLCondition(cond, targetData, gameState);
});
case 'or':
return conditionObj.conditions.some(function (cond) {
return _this.evaluateDSLCondition(cond, targetData, gameState);
});
case 'not':
return !this.evaluateDSLCondition(conditionObj.condition, targetData, gameState);
default:
return false;
}
}
},{}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = evaluateSingleCondition;
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function evaluateSingleCondition(condition, data, gameState) {
// logger('Evaluating condition', condition, data);
var targetData;
if (typeof data === 'function') {
targetData = data(gameState);
} else {
targetData = data;
}
if (typeof condition === 'string') {
var conditionEntry = this.conditions[condition];
if (conditionEntry) {
// Handle composite conditions
if (['and', 'or', 'not'].includes(conditionEntry.op)) {
return this.evaluateCompositeCondition(conditionEntry, targetData, gameState);
}
// Handle named function conditions
if (typeof conditionEntry === 'function') {
return conditionEntry(targetData, gameState); // Pass gameState here
}
// Handle DSL conditions
if (_typeof(conditionEntry.original) === 'object') {
return this.evaluateDSLCondition(conditionEntry.original, targetData, gameState); // Pass gameState here
}
}
}
// Handle direct function conditions
if (typeof condition === 'function') {
return condition(targetData, gameState); // Pass gameState here
}
// Handle if condition is array of conditions
/* Remark: needs tests before we should add this, may be working already
if (Array.isArray(condition)) {
return condition.every(cond => this.evaluateCondition(cond, targetData, gameState)); // Pass gameState here
}
*/
// logger('Evaluating unrecognized condition');
return false;
}
},{}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = exportToEnglish;
var _i18n = _interopRequireDefault(require("./i18n.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
// Custom function to format data in a readable manner
var formatData = function formatData(data, indent) {
if (_typeof(data) !== 'object' || data === null) {
return "".concat(indent).concat(data);
}
return Object.entries(data).map(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
var stringValue = '';
if (typeof value === 'function') {
// Get function name from value
stringValue = "".concat(value.name, "()");
} else if (_typeof(value) === 'object' && value !== null) {
// Format nested object
var nestedIndent = indent + ' ';
stringValue = "\n".concat(formatData(value, nestedIndent));
} else {
// Handle non-object values
stringValue = value;
}
return "".concat(indent).concat(key, ": ").concat(stringValue);
}).join('\n');
};
function exportToEnglish() {
var _this = this;
var indentLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var lang = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en';
var langKeywords = _i18n["default"][lang] || _i18n["default"].en; // Fallback to English if the language is not found
var conditionDescriptions = '';
var describeAction = function describeAction(node, indentLevel) {
var currentIndent = ' '.repeat(indentLevel * 2);
var nextIndent = ' '.repeat(indentLevel * 2);
var withIndent = ' '.repeat(indentLevel * 2 + 2); // Indent for "with" label
var dataIndent = ' '.repeat((indentLevel + 2) * 2); // Indent for data
var output = '';
if (node.action) {
return "".concat(currentIndent).concat(node.action);
} else if (node["if"]) {
var ifString;
if (Array.isArray(node["if"])) {
ifString = node["if"].join(" ".concat(langKeywords.andKeyword, " "));
} else {
ifString = node["if"];
}
var description = "".concat(currentIndent).concat(langKeywords.ifKeyword, " ").concat(ifString);
// Process 'then' block
if (node.then) {
var thenDescription = node.then.map(function (childNode) {
var childDesc = describeAction(childNode, indentLevel + 1);
if (childNode.data) {
childDesc += "\n".concat(formatData(childNode.data, dataIndent));
}
return childDesc;
}).join('\n');
description += "\n".concat(thenDescription);
}
// Process 'else' block
if (node["else"]) {
var elseDescription = node["else"].map(function (childNode) {
var childDesc = describeAction(childNode, indentLevel + 1);
if (childNode.data) {
childDesc += "\n".concat(formatData(childNode.data, dataIndent));
}
return childDesc;
}).join('\n');
description += "\n".concat(currentIndent).concat(langKeywords.elseKeyword, "\n").concat(elseDescription);
}
output += description;
}
if (node.subtree) {
var subtree = _this.subtrees[node.subtree];
if (subtree) {
// Recursively call exportToEnglish for the subtree
output += "".concat(currentIndent, " @").concat(node.subtree, "=>\n").concat(exportToEnglish.call(subtree, indentLevel + 1, lang));
}
// If the subtree is not found, return a placeholder message
// output += `${currentIndent}${langKeywords.subtreeKeyword} ${node.subtree} (not found)`;
}
// Combine actions and conditions descriptions
//let output = this.tree.map(node => describeAction(node, indentLevel, this)).join('\n');
// Remark: this will expand *all conditions* inline, which is not ideal, but useful as a config setting
/*
for (let c in this.originalConditions) {
let condition = this.originalConditions[c];
output += `\n\n${c}:\n ${describeCondition(condition)}`;
}
*/
// Add an extra newline for root nodes
if (indentLevel === 0) {
output += '\n';
}
return output;
};
var describeCondition = function describeCondition(condition) {
// Handle condition if it's a function
if (typeof condition === 'function') {
return condition.toString();
}
// Handle condition if it's an array of conditions
if (Array.isArray(condition)) {
return condition.map(function (cond) {
return describeCondition(cond);
}).join(' and ');
}
// Handle condition if it's an object
if (_typeof(condition) === 'object' && condition !== null) {
switch (condition.op) {
case 'eq':
return "".concat(condition.property, " equals ").concat(condition.value);
case 'lessThan':
return "".concat(condition.property, " less than ").concat(condition.value);
case 'greaterThan':
return "".concat(condition.property, " greater than ").concat(condition.value);
case 'and':
return "all of (".concat(condition.conditions.map(function (cond) {
return describeCondition(cond);
}).join(', '), ") are true");
case 'or':
return "any of (".concat(condition.conditions.map(function (cond) {
return describeCondition(cond);
}).join(', '), ") is true");
case 'not':
return "not (".concat(describeCondition(condition.conditions), ")");
// Add more cases as needed for other operators
default:
// console.log('calling default', condition)
return JSON.stringify(condition);
}
}
return JSON.stringify(condition);
};
return this.tree.map(function (node) {
return describeAction(node, indentLevel);
}).join('\n').concat('') + '\n' + conditionDescriptions;
}
},{"./i18n.js":7}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
// Example language configuration object
var languageConfig = {
en: {
// English
ifKeyword: "if",
thenKeyword: "then",
elseKeyword: "else",
andKeyword: "and",
subtreeKeyword: "subtree"
// Add more keywords or phrases as needed
},
zh: {
// Mandarin
ifKeyword: "如果",
// Rúguǒ
thenKeyword: "那么",
// Nàme
elseKeyword: "否则",
// Fǒuzé
andKeyword: "和",
// Hé
subtreeKeyword: "子树" // Zǐshù
},
ja: {
// Japanese
ifKeyword: "もし",
// Moshi
thenKeyword: "ならば",
// Naraba
elseKeyword: "それ以外",
// Sore igai
andKeyword: "と",
// To
subtreeKeyword: "サブツリー" // Sabutsurī
}
// You can add more languages here
};
var _default = exports["default"] = languageConfig;
},{}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _default = exports["default"] = {
eq: 'equals',
'==': 'equals',
neq: 'notEquals',
'!=': 'notEquals',
lt: 'lessThan',
'<': 'lessThan',
lte: 'lessThanOrEqual',
'<=': 'lessThanOrEqual',
gt: 'greaterThan',
'>': 'greaterThan',
gte: 'greaterThanOrEqual',
'>=': 'greaterThanOrEqual',
'&&': 'and',
'||': 'or',
'!': 'not'
};
},{}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = parsePath;
function parsePath(path) {
return path.split('.').reduce(function (acc, part) {
var arrayMatch = part.match(/([^\[]+)(\[\d+\])?/);
if (arrayMatch) {
acc.push(arrayMatch[1]);
if (arrayMatch[2]) {
acc.push(parseInt(arrayMatch[2].replace(/[\[\]]/g, '')));
}
}
return acc;
}, []);
}
},{}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = serializeToJson;
var _excluded = ["parent"];
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function serializeToJson() {
var serializedData = {
tree: this.tree.map(function (node) {
return serializeNode(node);
}),
conditions: {}
};
// Serialize the DSL part of conditions correctly
for (var key in this.conditions) {
if (typeof this.conditions[key] === 'function') {
serializedData.conditions[key] = _typeof(this.conditions[key].original) === 'object' ? this.conditions[key].original : {
type: 'customFunction'
};
}
}
return JSON.stringify(serializedData, null, 2);
}
// Helper function to serialize a single node
function serializeNode(node) {
var parent = node.parent,
serializedNode = _objectWithoutProperties(node, _excluded); // Exclude the parent property
// Recursively serialize 'then' and 'else' branches
if (serializedNode.then && Array.isArray(serializedNode.then)) {
serializedNode.then = serializedNode.then.map(function (childNode) {
return serializeNode(childNode);
});
}
if (serializedNode["else"] && Array.isArray(serializedNode["else"])) {
serializedNode["else"] = serializedNode["else"].map(function (childNode) {
return serializeNode(childNode);
});
}
return serializedNode;
}
},{}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _exportToEnglish = _interopRequireDefault(require("./exportToEnglish.js"));
var _serializeToJson = _interopRequireDefault(require("./serializeToJson.js"));
var _evaluateCondition = _interopRequireDefault(require("./evaluateCondition.js"));
var _evaluateSingleCondition = _interopRequireDefault(require("./evaluateSingleCondition.js"));
var _evaluateDSLCondition = _interopRequireDefault(require("./evaluateDSLCondition.js"));
var _evaluateCompositeCondition = _interopRequireDefault(require("./evaluateCompositeCondition.js"));
var _parsePath = _interopRequireDefault(require("./parsePath.js"));
var _operatorAliases = _interopRequireDefault(require("./operatorAliases.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var logger = function logger() {};
// logger = console.log.bind(console);
var Sutra = /*#__PURE__*/function () {
function Sutra() {
_classCallCheck(this, Sutra);
this.tree = [];
this.conditions = {};
this.listeners = {};
this.maps = {};
this.operators = ['equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', 'and', 'or', 'not'];
this.operatorAliases = _operatorAliases["default"];
this.exportToEnglish = _exportToEnglish["default"];
this.serializeToJson = _serializeToJson["default"];
this.toJSON = _serializeToJson["default"];
this.toEnglish = _exportToEnglish["default"];
this.evaluateCondition = _evaluateCondition["default"];
this.evaluateSingleCondition = _evaluateSingleCondition["default"];
this.evaluateDSLCondition = _evaluateDSLCondition["default"];
this.evaluateCompositeCondition = _evaluateCompositeCondition["default"];
this.parsePath = _parsePath["default"];
this.nodeIdCounter = 0; // New property to keep track of node IDs
}
_createClass(Sutra, [{
key: "use",
value: function use(subSutra, name) {
var _this = this;
var insertAt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.tree.length;
var shareListeners = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
// Store a reference to the subSutra for subtree-specific logic
this.subtrees = this.subtrees || {};
subSutra.isSubtree = true;
subSutra.parent = this;
this.subtrees[name] = subSutra;
if (shareListeners) {
this.sharedListeners = true;
// Merge subtree's listeners into the main tree's listeners
this.listeners = _objectSpread(_objectSpread({}, this.listeners), subSutra.listeners);
this.anyListeners = [].concat(_toConsumableArray(this.anyListeners || []), _toConsumableArray(subSutra.anyListeners || []));
// Optionally, update the subtree's listeners to reflect this change
// This ensures that both the subtree and main tree have the same set of listeners
subSutra.listeners = this.listeners;
subSutra.anyListeners = this.anyListeners;
}
// Integrate conditions from the subSutra
if (subSutra.conditions) {
Object.entries(subSutra.conditions).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
conditionName = _ref2[0],
condition = _ref2[1];
if (_this.conditions[conditionName]) {
console.warn("Condition '".concat(conditionName, "' from subtree '").concat(name, "' will overwrite an existing condition in the main Sutra."));
}
_this.addCondition(conditionName, condition);
});
}
// always combine conditions from subtrees
subSutra.conditions = _objectSpread(_objectSpread({}, this.conditions), subSutra.conditions);
}
}, {
key: "on",
value: function on(event, listener) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(listener);
}
}, {
key: "emit",
value: function emit(event) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
// Emit to the current instance's listeners
this.emitLocal.apply(this, [event].concat(args));
// If this instance is a subtree and sharedListeners is true, propagate to the parent tree
if (this.isSubtree && this.sharedListeners && this.parent) {
var _this$parent;
(_this$parent = this.parent).emitShared.apply(_this$parent, [event].concat(args));
}
}
}, {
key: "emitLocal",
value: function emitLocal(event) {
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
// Trigger all listeners for this specific event
if (this.listeners[event]) {
this.listeners[event].forEach(function (listener) {
return listener.apply(void 0, args);
});
}
// Trigger all 'any' listeners, regardless of the event type
if (this.anyListeners) {
this.anyListeners.forEach(function (listener) {
return listener.apply(void 0, [event].concat(args));
});
}
}
}, {
key: "emitShared",
value: function emitShared(event) {
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
// Emit to main tree's listeners
if (this.listeners[event]) {
this.listeners[event].forEach(function (listener) {
return listener.apply(void 0, args);
});
}
if (this.anyListeners) {
this.anyListeners.forEach(function (listener) {
return listener.apply(void 0, [event].concat(args));
});
}
// Additionally, emit to each subtree's listeners
Object.values(this.subtrees).forEach(function (subtree) {
if (subtree.listeners[event]) {
subtree.listeners[event].forEach(function (listener) {
return listener.apply(void 0, args);
});
}
if (subtree.anyListeners) {
subtree.anyListeners.forEach(function (listener) {
return listener.apply(void 0, [event].concat(args));
});
}
});
}
}, {
key: "onAny",
value: function onAny(listener) {
// Initialize the anyListeners array if it doesn't exist
this.anyListeners = this.anyListeners || [];
this.anyListeners.push(listener);
}
}, {
key: "if",
value: function _if() {
var conditions = Array.from(arguments); // Convert arguments to an array
var lastNode = this.tree.length > 0 ? this.tree[this.tree.length - 1] : null;
if (lastNode && !lastNode.then) {
// If the last node exists and doesn't have a 'then', add conditions to it
if (!Array.isArray(lastNode["if"])) {
lastNode["if"] = [lastNode["if"]];
}
lastNode["if"] = lastNode["if"].concat(conditions);
} else {
// Create a new node
var node = {
"if": conditions.length > 1 ? conditions : conditions[0]
};
this.addAction(node);
}
return this; // Return this for chaining
}
}, {
key: "then",
value: function then(actionOrFunction) {
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var lastNode = this.tree[this.tree.length - 1];
if (!lastNode.then) {
lastNode.then = [];
}
if (typeof actionOrFunction === 'function') {
// Create a scoped context
var scopedContext = {
"if": function _if(condition) {
var node = {
"if": condition,
then: []
};
node.sutraPath = "".concat(lastNode.sutraPath, ".then[").concat(lastNode.then.length, "]");
lastNode.then.push(node);
return scopedContext; // Allow chaining within the scoped context
},
then: function then(action) {
var node = lastNode.then[lastNode.then.length - 1];
if (!node.then) {
node.then = [];
}
var actionNode = {
action: action
};
actionNode.sutraPath = "".concat(node.sutraPath, ".then[").concat(node.then.length, "]");
node.then.push(actionNode);
return scopedContext;
},
"else": function _else(action) {
var node = lastNode.then[lastNode.then.length - 1];
if (!node["else"]) {
node["else"] = [];
}
var actionNode = {
action: action
};
actionNode.sutraPath = "".concat(node.sutraPath, ".else[").concat(node["else"].length, "]");
node["else"].push(actionNode);
return scopedContext;
},
map: function map(name) {
// Add the map node to the last 'then' node
var mapNode = {
map: name
};
lastNode.then.push(mapNode);
return scopedContext; // Allow chaining within the scoped context
}
};
// Execute the function in the scoped context
actionOrFunction(scopedContext);
} else {
// check see if string matches name of known subtree
var subSutra;
if (this.subtrees) {
subSutra = this.subtrees[actionOrFunction];
}
var actionNode = {};
if (data) {
actionNode.data = data;
}
if (subSutra) {
// If it's a subtree, add a subtree node
lastNode.subtree = actionOrFunction;
delete lastNode.then;
} else {
// Otherwise, add an action node
actionNode.action = actionOrFunction;
actionNode.sutraPath = "".concat(lastNode.sutraPath, ".then[").concat(lastNode.then.length, "]");
lastNode.then.push(actionNode);
}
}
return this; // Return this for chaining
}
}, {
key: "else",
value: function _else(actionOrFunction) {
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var lastNode = this.tree[this.tree.length - 1];
if (!lastNode["else"]) {
lastNode["else"] = [];
}
if (typeof actionOrFunction === 'function') {
// Create a scoped context for else
var scopedContext = {
"if": function _if(condition) {
var node = {
"if": condition,
then: [],
"else": []
};
lastNode["else"].push(node);
return scopedContext; // Allow chaining within the scoped context
},
then: function then(action) {
var node = lastNode["else"][lastNode["else"].length - 1];
if (!node.then) {
node.then = [];
}
node.then.push({
action: action,
data: data
});
return scopedContext;
},
"else": function _else(action) {
var node = lastNode["else"][lastNode["else"].length - 1];
if (!node["else"]) {
node["else"] = [];
}
node["else"].push({
action: action,
data: data
});
return scopedContext;
}
};
// Execute the function in the scoped context
actionOrFunction(scopedContext);
} else {
lastNode["else"].push({
action: actionOrFunction,
data: data
});
}
return this; // Return this for chaining
}
}, {
key: "addMap",
value: function addMap(name, mapFunction) {
var mapNode = {
map: name,
func: mapFunction
};
this.maps[name] = mapNode;
this.generateSutraPath(mapNode, 'tree', this.tree.length - 1, null);
}
// Method to execute a map node
}, {
key: "executeMap",
value: function executeMap(mapNode, data, gameState) {
if (typeof mapNode.func === 'function') {
// Execute the map function and update data and gameState accordingly
var result = mapNode.func(data, gameState);
if (result !== undefined) {
// Update data and gameState if result is returned
return result;
}
}
return data; // Return original data if no transformation occurred
}
// Fluent API for map
}, {
key: "map",
value: function map(name) {
var lastNode = this.tree[this.tree.length - 1];
// If there's no last 'then' node or it's a placeholder, create a new node
if (!lastNode) {
lastNode = {
then: []
};
// lastNode.then.push(lastThenNode);
}
if (!lastNode.then) {
lastNode.then = [];
}
// Add the map node to the last 'then' node
var mapNode = {
map: name
};
lastNode.then.push(mapNode);
return this; // Allow chaining within the scoped context
}
}, {
key: "addAction",
value: function addAction(node) {
this.tree.push(node);
this.generateSutraPath(node, 'tree', this.tree.length - 1, null);
}
}, {
key: "addCondition",
value: function addCondition(name, conditionObj) {
var _this2 = this;
this.originalConditions = this.originalConditions || {};
if (Array.isArray(conditionObj)) {
this.conditions[name] = conditionObj.map(function (cond) {
if (typeof cond === 'function') {
_this2.originalConditions[name] = _this2.originalConditions[name] || [];
_this2.originalConditions[name].push({
type: 'function',
func: cond
});
return {
func: function func(data, gameState) {
return cond(data, gameState);
},
original: null
};
} else {
_this2.originalConditions[name] = _this2.originalConditions[name] || [];
_this2.originalConditions[name].push(cond);
var conditionFunc = function conditionFunc(data, gameState) {
return _this2.evaluateDSLCondition(cond, data, gameState);
};
return {
func: conditionFunc,
original: cond
};
}
});
} else {
this.storeSingleCondition(name, conditionObj);
}
}
}, {
key: "removeCondition",
value: function removeCondition(name) {
if (this.conditions[name]) {
delete this.conditions[name];
if (this.originalConditions && this.originalConditions[name]) {
delete this.originalConditions[name];
}
return true;
}
return false; // Condition name not found
}
}, {
key: "updateCondition",
value: function updateCondition(name, newConditionObj) {
var _this3 = this;
if (!this.conditions[name]) {
return false;
}
// If the new condition is a function, update directly
if (typeof newConditionObj === 'function') {
this.conditions[name] = newConditionObj;
} else if (_typeof(newConditionObj) === 'object') {
// Handle if newConditionObj is an array
if (Array.isArray(newConditionObj)) {
// Update each condition in the array
newConditionObj.forEach(function (condition) {
if (condition.op === 'and' || condition.op === 'or' || condition.op === 'not') {
// Composite condition for each element in the array
var conditionFunc = function conditionFunc(data, gameState) {
return _this3.evaluateDSLCondition(condition, data, gameState);
};
conditionFunc.original = condition;
_this3.conditions[name] = conditionFunc;
} else {
// DSL condition for each element in the array
var _conditionFunc = function _conditionFunc(data, gameState) {
return _this3.evaluateDSLCondition(condition, data, gameState);
};
_conditionFunc.original = condition;
_this3.conditions[name] = _conditionFunc;
}
});
} else if (newConditionObj.op === 'and' || newConditionObj.op === 'or' || newConditionObj.op === 'not') {
// Composite condition
var conditionFunc = function conditionFunc(data, gameState) {
return _this3.evaluateDSLCondition(newConditionObj, data, gameState);
};
conditionFunc.original = newConditionObj;
this.conditions[name] = conditionFunc;
} else {
// DSL condition
var _conditionFunc2 = function _conditionFunc2(data, gameState) {
return _this3.evaluateDSLCondition(newConditionObj, data, gameState);
};
_conditionFunc2.original = newConditionObj;
this.conditions[name] = _conditionFunc2;
}
} else {
return false;
}
// Update original conditions for GUI use
this.originalConditions[name] = newConditionObj;
return true;
}
}, {
key: "storeSingleCondition",
value: function storeSingleCondition(name, conditionObj) {
var _this4 = this;
// Store the original condition object separately for GUI use
if (!(typeof conditionObj === 'function' && conditionObj.original)) {
this.originalConditions = this.originalConditions || {};
this.originalConditions[name] = conditionObj;
}
if (conditionObj.op === 'and' || conditionObj.op === 'or' || conditionObj.op === 'not') {
// Store composite conditions directly
this.conditions[name] = conditionObj;
this.originalConditions[name] = conditionObj;
} else if (typeof conditionObj === 'function') {
// Wrap custom function conditions to include gameState
this.conditions[name] = function (data, gameState) {
var val = false;
try {
val = conditionObj(data, gameState);
} catch (err) {
// console.log('warning: error in condition function', err)
}
return val;
};
} else {
// For DSL conditions, pass gameState to the evaluateDSLCondition function
var conditionFunc = function conditionFunc(data, gameState) {
return _this4.evaluateDSLCondition(conditionObj, data, gameState);
};
conditionFunc.original = conditionObj;
this.conditions[name] = conditionFunc;
}
}
}, {
key: "resolveOperator",
value: function resolveOperator(operator) {
return this.operatorAliases[operator] || operator;
}
// Method to set or update aliases
}, {
key: "setOperatorAlias",
value: function setOperatorAlias(alias, operator) {
this.operatorAliases[alias] = operator;
}
}, {
key: "getConditionFunction",
value: function getConditionFunction(name) {
return this.conditions[name];
}
}, {
key: "getCondition",
value: function getCondition(name) {
return this.originalConditions ? this.originalConditions[name] : undefined;
}
}, {
key: "getOperators",
value: function getOperators() {
return Object.keys(this.operatorAliases);
}
}, {
key: "traverseNode",
value: function traverseNode(node, data, gameState) {
var mappedData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
if (node.subtree) {
var subSutra = this.subtrees[node.subtree];
if (subSutra) {
var _conditionMet = node["if"] ? this.evaluateCondition(node["if"], data, gameState, subSutra) : true;
if (_conditionMet) {
subSutra.tick(data, gameState);
}
} else {
console.warn("Subtree '".concat(node.subtree, "' not found."));
}
return;
}
// Use mappedData if available, otherwise use the original data
var currentData = mappedData || data;
// Process the map node if present
if (node.map) {
var map = this.maps[node.map];
if (!map) {
throw new Error("Map \"".concat(node.map, "\" not found"));
}
var newMappedData = this.executeMap(map, currentData, gameState);
if (newMappedData !== undefined) {
mappedData = newMappedData;
}
}
// Execute action if present
if (node.action) {
this.executeAction(node.action, mappedData || currentData, node, gameState);
return;
}
var conditionMet = node["if"] ? this.evaluateCondition(node["if"], mappedData || currentData, gameState) : true;
if (conditionMet) {
this.processBranch(node.then, mappedData || currentData, gameState, mappedData);
} else {
this.processBranch(node["else"], mappedData || currentData, gameState, mappedData);
}
}
}, {
key: "processBranch",
value: function processBranch(branch, data, gameState) {
var _this5 = this;
var mappedData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
if (Array.isArray(branch)) {
branch.forEach(function (childNode) {
return _this5.traverseNode(childNode, data, gameState, mappedData);
});
}
}
}, {
key: "executeAction",
value: function executeAction(action, data, node, gameState) {
var object = {};
if (!node.data) {
node.data = {};
}
var entityData = data;
Object.entries(entityData).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
key = _ref4[0],
value = _ref4[1];
// check data to see if any of the keys at the first level are functions
// if so, execute them and replace the value with the result
// this is to allow for dynamic data to be passed to the action
if (typeof value === 'function') {
object[key] = value(entityData, gameState, node);
} else {
object[key] = value;
}
});
Object.entries(node.data).forEach(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
key = _ref6[0],
value = _ref6[1];
if (typeof value === 'function') {
object[key] = value(entityData, gameState, node);
} else {
object[key] = value;
}
});
var mergedData = object;
this.emit(action, mergedData, node, gameState);
}
}, {
key: "updateEntity",
value: function updateEntity(entity, updateData, gameState) {
Object.entries(updateData).forEach(function (_ref7) {
var _ref8 = _slicedToArray(_ref7, 2),
key = _ref8[0],
value = _ref8[1];
if (typeof value === 'function') {
entity[key] = value();
} else {
entity[key] = value;
}
});
}
}, {
key: "generateSutraPath",
value: function generateSutraPath(node, parentPath, index, parent) {
var _this6 = this;
var path = index === -1 ? parentPath : "".concat(parentPath, "[").concat(index, "]");
node.sutraPath = path;
node.parent = parent; // Set the parent reference
if (node.then && Array.isArray(node.then)) {
node.then.forEach(function (child, idx) {
return _this6.generateSutraPath(child, "".concat(path, ".then"), idx, node);
});
}
if (node["else"] && Array.isArray(node["else"])) {