diff --git a/AndorsTrailEdit/ImportExport.js b/AndorsTrailEdit/ImportExport.js deleted file mode 100644 index ef7ea7289..000000000 --- a/AndorsTrailEdit/ImportExport.js +++ /dev/null @@ -1,70 +0,0 @@ - -var importDialog; -var exportDialog; - -function exportIfExists(dataStore, div) { - var exportData = dataStore.serialize(); - $( "#value" , div ).val(exportData); -} - -function importDatastore(dataStore, content) { - dataStore.deserialize(content); -} - -function prepareImport(dataStore, div) { - var importButton = $( "#import", div ); - var textarea = $( "#value", div ); - importButton.button().click(function() { - if (!textarea.val()) return; - importDatastore(dataStore, textarea.val()); - }); - textarea.keyup(function() { - var disabled = $(this).val() ? false : true; - importButton.button( "option", "disabled", disabled ); - }); -} - -function showImportDialog() { - $( "#import", importDialog ).button( "option", "disabled", true ); - $( "#value", importDialog ).val(""); - importDialog.dialog( "open" ); -} - -function showExportDialog() { - exportIfExists(model.actorConditions, $( ".export-actorconditions", exportDialog )); - exportIfExists(model.quests, $( ".export-quests", exportDialog )); - exportIfExists(model.items, $( ".export-items", exportDialog )); - exportIfExists(model.droplists, $( ".export-droplists", exportDialog )); - exportIfExists(model.dialogue, $( ".export-dialogue", exportDialog )); - exportIfExists(model.monsters, $( ".export-monsters", exportDialog )); - exportDialog.dialog( "open" ); -} - -function prepareImportExportDialogs(buttons) { - importDialog = $( "#templates #dialog-import" ) - .dialog({ - title: "Import data", - modal: true, - autoOpen: false, - width: 840, - buttons: buttons - }); - prepareImport(model.actorConditions, $( ".import-actorconditions", importDialog )); - prepareImport(model.quests, $( ".import-quests", importDialog )); - prepareImport(model.items, $( ".import-items", importDialog )); - prepareImport(model.droplists, $( ".import-droplists", importDialog )); - prepareImport(model.dialogue, $( ".import-dialogue", importDialog )); - prepareImport(model.monsters, $( ".import-monsters", importDialog )); - $( "#importsections", importDialog ).accordion({ autoHeight: false }); - - exportDialog = $( "#templates #dialog-export" ) - .dialog({ - title: "Export data", - modal: true, - autoOpen: false, - width: 840, - buttons: buttons - }); - - $( "#exportsections", exportDialog ).accordion({ autoHeight: false }); -} diff --git a/AndorsTrailEdit/app.js b/AndorsTrailEdit/app.js index aef5d481c..bd5d18a51 100644 --- a/AndorsTrailEdit/app.js +++ b/AndorsTrailEdit/app.js @@ -1,5 +1,5 @@ -var app = (function(controllers) { - return angular +var ATEditor = (function(ATEditor, controllers) { + ATEditor.app = angular .module('ateditor', []) .config(['$routeProvider', function($routeProvider) { $routeProvider @@ -9,6 +9,9 @@ var app = (function(controllers) { .when('/droplist/edit/:id', {templateUrl: 'edit_droplist.html', controller: controllers.DropListController}) .when('/dialogue/edit/:id', {templateUrl: 'edit_dialogue.html', controller: controllers.DialogueController}) .when('/monster/edit/:id', {templateUrl: 'edit_monster.html', controller: controllers.MonsterController}) - .when('/itemcategory/edit/:id', {templateUrl: 'edit_itemcategory.html', controller: controllers.ItemCategoryController}); + .when('/itemcategory/edit/:id', {templateUrl: 'edit_itemcategory.html', controller: controllers.ItemCategoryController}) + .when('/import', {templateUrl: 'import.html', controller: controllers.ImportController}) + .when('/export', {templateUrl: 'export.html', controller: controllers.ExportController}); }]); -})(controllers); + return ATEditor; +})(ATEditor, ATEditor.controllers); diff --git a/AndorsTrailEdit/controllers.js b/AndorsTrailEdit/controllers.js index e38f2a990..ad7896d36 100644 --- a/AndorsTrailEdit/controllers.js +++ b/AndorsTrailEdit/controllers.js @@ -1,6 +1,8 @@ -var controllers = (function(model) { +var ATEditor = (function(ATEditor, model, importExport) { - function NavigationController($scope, $routeParams) { + var controllers = {}; + + controllers.NavigationController = function($scope, $routeParams) { $scope.sections = model.sections; $scope.previousItems = []; @@ -12,7 +14,7 @@ var controllers = (function(model) { if ($scope.previousItems.length > 5) { $scope.previousItems.pop(); } - window.location = "#/" + section.objectTypename + "/edit/" + obj.id; + window.location = "#/" + section.id + "/edit/" + obj.id; }; $scope.addObj = function(section) { var item = section.addNew(); @@ -36,44 +38,33 @@ var controllers = (function(model) { $scope.editObj(section, item); }; - } + }; - function ActorConditionController($scope, $routeParams) { + controllers.ActorConditionController = function($scope, $routeParams) { $scope.datasource = model.actorConditions; - $scope.obj = model.actorConditions.findById($routeParams.id); - } - function QuestController($scope, $routeParams) { + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + controllers.QuestController = function($scope, $routeParams) { $scope.datasource = model.quests; - $scope.obj = model.quests.findById($routeParams.id); - } - function ItemController($scope, $routeParams) { + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + controllers.ItemController = function($scope, $routeParams) { $scope.datasource = model.items; - $scope.obj = model.items.findById($routeParams.id); - } - function DropListController($scope, $routeParams) { + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + controllers.DropListController = function($scope, $routeParams) { $scope.datasource = model.droplists; - $scope.obj = model.droplists.findById($routeParams.id); - } - function DialogueController($scope, $routeParams) { + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + controllers.DialogueController = function($scope, $routeParams) { $scope.datasource = model.dialogue; - $scope.obj = model.dialogue.findById($routeParams.id); - } - function MonsterController($scope, $routeParams) { + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + controllers.MonsterController = function($scope, $routeParams) { $scope.datasource = model.monsters; - var m = model.monsters.findById($routeParams.id) || {}; - m.attackDamage = m.attackDamage || {}; - m.hasConversation = m.phraseID; - m.hasCombatTraits = m.attackChance || m.attackDamage.min || m.criticalSkill || m.criticalMultiplier || m.blockChance || m.damageResistance || m.hitEffect; - m.hasHitEffect = m.hitEffect; - m.hitEffect = m.hitEffect || { conditionsSource: [], conditionsTarget: [] }; + var m = $scope.datasource.findById($routeParams.id) || {}; $scope.obj = m; $scope.getExperience = function(obj) { - /* - final float avgAttackHP = t.getAttacksPerTurn(maxAP) * div100(t.attackChance) * t.damagePotential.averagef() * (1 + div100(t.criticalChance) * t.criticalMultiplier); - final float avgDefenseHP = maxHP * (1 + div100(t.blockChance)) + Constants.EXP_FACTOR_DAMAGERESISTANCE * t.damageResistance; - return (int) Math.ceil((avgAttackHP * 3 + avgDefenseHP) * Constants.EXP_FACTOR_SCALING); - */ - var EXP_FACTOR_DAMAGERESISTANCE = 9; var EXP_FACTOR_SCALING = 0.7; @@ -81,10 +72,15 @@ var controllers = (function(model) { var v = function(i) { return i ? i : 0; } var attacksPerTurn = Math.floor(v(obj.maxAP) / v(obj.attackCost)); - var avgDamagePotential = (v(obj.attackDamage.min) + v(obj.attackDamage.max)) / 2; + var avgDamagePotential = 0; + if (obj.attackDamage) { avgDamagePotential = (v(obj.attackDamage.min) + v(obj.attackDamage.max)) / 2; } var avgAttackHP = attacksPerTurn * div100(v(obj.attackChance)) * avgDamagePotential * (1 + div100(v(obj.criticalSkill)) * v(obj.criticalMultiplier)); var avgDefenseHP = v(obj.maxHP) * (1 + div100(v(obj.blockChance))) + EXP_FACTOR_DAMAGERESISTANCE * v(obj.damageResistance); - var experience = (avgAttackHP * 3 + avgDefenseHP) * EXP_FACTOR_SCALING; + var attackConditionBonus = 0; + if (obj.hitEffect && obj.hitEffect.conditionsTarget && v(obj.hitEffect.conditionsTarget.length) > 0) { + attackConditionBonus = 50; + } + var experience = (avgAttackHP * 3 + avgDefenseHP) * EXP_FACTOR_SCALING + attackConditionBonus; return Math.ceil(experience); }; @@ -92,6 +88,17 @@ var controllers = (function(model) { $scope.experience = $scope.getExperience($scope.obj); }; $scope.recalculateExperience(); + $scope.$watch('obj.maxAP', $scope.recalculateExperience); + $scope.$watch('obj.attackCost', $scope.recalculateExperience); + $scope.$watch('obj.attackDamage.min', $scope.recalculateExperience); + $scope.$watch('obj.attackDamage.max', $scope.recalculateExperience); + $scope.$watch('obj.attackChance', $scope.recalculateExperience); + $scope.$watch('obj.criticalSkill', $scope.recalculateExperience); + $scope.$watch('obj.criticalMultiplier', $scope.recalculateExperience); + $scope.$watch('obj.maxHP', $scope.recalculateExperience); + $scope.$watch('obj.blockChance', $scope.recalculateExperience); + $scope.$watch('obj.damageResistance', $scope.recalculateExperience); + $scope.$watch('obj.hitEffect.conditionsTarget.length', $scope.recalculateExperience); $scope.addCondition = function(list) { list.push({magnitude:1, duration:1, chance:100}); }; @@ -99,20 +106,44 @@ var controllers = (function(model) { var idx = list.indexOf(cond); list.splice(idx, 1); }; - } - function ItemCategoryController($scope, $routeParams) { - $scope.datasource = model.itemCategories; - $scope.obj = model.itemCategories.findById($routeParams.id); - } - - return { - NavigationController: NavigationController - ,ActorConditionController: ActorConditionController - ,QuestController: QuestController - ,ItemController: ItemController - ,DropListController: DropListController - ,DialogueController: DialogueController - ,MonsterController: MonsterController - ,ItemCategoryController: ItemCategoryController }; -})(model); + controllers.ItemCategoryController = function($scope, $routeParams) { + $scope.datasource = model.itemCategories; + $scope.obj = $scope.datasource.findById($routeParams.id); + }; + + String.prototype.trim = String.prototype.trim || (function(){return this.replace(/^\s+|\s+$/g, '');}); + + controllers.ImportController = function($scope) { + $scope.sections = model.sections; + $scope.content = ""; + $scope.selectedSection = $scope.selectedSection || model.items; + + $scope.importData = function() { + $scope.errorMsg = ""; + $scope.importedMsg = ""; + + var section = $scope.selectedSection; + var countBefore = section.items.length; + function success() { + var countAfter = section.items.length; + $scope.importedMsg = "Imported " + (countAfter - countBefore) + " " + section.name; + } + function error(msg) { + $scope.errorMsg = "Error importing data: " + msg; + } + importExport.importData(section, $scope.content, success, error); + }; + }; + controllers.ExportController = function($scope) { + $scope.sections = model.sections; + $scope.content = ""; + $scope.selectedSection = $scope.selectedSection || model.items; + $scope.exportData = function() { + $scope.content = importExport.exportData($scope.selectedSection); + }; + }; + + ATEditor.controllers = controllers; + return ATEditor; +})(ATEditor, ATEditor.model, ATEditor.importExport); diff --git a/AndorsTrailEdit/DataStore.js b/AndorsTrailEdit/datastore.js similarity index 63% rename from AndorsTrailEdit/DataStore.js rename to AndorsTrailEdit/datastore.js index c5c0a89b3..55856da83 100644 --- a/AndorsTrailEdit/DataStore.js +++ b/AndorsTrailEdit/datastore.js @@ -1,19 +1,19 @@ - -var DataStore = (function(_) { - function DataStore(options) { +var ATEditor = (function(ATEditor, _) { + + ATEditor.DataStore = function(options) { var defaultOptions = { nameField: 'name' ,idField: 'id' ,iconIDField: 'iconID' ,newItemTemplate: function() { return {}; } }; - options = _.extend(defaultOptions, options); + _.defaults(options, defaultOptions); - this.items = []; + var items = []; + this.items = items; this.name = options.name; - this.objectTypename = options.objectTypename; - this.legacyFieldList = options.legacyFieldList; + this.id = options.id; this.findById = function(id) { return _.find(this.items, function(obj) { return obj[options.idField] === id; }); @@ -30,37 +30,49 @@ var DataStore = (function(_) { this.addNew = function() { var obj = options.newItemTemplate(); this.ensureUniqueId(obj); - this.items.push(obj); + items.push(obj); return obj; }; this.add = function(o) { - this.items.push(o); + items.push(o); }; this.clone = function(o) { - var obj = _.extend({}, o); + var obj = ATEditor.utils.deepClone(o); this.ensureUniqueId(obj); - this.items.push(obj); + items.push(obj); return obj; }; this.remove = function(o) { - this.items = _.without(this.items, o); + items = _.without(items, o); }; - this.findFirstFreeId = function(id) { - var i = 0; - var result = id; + if(!this.hasObjectWithId(id)) { + return id; + } + + var prefix; + var n = 1; + + var match = (/^(.*\D)(\d+)$/g).exec(id); + if (match) { + prefix = match[1]; + n = parseInt(match[2]) + 1; + } else { + prefix = id + "_"; + } + + var result = prefix + n; while(this.hasObjectWithId(result)) { - i = i + 1; - result = id + i; + n = n + 1; + result = prefix + n; } return result; }; this.ensureUniqueId = function(obj) { obj[options.idField] = this.findFirstFreeId(obj[options.idField]); }; - } + }; - - return DataStore; -})(_); + return ATEditor; +})(ATEditor || {}, _); diff --git a/AndorsTrailEdit/defaults.js b/AndorsTrailEdit/defaults.js new file mode 100644 index 000000000..ecdccbc51 --- /dev/null +++ b/AndorsTrailEdit/defaults.js @@ -0,0 +1,27 @@ +var ATEditor = (function(ATEditor, _) { + + var defaults = { + monster: { + size: "1x1" + ,maxHP: 1 + ,maxAP: 10 + ,moveCost: 10 + ,unique: 0 + ,monsterClass: 0 + ,attackDamage: {} + ,hitEffect: { increaseCurrentHP: {}, increaseCurrentAP: {}, conditionsSource: [], conditionsTarget: [] } + } + }; + + ATEditor.defaults = { + addDefaults: function(type, o) { + var copyOfDefaults = ATEditor.utils.deepClone(defaults[type]); + _.defaults(o, copyOfDefaults); + }, + removeDefaults: function(type, o) { + return ATEditor.utils.cleanCopy(o, defaults[type]); + } + }; + + return ATEditor; +})(ATEditor || {}, _); diff --git a/AndorsTrailEdit/directives.js b/AndorsTrailEdit/directives.js index 4c9b1f726..ae9fe47f7 100644 --- a/AndorsTrailEdit/directives.js +++ b/AndorsTrailEdit/directives.js @@ -1,4 +1,4 @@ -(function(app, $) { +var ATEditor = (function(ATEditor, app, $) { // Copied from http://jsfiddle.net/p69aT/ // -> originally from https://groups.google.com/forum/?fromgroups=#!topic/angular/7XVOebG6z6E @@ -52,4 +52,5 @@ }; }); -})(app, jQuery); + return ATEditor; +})(ATEditor, ATEditor.app, jQuery); diff --git a/AndorsTrailEdit/edit_monster.html b/AndorsTrailEdit/edit_monster.html index 0191cc21a..7b762ac16 100644 --- a/AndorsTrailEdit/edit_monster.html +++ b/AndorsTrailEdit/edit_monster.html @@ -19,7 +19,7 @@
Has effect on each hit
-
+
On every successful attack hit
diff --git a/AndorsTrailEdit/editor.html b/AndorsTrailEdit/editor.html index cd6c8956d..923cd15ca 100644 --- a/AndorsTrailEdit/editor.html +++ b/AndorsTrailEdit/editor.html @@ -18,19 +18,19 @@
-
Import
-
Export
+ Import + Export
-
-
+
+
-
+
  • @@ -42,11 +42,11 @@
-
+
@@ -827,12 +827,16 @@ - - + + + + + + diff --git a/AndorsTrailEdit/export.html b/AndorsTrailEdit/export.html new file mode 100644 index 000000000..683176731 --- /dev/null +++ b/AndorsTrailEdit/export.html @@ -0,0 +1,10 @@ +
+
This data corresponds to the files named res/values/content_*.xml in the source code.
+ Export + + + + +
+
diff --git a/AndorsTrailEdit/FieldList.js b/AndorsTrailEdit/fieldlist.js similarity index 94% rename from AndorsTrailEdit/FieldList.js rename to AndorsTrailEdit/fieldlist.js index 5b4dd17de..e32b58d92 100644 --- a/AndorsTrailEdit/FieldList.js +++ b/AndorsTrailEdit/fieldlist.js @@ -1,5 +1,5 @@ -var FieldList = (function() { +var ATEditor = (function(ATEditor) { var FieldList_Header_fieldName = '[^\\[\\]\\|]*'; var FieldList_Header_arrayField = FieldList_Header_fieldName + '\\[(' + FieldList_Header_fieldName + '\\|)*\\]'; var FieldList_Header_arrayFieldName = new RegExp(FieldList_Header_fieldName); @@ -53,7 +53,7 @@ var FieldList = (function() { this.getHeaderLine = function() { return this.getHeader() + ";"; } - } + }; function findHeader(str) { var match = str.match(FieldList_Header_linePattern); @@ -93,6 +93,7 @@ var FieldList = (function() { } dataStore.legacyFieldList = header; dataStore.items = deserializeObjectList(header, str); + return dataStore.items; } var serialize = function(dataStore) { return serializeObjectList(dataStore.fieldList, dataStore.items); @@ -193,9 +194,12 @@ var FieldList = (function() { return result; } - return { - FieldList: FieldList - ,deserialize: deserialize - ,serialize: serialize - }; -})(); + ATEditor.FieldList = FieldList; + + ATEditor.legacy = ATEditor.legacy || {}; + ATEditor.legacy.deserialize = deserialize; + ATEditor.legacy.serialize = serialize; + ATEditor.legacy.findHeader = findHeader; + + return ATEditor; +})(ATEditor || {}); diff --git a/AndorsTrailEdit/import.html b/AndorsTrailEdit/import.html new file mode 100644 index 000000000..a7cad2ea1 --- /dev/null +++ b/AndorsTrailEdit/import.html @@ -0,0 +1,12 @@ +
+
This data corresponds to the files named res/values/content_*.xml in the source code.
+ Import as + + +
+ + +
{{errorMsg}}
+
{{importedMsg}}
+
diff --git a/AndorsTrailEdit/importexport.js b/AndorsTrailEdit/importexport.js new file mode 100644 index 000000000..6247441a4 --- /dev/null +++ b/AndorsTrailEdit/importexport.js @@ -0,0 +1,97 @@ +var ATEditor = (function(ATEditor, _) { + + var prep = {}; + prep.item = function(o) { + }; + prep.monster = function(o) { + ATEditor.defaults.addDefaults('monster', o); + o.hasConversation = o.phraseID; + o.hasHitEffect = o.hitEffect.increaseCurrentHP.min || o.hitEffect.increaseCurrentAP.min || _.some(o.hitEffect.conditionsSource) || _.some(o.hitEffect.conditionsTarget); + o.hasCombatTraits = o.attackChance || o.attackDamage.min || o.criticalSkill || o.criticalMultiplier || o.blockChance || o.damageResistance || o.hasHitEffect; + }; + var unprep = {}; + unprep.monster = function(o) { + o = ATEditor.defaults.removeDefaults('monster', o); + delete o.hasConversation; + delete o.hasCombatTraits; + delete o.hasHitEffect; + return o; + }; + + function prepareObjectsForEditor(section, objs) { + var p = prep[section.id]; + if (p) { + _.each(objs, p); + } + } + + function importDataObjects(section, data, success, error) { + if (!data || _.isEmpty(data)) { + error("No data?"); + return; + } + + var first = data; + if (_.isArray(data)) { + first = _.first(data); + } else if (_.isObject(data)) { + data = [ data ]; + } else { + error("Malformed data? Expected array or object."); + return; + } + + if (!section.getId(first)) { + error("Malformed data? Expected to find at least an id field, but no such field was found."); + return; + } + + prepareObjectsForEditor(section, data); + + _.each(data, section.add); + success(); + }; + + function importData(section, content, success, error) { + if (ATEditor.legacy.findHeader(content)) { + var data = ATEditor.legacy.deserialize(content, section); + var convert = ATEditor.legacy.convertFromLegacyFormat[section.id]; + if (convert) { + _.each(data, convert); + } + + prepareObjectsForEditor(section, data); + success(); + return; + } + + var data; + try { + data = JSON.parse(content); + } catch(e) { + error("Unable to parse data as JSON."); + return; + } + + importDataObjects(section, data, success, error); + }; + function exportData(section) { + var resultObjs = []; + var objs = section.items; + var p = unprep[section.id]; + if (p) { + resultObjs = _.map(objs, p); + } else { + resultObjs = objs; + } + + return JSON.stringify(resultObjs, undefined, 2); + }; + + ATEditor.importExport = { + importData: importData + ,importDataObjects: importDataObjects + ,exportData: exportData + }; + return ATEditor; +})(ATEditor || {}, _); diff --git a/AndorsTrailEdit/legacyimport.js b/AndorsTrailEdit/legacyimport.js new file mode 100644 index 000000000..a2cc18df7 --- /dev/null +++ b/AndorsTrailEdit/legacyimport.js @@ -0,0 +1,335 @@ +var ATEditor = (function(ATEditor, model, FieldList, _) { + + function addLegacyFieldLists(model) { + model.actorConditions.legacyFieldList = new FieldList("[id|name|iconID|category|isStacking|isPositive|" + + "hasRoundEffect|round_visualEffectID|round_boostHP_Min|round_boostHP_Max|round_boostAP_Min|round_boostAP_Max|" + + "hasFullRoundEffect|fullround_visualEffectID|fullround_boostHP_Min|fullround_boostHP_Max|fullround_boostAP_Min|fullround_boostAP_Max|" + + "hasAbilityEffect|boostMaxHP|boostMaxAP|moveCostPenalty|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|" + + "];" + ); + model.quests.legacyFieldList = new FieldList("[id|name|showInLog|stages[progress|logText|rewardExperience|finishesQuest|]|];"); + model.items.legacyFieldList = new FieldList("[id|iconID|name|category|displaytype|hasManualPrice|baseMarketCost|" + + "hasEquipEffect|equip_boostMaxHP|equip_boostMaxAP|equip_moveCostPenalty|equip_attackCost|equip_attackChance|equip_criticalChance|equip_criticalMultiplier|equip_attackDamage_Min|equip_attackDamage_Max|equip_blockChance|equip_damageResistance|equip_conditions[condition|magnitude|]|" + + "hasUseEffect|use_boostHP_Min|use_boostHP_Max|use_boostAP_Min|use_boostAP_Max|use_conditionsSource[condition|magnitude|duration|chance|]|" + + "hasHitEffect|hit_boostHP_Min|hit_boostHP_Max|hit_boostAP_Min|hit_boostAP_Max|hit_conditionsSource[condition|magnitude|duration|chance|]|hit_conditionsTarget[condition|magnitude|duration|chance|]|" + + "hasKillEffect|kill_boostHP_Min|kill_boostHP_Max|kill_boostAP_Min|kill_boostAP_Max|kill_conditionsSource[condition|magnitude|duration|chance|]|" + + "];" + ); + model.droplists.legacyFieldList = new FieldList("[id|items[itemID|quantity_Min|quantity_Max|chance|]|];"); + model.dialogue.legacyFieldList = new FieldList("[id|message|rewards[rewardType|rewardID|value|]|replies[text|nextPhraseID|requires_Progress|requires_itemID|requires_Quantity|requires_Type|]|];"); + model.monsters.legacyFieldList = new FieldList("[id|iconID|name|tags|size|monsterClass|unique|faction|maxHP|maxAP|moveCost|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|droplistID|phraseID|" + + "hasHitEffect|onHit_boostHP_Min|onHit_boostHP_Max|onHit_boostAP_Min|onHit_boostAP_Max|onHit_conditionsSource[condition|magnitude|duration|chance|]|onHit_conditionsTarget[condition|magnitude|duration|chance|]|" + + "];" + ); + model.itemCategories.legacyFieldList = new FieldList("[id|name|actionType|inventorySlot|size|];"); + } + addLegacyFieldLists(model); + + ATEditor.legacy = ATEditor.legacy || {}; + ATEditor.legacy.convertFromLegacyFormat = { + monster: convertMonster + ,quest: convertQuest + ,itemcategory: convertItemCategory + ,item: convertItem + ,droplist: convertDroplist + ,dialogue: convertConversation + ,actorcondition: convertCondition + }; + + function convertMonster(obj) { + // [id|iconID|name|tags|size|monsterClass|unique|faction|maxHP|maxAP|moveCost|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance| + // damageResistance|droplistID|phraseID| + // hasHitEffect|onHit_boostHP_Min|onHit_boostHP_Max|onHit_boostAP_Min|onHit_boostAP_Max|onHit_conditionsSource[condition|magnitude|duration|chance|]|onHit_conditionsTarget[condition|magnitude|duration|chance|]|]; + + var result = { + id: obj.id, + iconID: obj.iconID, + name: obj.name, + spawnGroup: obj.tags, + size: obj.size, + monsterClass: obj.monsterClass, + unique: obj.unique, + faction: obj.faction, + maxHP: obj.maxHP, + maxAP: obj.maxAP, + moveCost: obj.moveCost, + attackCost: obj.attackCost, + attackChance: obj.attackChance, + criticalSkill: obj.criticalChance, + criticalMultiplier: obj.criticalMultiplier, + blockChance: obj.blockChance, + damageResistance: obj.damageResistance, + droplistID: obj.droplistID, + phraseID: obj.phraseID + }; + + if (obj.attackDamage_Min || obj.attackDamage_Max) { + result.attackDamage = { + min: (obj.attackDamage_Min || 0), + max: (obj.attackDamage_Max || 0) + }; + } + + if (obj.hasHitEffect) { + result.hitEffect = {}; + var e = result.hitEffect; + if (obj.onHit_boostHP_Min || obj.onHit_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.onHit_boostHP_Min || 0), + max: (obj.onHit_boostHP_Max || 0) + }; + } + if (obj.onHit_boostAP_Min || obj.onHit_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.onHit_boostAP_Min || 0), + max: (obj.onHit_boostAP_Max || 0) + }; + } + if (obj.onHit_conditionsSource) { e.conditionsSource = obj.onHit_conditionsSource; } + if (obj.onHit_conditionsTarget) { e.conditionsTarget = obj.onHit_conditionsTarget; } + } + + return result; + } + + + function convertQuest(obj) { + // [id|name|showInLog|stages[progress|logText|rewardExperience|finishesQuest|]|]; + return obj; + } + + function convertItemCategory(obj) { + // [id|name|actionType|inventorySlot|size|]; + return obj; + } + + function convertItem(obj) { + //[id|iconID|name|category|displaytype|hasManualPrice|baseMarketCost| + // hasEquipEffect|equip_boostMaxHP|equip_boostMaxAP|equip_moveCostPenalty| + // equip_attackCost|equip_attackChance|equip_criticalChance|equip_criticalMultiplier|equip_attackDamage_Min|equip_attackDamage_Max|equip_blockChance|equip_damageResistance| + // equip_conditions[condition|magnitude|]| + // hasUseEffect|use_boostHP_Min|use_boostHP_Max|use_boostAP_Min|use_boostAP_Max|use_conditionsSource[condition|magnitude|duration|chance|]| + // hasHitEffect|hit_boostHP_Min|hit_boostHP_Max|hit_boostAP_Min|hit_boostAP_Max|hit_conditionsSource[condition|magnitude|duration|chance|]|hit_conditionsTarget[condition|magnitude|duration|chance|]| + // hasKillEffect|kill_boostHP_Min|kill_boostHP_Max|kill_boostAP_Min|kill_boostAP_Max|kill_conditionsSource[condition|magnitude|duration|chance|]|]; + + var result = { + id: obj.id, + iconID: obj.iconID, + name: obj.name, + category: obj.category, + displaytype: obj.displaytype, + hasManualPrice: obj.hasManualPrice, + baseMarketCost: obj.baseMarketCost + }; + + if (obj.hasEquipEffect) { + result.equipEffect = {}; + var e = result.equipEffect; + if (obj.equip_boostMaxHP) { e.increaseMaxHP = obj.equip_boostMaxHP; } + if (obj.equip_boostMaxAP) { e.increaseMaxAP = obj.equip_boostMaxAP; } + if (obj.equip_moveCostPenalty) { e.increaseMoveCost = obj.equip_moveCostPenalty; } + if (obj.equip_attackCost) { e.increaseAttackCost = obj.equip_attackCost; } + if (obj.equip_attackChance) { e.increaseAttackChance = obj.equip_attackChance; } + if (obj.equip_criticalChance) { e.increaseCriticalSkill = obj.equip_criticalChance; } + if (obj.equip_criticalMultiplier) { e.setCriticalMultiplier = obj.equip_criticalMultiplier; } + if (obj.equip_attackDamage_Min || obj.equip_attackDamage_Max) { + e.increaseAttackDamage = { + min: (obj.equip_attackDamage_Min || 0), + max: (obj.equip_attackDamage_Max || 0) + }; + } + if (obj.equip_blockChance) { e.increaseBlockChance = obj.equip_blockChance; } + if (obj.equip_damageResistance) { e.increaseDamageResistance = obj.equip_damageResistance; } + } + + if (obj.equip_conditions) { + result.equipEffect = result.equipEffect || {}; + result.equipEffect.addedConditions = obj.equip_conditions; + } + + if (obj.hasUseEffect) { + result.useEffect = {}; + var e = result.useEffect; + if (obj.use_boostHP_Min || obj.use_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.use_boostHP_Min || 0), + max: (obj.use_boostHP_Max || 0) + }; + } + if (obj.use_boostAP_Min || obj.use_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.use_boostAP_Min || 0), + max: (obj.use_boostAP_Max || 0) + }; + } + if (obj.use_conditionsSource) { e.conditionsSource = obj.use_conditionsSource; } + } + + if (obj.hasHitEffect) { + result.hitEffect = {}; + var e = result.hitEffect; + if (obj.hit_boostHP_Min || obj.hit_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.hit_boostHP_Min || 0), + max: (obj.hit_boostHP_Max || 0) + }; + } + if (obj.hit_boostAP_Min || obj.hit_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.hit_boostAP_Min || 0), + max: (obj.hit_boostAP_Max || 0) + }; + } + if (obj.hit_conditionsSource) { e.conditionsSource = obj.hit_conditionsSource; } + if (obj.hit_conditionsTarget) { e.conditionsTarget = obj.hit_conditionsTarget; } + } + + if (obj.hasKillEffect) { + result.killEffect = {}; + var e = result.killEffect; + if (obj.kill_boostHP_Min || obj.kill_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.kill_boostHP_Min || 0), + max: (obj.kill_boostHP_Max || 0) + }; + } + if (obj.kill_boostAP_Min || obj.kill_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.kill_boostAP_Min || 0), + max: (obj.kill_boostAP_Max || 0) + }; + } + if (obj.kill_conditionsSource) { e.conditionsSource = obj.kill_conditionsSource; } + } + + return result; + } + + function convertDroplist(obj) { + // [id|items[itemID|quantity_Min|quantity_Max|chance|]|]; + var result = { + id: obj.id, + items: _.map(obj.items, function(obj) { + return { + itemID: obj.itemID, + quantity: { + min: (obj.quantity_Min || 0), + max: (obj.quantity_Max || 0) + }, + chance: obj.chance + }; + }) + }; + + return result; + } + + function convertConversation(obj) { + // [id|message|rewards[rewardType|rewardID|value|]|replies[text|nextPhraseID|requires_Progress|requires_itemID|requires_Quantity|requires_Type|]|]; + + var result = { + id: obj.id, + message: obj.message, + rewards: obj.rewards + }; + if (obj.replies) { + result.replies = _.map(obj.replies, function(obj) { + var result = { + text: obj.text, + nextPhraseID: obj.nextPhraseID + }; + + if (obj.requires_Progress) { result.requires = { progress: obj.requires_Progress }; } + if (obj.requires_itemID) { + result.requires = result.requires || {}; + result.requires.item = { + itemID: obj.requires_itemID, + quantity: obj.requires_Quantity, + requireType: obj.requires_Type + }; + } + + return result; + }); + } + + return result; + } + + function convertCondition(obj) { + // [id|name|iconID|category|isStacking|isPositive| + // hasRoundEffect|round_visualEffectID|round_boostHP_Min|round_boostHP_Max|round_boostAP_Min|round_boostAP_Max| + // hasFullRoundEffect|fullround_visualEffectID|fullround_boostHP_Min|fullround_boostHP_Max|fullround_boostAP_Min|fullround_boostAP_Max| + // hasAbilityEffect|boostMaxHP|boostMaxAP|moveCostPenalty|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|]; + + var result = { + id: obj.id, + iconID: obj.iconID, + name: obj.name, + category: obj.category, + isStacking: obj.isStacking, + isPositive: obj.isPositive + }; + + if (obj.hasRoundEffect) { + result.roundEffect = {}; + var e = result.roundEffect; + if (obj.round_visualEffectID || obj.round_visualEffectID === 0) { e.visualEffectID = obj.round_visualEffectID; } + if (obj.round_boostHP_Min || obj.round_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.round_boostHP_Min || 0), + max: (obj.round_boostHP_Max || 0) + }; + } + if (obj.round_boostAP_Min || obj.round_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.round_boostAP_Min || 0), + max: (obj.round_boostAP_Max || 0) + }; + } + } + + if (obj.hasFullRoundEffect) { + result.fullRoundEffect = {}; + var e = result.fullRoundEffect; + if (obj.fullround_visualEffectID || obj.fullround_visualEffectID === 0) { e.visualEffectID = obj.fullround_visualEffectID; } + if (obj.fullround_boostHP_Min || obj.fullround_boostHP_Max) { + e.increaseCurrentHP = { + min: (obj.fullround_boostHP_Min || 0), + max: (obj.fullround_boostHP_Max || 0) + }; + } + if (obj.fullround_boostAP_Min || obj.fullround_boostAP_Max) { + e.increaseCurrentAP = { + min: (obj.fullround_boostAP_Min || 0), + max: (obj.fullround_boostAP_Max || 0) + }; + } + } + + if (obj.hasAbilityEffect) { + result.abilityEffect = {}; + var e = result.abilityEffect; + if (obj.boostMaxHP) { e.increaseMaxHP = obj.boostMaxHP; } + if (obj.boostMaxAP) { e.increaseMaxAP = obj.boostMaxAP; } + if (obj.moveCostPenalty) { e.increaseMoveCost = obj.moveCostPenalty; } + if (obj.attackCost) { e.increaseAttackCost = obj.attackCost; } + if (obj.attackChance) { e.increaseAttackChance = obj.attackChance; } + if (obj.criticalChance) { e.increaseCriticalSkill = obj.criticalChance; } + if (obj.criticalMultiplier) { e.setCriticalMultiplier = obj.criticalMultiplier; } + if (obj.attackDamage_Min || obj.attackDamage_Max) { + e.increaseAttackDamage = { + min: (obj.attackDamage_Min || 0), + max: (obj.attackDamage_Max || 0) + }; + } + if (obj.blockChance) { e.increaseBlockChance = obj.blockChance; } + if (obj.damageResistance) { e.increaseDamageResistance = obj.damageResistance; } + } + + return result; + } + + + return ATEditor; +})(ATEditor, ATEditor.model, ATEditor.FieldList, _); diff --git a/AndorsTrailEdit/model.js b/AndorsTrailEdit/model.js index 78b1e6b14..a8b8bc144 100644 --- a/AndorsTrailEdit/model.js +++ b/AndorsTrailEdit/model.js @@ -1,98 +1,96 @@ - -var model = (function(DataStore, FieldList) { +var ATEditor = (function(ATEditor, DataStore, FieldList, _) { var model = { actorConditions: new DataStore({ name: 'Actor Conditions' - ,objectTypename: 'actorcondition' + ,id: 'actorcondition' ,iconIDField: 'iconID' ,newItemTemplate: function() { return {name: "New Condition", id: 'new_condition' }; } }) ,quests: new DataStore({ name: 'Quests' - ,objectTypename: 'quest' + ,id: 'quest' ,newItemTemplate: function() { return {name: "New Quest", id: 'new_quest' }; } }) ,items: new DataStore({ name: 'Items' - ,objectTypename: 'item' + ,id: 'item' ,iconIDField: 'iconID' ,newItemTemplate: function() { return {name: "New Item", id: "new_item", category: 'other' }; } }) ,droplists: new DataStore({ name: 'Droplists' - ,objectTypename: 'droplist' + ,id: 'droplist' ,nameField: 'id' ,newItemTemplate: function() { return {id: "new_droplist" }; } }) ,dialogue: new DataStore({ name: 'Dialogue' - ,objectTypename: 'dialogue' + ,id: 'dialogue' ,nameField: 'id' ,newItemTemplate: function() { return {id: "new_conversation" }; } }) ,monsters: new DataStore({ name: 'Monsters' - ,objectTypename: 'monster' + ,id: 'monster' ,iconIDField: 'iconID' ,newItemTemplate: function() { return {id: "new_monster", name: "New Monster", maxAP: 10, attackCost: 5, moveCost: 5 }; } }) ,itemCategories: new DataStore({ name: 'Item Categories' - ,objectTypename: 'itemcategory' + ,id: 'itemcategory' ,newItemTemplate: function() { return {id: "new_itemtype", name: 'ItemType' }; } }) }; - model.sections = [ model.actorConditions, model.quests, model.items, model.droplists, model.dialogue, model.monsters, model.itemCategories ]; - + var sections = []; + var sectionIds = {}; + for (var key in model) { + var ds = model[key]; + sections.push(ds); + sectionIds[ds.id] = ds; + } + model.sections = sections; + model.getSectionFromID = function(id) { return sectionIds[id]; }; function addExampleModelItems(model) { - model.actorConditions.add({id: "bless", name: "Bless", isPositive: true, iconID: "actorconditions_1:38", category: 0, hasAbilityEffect: 1, attackChance: 15, blockChance: 5}); - model.actorConditions.add({id: "poison_weak", name: "Weak Poison", iconID: "actorconditions_1:60", category: 3, hasRoundEffect: 1, round_visualEffectID: 2, round_boostHP_Min: -1, round_boostHP_Max: -1}); + var _import = function(section, data) { + var _void = function() {}; + ATEditor.importExport.importDataObjects(section, data, _void, _void); + }; + + _import(model.actorConditions, [ + {id: "bless", name: "Bless", isPositive: true, iconID: "actorconditions_1:38", category: 0, hasAbilityEffect: 1, attackChance: 15, blockChance: 5} + ,{id: "poison_weak", name: "Weak Poison", iconID: "actorconditions_1:60", category: 3, hasRoundEffect: 1, round_visualEffectID: 2, round_boostHP_Min: -1, round_boostHP_Max: -1} + ]); - model.quests.add({id: "testQuest", name: "Test quest", stages: [ { progress: 10, logText: "Stage 10"} , { progress: 20, logText: "Stage 20", finishesQuest: 1 } ] }); + _import(model.quests, [ + {id: "testQuest", name: "Test quest", stages: [ { progress: 10, logText: "Stage 10"} , { progress: 20, logText: "Stage 20", finishesQuest: 1 } ] } + ]); - model.items.add({id: "item0", iconID: "items_weapons:0", name: "Longsword", category: 'lsword', baseMarketCost: 51, hasEquipEffect: 1, equip_attackChance: 10, equip_attackDamage_Min: 2, equip_attackDamage_Max: 4, equip_attackCost: 4}); - model.items.add({id: "dmg_ring1", iconID: "items_jewelry:0", name: "Ring of damage +1", category: 'ring', baseMarketCost: 62, hasEquipEffect: 1, equip_attackDamage_Min: 1, equip_attackDamage_Max: 1}); + _import(model.items, [ + {id: "item0", iconID: "items_weapons:0", name: "Longsword", category: 'lsword', baseMarketCost: 51, hasEquipEffect: 1, equip_attackChance: 10, equip_attackDamage_Min: 2, equip_attackDamage_Max: 4, equip_attackCost: 4} + ,{id: "dmg_ring1", iconID: "items_jewelry:0", name: "Ring of damage +1", category: 'ring', baseMarketCost: 62, hasEquipEffect: 1, equip_attackDamage_Min: 1, equip_attackDamage_Max: 1} + ]); - model.droplists.add({id: "merchant1", items: [ { itemID: 'dmg_ring1', quantity_Min: 4, quantity_Max: 5, chance: 100 } , { itemID: 'item0', quantity_Min: 1, quantity_Max: 1, chance: 100 } ] } ); + _import(model.droplists, [ + {id: "merchant1", items: [ { itemID: 'dmg_ring1', quantity_Min: 4, quantity_Max: 5, chance: 100 } , { itemID: 'item0', quantity_Min: 1, quantity_Max: 1, chance: 100 } ] } + ]); - model.dialogue.add({id: "mikhail_default", message: 'Anything else I can help you with?', replies: [ { text: 'Do you have any tasks for me?', nextPhraseID: 'mikhail_tasks' }, { text: 'Is there anything else you can tell me about Andor?', nextPhraseID: 'mikhail_andor1' } ]}); - model.dialogue.add({id: 'mikhail_andor1', message: 'As I said, Andor went out yesterday and hasn\'t been back since. I\'m starting to worry about him. Please go look for your brother, he said he would only be out a short while.'}); - model.dialogue.add({id: 'mikhail_tasks', message: 'Oh yes, there were some things I need help with, bread and rats. Which one would you like to talk about?'}); + _import(model.dialogue, [ + {id: "mikhail_default", message: 'Anything else I can help you with?', replies: [ { text: 'Do you have any tasks for me?', nextPhraseID: 'mikhail_tasks' }, { text: 'Is there anything else you can tell me about Andor?', nextPhraseID: 'mikhail_andor1' } ]} + ,{id: 'mikhail_andor1', message: 'As I said, Andor went out yesterday and hasn\'t been back since. I\'m starting to worry about him. Please go look for your brother, he said he would only be out a short while.'} + ,{id: 'mikhail_tasks', message: 'Oh yes, there were some things I need help with, bread and rats. Which one would you like to talk about?'} + ]); - model.monsters.add({id: "small_ant", name: "Small ant", iconID: "monsters_insects:2", maxHP: 30, size: ''}); - model.monsters.add({id: "red_ant", name: "Red ant", iconID: "monsters_insects:3", maxHP: 20, size: ''}); - model.monsters.add({id: "wasp", name: "Wasp", iconID: "monsters_insects:1", maxHP: 10, size: ''}); + _import(model.monsters, [ + {id: "small_ant", name: "Small ant", iconID: "monsters_insects:2", maxHP: 30 } + ,{id: "red_ant", name: "Red ant", iconID: "monsters_insects:3", maxHP: 20 } + ,{id: "wasp", name: "Wasp", iconID: "monsters_insects:1", maxHP: 10 } + ]); } addExampleModelItems(model); - function addLegacyFieldLists(model) { - model.actorConditions.legacyFieldList = new FieldList("[id|name|iconID|category|isStacking|isPositive|" - + "hasRoundEffect|round_visualEffectID|round_boostHP_Min|round_boostHP_Max|round_boostAP_Min|round_boostAP_Max|" - + "hasFullRoundEffect|fullround_visualEffectID|fullround_boostHP_Min|fullround_boostHP_Max|fullround_boostAP_Min|fullround_boostAP_Max|" - + "hasAbilityEffect|boostMaxHP|boostMaxAP|moveCostPenalty|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|" - + "];" - ); - model.quests.legacyFieldList = new FieldList("[id|name|showInLog|stages[progress|logText|rewardExperience|finishesQuest|]|];"); - model.items.legacyFieldList = new FieldList("[id|iconID|name|category|displaytype|hasManualPrice|baseMarketCost|" - + "hasEquipEffect|equip_boostMaxHP|equip_boostMaxAP|equip_moveCostPenalty|equip_attackCost|equip_attackChance|equip_criticalChance|equip_criticalMultiplier|equip_attackDamage_Min|equip_attackDamage_Max|equip_blockChance|equip_damageResistance|equip_conditions[condition|magnitude|]|" - + "hasUseEffect|use_boostHP_Min|use_boostHP_Max|use_boostAP_Min|use_boostAP_Max|use_conditionsSource[condition|magnitude|duration|chance|]|" - + "hasHitEffect|hit_boostHP_Min|hit_boostHP_Max|hit_boostAP_Min|hit_boostAP_Max|hit_conditionsSource[condition|magnitude|duration|chance|]|hit_conditionsTarget[condition|magnitude|duration|chance|]|" - + "hasKillEffect|kill_boostHP_Min|kill_boostHP_Max|kill_boostAP_Min|kill_boostAP_Max|kill_conditionsSource[condition|magnitude|duration|chance|]|" - + "];" - ); - model.droplists.legacyFieldList = new FieldList("[id|items[itemID|quantity_Min|quantity_Max|chance|]|];"); - model.dialogue.legacyFieldList = new FieldList("[id|message|rewards[rewardType|rewardID|value|]|replies[text|nextPhraseID|requires_Progress|requires_itemID|requires_Quantity|requires_Type|]|];"); - model.monsters.legacyFieldList = new FieldList("[id|iconID|name|tags|size|monsterClass|unique|faction|maxHP|maxAP|moveCost|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|droplistID|phraseID|" - + "hasHitEffect|onHit_boostHP_Min|onHit_boostHP_Max|onHit_boostAP_Min|onHit_boostAP_Max|onHit_conditionsSource[condition|magnitude|duration|chance|]|onHit_conditionsTarget[condition|magnitude|duration|chance|]|" - + "];" - ); - model.itemCategories.legacyFieldList = new FieldList("[id|name|actionType|inventorySlot|size|];"); - } - addLegacyFieldLists(model); - - - return model; -})(DataStore, FieldList.FieldList); + ATEditor.model = model; + return ATEditor; +})(ATEditor, ATEditor.DataStore, ATEditor.FieldList, _); diff --git a/AndorsTrailEdit/utils.js b/AndorsTrailEdit/utils.js new file mode 100644 index 000000000..4484a767e --- /dev/null +++ b/AndorsTrailEdit/utils.js @@ -0,0 +1,55 @@ +var ATEditor = (function(ATEditor, _) { + + function deepClone(o) { + // https://github.com/documentcloud/underscore/issues/162 + return JSON.parse(JSON.stringify(o)); + } + + function removeDefaults(o, defaults) { + for (var key in defaults) { + if (o[key] === defaults[key]) { + delete o[key]; + } + } + } + function cleanCopy(o, defaults) { + if (!o) { return null; } + o = _.clone(o); + if (defaults) { + removeDefaults(o, defaults); + } + for (var key in o) { + if (key.charAt(0) === '$') { + delete o[key]; + } + } + for (var key in o) { + var v = o[key]; + if (!v) { + delete o[key]; + } else if (_.isArray(v)) { + if (!_.some(v)) { + delete o[key]; + } else { + o[key] = _.map(v, function(o) { cleanCopy(o); }); + } + } else if (_.isObject(v)) { + v = cleanCopy(v); + if (v) { + o[key] = v; + } else { + delete o[key]; + } + } + } + if (!_.some(_.keys(o))) { return null; } + return o; + } + + ATEditor.utils = { + deepClone: deepClone + ,cleanCopy: cleanCopy + }; + + return ATEditor; +})(ATEditor || {}, _);