├── .gitattributes ├── .gitignore ├── README.md ├── build ├── css │ ├── app.min.css │ ├── preload.min.css │ └── vendor.min.css ├── desktop.js ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── imgs │ ├── closedhand.cur │ └── favicon.ico ├── index.html ├── js │ ├── app.min.js │ ├── preload.min.js │ ├── templates.min.js │ └── vendor.min.js └── package.json ├── debug.png ├── editor.png ├── editor ├── BUILD.md ├── LICENSE ├── README.md ├── bower.json ├── gulpfile.js ├── package.json └── src │ ├── app │ ├── app.controller.js │ ├── app.js │ ├── app.routes.js │ ├── directives │ │ ├── dragnode.directive.js │ │ ├── dropnode.directive.js │ │ ├── keytable.directive.js │ │ ├── keytable.html │ │ ├── tab.directive.js │ │ ├── tab.html │ │ ├── tabset.directive.js │ │ └── tabset.html │ ├── models │ │ ├── project.model.js │ │ └── settings.model.js │ ├── pages │ │ ├── dash │ │ │ ├── dash.controller.js │ │ │ └── dash.html │ │ ├── editor │ │ │ ├── components │ │ │ │ ├── menubar.controller.js │ │ │ │ ├── menubar.html │ │ │ │ ├── nodespanel.controller.js │ │ │ │ ├── nodespanel.html │ │ │ │ ├── propertiespanel.controller.js │ │ │ │ ├── propertiespanel.html │ │ │ │ ├── treespanel.controller.js │ │ │ │ └── treespanel.html │ │ │ ├── editor.controller.js │ │ │ ├── editor.html │ │ │ └── modals │ │ │ │ ├── editnode.controller.js │ │ │ │ ├── editnode.html │ │ │ │ ├── export.controller.js │ │ │ │ ├── export.html │ │ │ │ ├── import.controller.js │ │ │ │ ├── import.html │ │ │ │ └── modal.html │ │ ├── home │ │ │ ├── home.controller.js │ │ │ └── home.html │ │ ├── projects │ │ │ ├── projects.controller.js │ │ │ └── projects.html │ │ └── settings │ │ │ ├── settings.controller.js │ │ │ └── settings.html │ ├── services │ │ ├── dialog.service.js │ │ ├── editor.service.js │ │ ├── nodejs.service.js │ │ ├── notification.service.js │ │ ├── storage │ │ │ ├── filestorage.service.js │ │ │ ├── localstorage.service.js │ │ │ └── storage.service.js │ │ └── system.service.js │ └── validators │ │ └── blacklist.directive.js │ ├── assets │ ├── css │ │ └── preload.css │ ├── imgs │ │ ├── closedhand.cur │ │ └── favicon.ico │ ├── js │ │ └── preload.js │ ├── less │ │ ├── animations.less │ │ ├── bootstrap.less │ │ ├── c_app.less │ │ ├── c_dash.less │ │ ├── c_keytable.less │ │ ├── c_menubar.less │ │ ├── c_modal.less │ │ ├── c_nodes.less │ │ ├── c_notification.less │ │ ├── c_page.less │ │ ├── c_properties.less │ │ ├── c_sidebar.less │ │ ├── c_tabset.less │ │ ├── c_trees.less │ │ ├── index.less │ │ └── variables.less │ └── libs │ │ ├── behavior3js-0.1.0.min.js │ │ ├── createjs.min.js │ │ ├── creatine-1.0.0.min.js │ │ └── mousetrap.min.js │ ├── desktop.js │ ├── editor │ ├── draw │ │ ├── shapes.js │ │ └── symbols.js │ ├── editor │ │ ├── Editor.js │ │ ├── managers │ │ │ ├── ExportManager.js │ │ │ ├── ImportManager.js │ │ │ ├── ProjectManager.js │ │ │ └── ShortcutManager.js │ │ └── systems │ │ │ ├── CameraSystem.js │ │ │ ├── CollapseSystem.js │ │ │ ├── ConnectionSystem.js │ │ │ ├── DragSystem.js │ │ │ ├── SelectionSystem.js │ │ │ └── ShortcutSystem.js │ ├── namespaces.js │ ├── project │ │ ├── Project.js │ │ └── managers │ │ │ ├── HistoryManager.js │ │ │ ├── NodeManager.js │ │ │ └── TreeManager.js │ ├── tree │ │ ├── Tree.js │ │ └── managers │ │ │ ├── BlockManager.js │ │ │ ├── ConnectionManager.js │ │ │ ├── EditManager.js │ │ │ ├── OrganizeManager.js │ │ │ ├── SelectionManager.js │ │ │ └── ViewManager.js │ └── utils │ │ ├── Block.js │ │ ├── Command.js │ │ ├── Connection.js │ │ ├── EditorError.js │ │ ├── Node.js │ │ ├── Root.js │ │ ├── SelectionBox.js │ │ ├── SettingsManager.js │ │ ├── functions.js │ │ └── settings.js │ ├── index.html │ ├── package.json │ └── start.js ├── examples └── ai.json ├── lua └── src │ ├── BTInit.lua │ ├── BTNode.lua │ ├── BTPrecondition.lua │ ├── BTReference.lua │ ├── action │ ├── BTAction.lua │ ├── BTRunAction.lua │ └── BTWaitAction.lua │ ├── cjson.so │ ├── composite │ ├── BTParallel.lua │ ├── BTParallelFlexible.lua │ ├── BTPrioritySelector.lua │ ├── BTRandomSelector.lua │ └── BTSequence.lua │ ├── condition │ └── BTCondition.lua │ ├── decorator │ ├── BTConditionEvulator.lua │ ├── BTDecorator.lua │ ├── BTInverse.lua │ ├── BTRepeat.lua │ └── BTUntil.lua │ ├── global.lua │ └── test.lua └── unityeditor.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Lua -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | editor/build 2 | editor/node_modules 3 | editor/bower_components 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BtTree 2 | A behaviour tree framework with editor. 3 | ![](debug.png) 4 | Run in Unity Editor: 5 | ![](unityeditor.png) 6 | 7 | ## Features: 8 | * simple 9 | * fast 10 | * extendable 11 | * visual-editing 12 | * visual-debuggable 13 | * platform-independent 14 | 15 | ![](editor.png) 16 | 17 | ## Support Languages: 18 | * Lua 19 | * to be continued... 20 | 21 | ## Usage: 22 | * edit behaviour scripts(json) with editor 23 | * see: lua/src/test.lua as a demo 24 | 25 | ## Roadmap: 26 | * more composite nodes, such as RandomSelector... 27 | * more decorators and editor support. 28 | * other languages support, maybe python or java first... 29 | * add debug&release feature for editor, i.e., remove debug infomations from json output files. 30 | 31 | enjoy it, any suggestion will be appreciated. you can contact me via email: 49392515@qq.com 32 | -------------------------------------------------------------------------------- /build/desktop.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); // Module to control application life. 2 | var BrowserWindow = require('browser-window'); // Module to create native browser window. 3 | 4 | // Report crashes to our server. 5 | require('crash-reporter').start(); 6 | 7 | // Keep a global reference of the window object, if you don't, the window will 8 | // be closed automatically when the JavaScript object is garbage collected. 9 | var mainWindow = null; 10 | 11 | // Quit when all windows are closed. 12 | app.on('window-all-closed', function() { 13 | // On OS X it is common for applications and their menu bar 14 | // to stay active until the user quits explicitly with Cmd + Q 15 | if (process.platform != 'darwin') { 16 | app.quit(); 17 | } 18 | }); 19 | 20 | // This method will be called when Electron has finished 21 | // initialization and is ready to create browser windows. 22 | app.on('ready', function() { 23 | // Create the browser window. 24 | mainWindow = new BrowserWindow({width: 1000, height: 800}); 25 | 26 | // and load the index.html of the app. 27 | mainWindow.loadUrl('file://' + __dirname + '/index.html'); 28 | 29 | // Open the DevTools. 30 | // mainWindow.openDevTools(); 31 | 32 | // Emitted when the window is closed. 33 | mainWindow.on('closed', function() { 34 | // Dereference the window object, usually you would store windows 35 | // in an array if your app supports multi windows, this is the time 36 | // when you should delete the corresponding element. 37 | mainWindow = null; 38 | }); 39 | }); -------------------------------------------------------------------------------- /build/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /build/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /build/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /build/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /build/imgs/closedhand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/imgs/closedhand.cur -------------------------------------------------------------------------------- /build/imgs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/build/imgs/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Behavior3 Editor - v0.3.0 - http://behavior3.com 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | Behavior3 Editor 22 | 23 | 24 | Loading... 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /build/js/preload.min.js: -------------------------------------------------------------------------------- 1 | function preloadProgress(e){var r=document.getElementById("page-preload-progress");r.innerHTML=e} -------------------------------------------------------------------------------- /build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "behavior3editor", 3 | "version" : "0.3.0", 4 | "main" : "desktop.js", 5 | "window": { 6 | "title" : "Behavior3 Editor (v0.3.0) http://behavior3.com", 7 | "position" : "center", 8 | "toolbar" : false, 9 | "frame" : true, 10 | "width" : 1000, 11 | "height" : 800, 12 | "min_width" : 1000, 13 | "min_height" : 800 14 | } 15 | } -------------------------------------------------------------------------------- /debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/debug.png -------------------------------------------------------------------------------- /editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/editor.png -------------------------------------------------------------------------------- /editor/BUILD.md: -------------------------------------------------------------------------------- 1 | # Building Behavior3 Editor 2 | 3 | You can build the editor in two different environments: for development and for production. For development you can run a local web server that will build and reload automatically the application for each new modification on the project. The production mode builds and package the editor for different platforms. 4 | 5 | 6 | ## Requirements 7 | 8 | To run the editor you will need the following softwares: 9 | 10 | **required for everything:** 11 | - [NodeJS](https://nodejs.org) 12 | - [Bower](http://bower.io) 13 | 14 | *if you want to run/build the desktop version:* 15 | - [Node-Webkit](http://nwjs.io) 16 | - [Node-Webkit Builder](https://github.com/nwjs/nw-builder) 17 | 18 | 19 | ## Configuration 20 | 21 | Before building, you need to install some 3rd-party libraries. You need to run in console the following commands: 22 | 23 | npm install 24 | 25 | and: 26 | 27 | bower install 28 | 29 | The former installs a bunch of NodeJS modules, which are used on the building system and some dependences of the desktop application. The last installs CSS and Javascript vendor libraries. 30 | 31 | 32 | ## Building during development 33 | 34 | During development you can run the editor in a web browser with automatically building and reloading: 35 | 36 | gulp serve 37 | 38 | which will run a web server hosted on `http://127.0.0.1:8000`. 39 | 40 | To run the desktop version (without automatically building and reload): 41 | 42 | gulp nw 43 | 44 | 45 | ## Building final version 46 | 47 | Just run: 48 | 49 | gulp dist 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /editor/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Renato de Pontes Pereira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /editor/README.md: -------------------------------------------------------------------------------- 1 | # bttree editor 2 | >如何安装请查看BUILD.md 3 | 4 | *** 5 | 需要nodejs <= 11.x 6 | -------------------------------------------------------------------------------- /editor/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "behavior3editor", 3 | "version": "0.2.0dev", 4 | "directory": "src/vendor/", 5 | "devDependencies": { 6 | "angular" : "~1.4", 7 | "bootstrap" : "~3.1.1", 8 | "angular-animate" : "~1.4", 9 | "angular-bootstrap" : "~0.13.0", 10 | "angular-ui-router" : "~0.2.14", 11 | "fontawesome" : "~4.3.0", 12 | "less" : "~2.5.0", 13 | "sweetalert" : "~1.0.0-beta" 14 | }, 15 | "dependencies": {} 16 | } -------------------------------------------------------------------------------- /editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Renato de Pontes Pereira", 3 | "name": "behavior3editor", 4 | "version": "0.3.0", 5 | "homepage": "http://behavior3.com", 6 | "license": "MIT", 7 | "bugs": "https://github.com/behavior3/behavior3editor/issues", 8 | "repository": "github:behavior3/behavior3editor", 9 | "devDependencies": { 10 | "electron-packager": "^5.1.1", 11 | "electron-prebuilt": "^0.33.8", 12 | "gulp": "~3.9.0", 13 | "gulp-angular-templatecache": "~1.7.0", 14 | "gulp-concat": "~2.6.0", 15 | "gulp-connect": "~2.2.0", 16 | "gulp-foreach": "^0.1.0", 17 | "gulp-jshint": "~1.11.2", 18 | "gulp-less": "~3.0.3", 19 | "gulp-minify-css": "~1.2.1", 20 | "gulp-minify-html": "~1.0.4", 21 | "gulp-replace": "~0.5.4", 22 | "gulp-uglify": "~1.4.1", 23 | "gulp-zip": "^3.0.2", 24 | "jshint-stylish": "~2.0.1", 25 | "merge-stream": "~1.0.0", 26 | "rimraf": "^2.4.3" 27 | }, 28 | "dependencies": {} 29 | } 30 | -------------------------------------------------------------------------------- /editor/src/app/app.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('AppController', AppController); 7 | 8 | AppController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService' 12 | ]; 13 | 14 | function AppController($scope, 15 | $window, 16 | dialogService) { 17 | 18 | // HEAD // 19 | var vm = this; 20 | 21 | _active(); 22 | 23 | // BODY // 24 | function _active() { 25 | window.onbeforeunload = _onBeforeCloseBrowser; 26 | 27 | try { 28 | var gui = require('nw.gui'); 29 | var win = gui.Window.get(); 30 | 31 | win.on('close', function() { 32 | _onBeforeCloseDesktop(win); 33 | }); 34 | } catch (e) {} 35 | } 36 | 37 | function _onBeforeCloseBrowser() { 38 | if ($window.editor.isDirty()) { 39 | return "Leaving now will erase your unsaved changes."; 40 | } 41 | } 42 | function _onBeforeCloseDesktop(win) { 43 | if ($window.editor.isDirty()) { 44 | dialogService 45 | .confirm( 46 | 'Leave without saving?', 47 | 'If you proceed you will lose all unsaved modifications.', 48 | null) 49 | .then(function() { 50 | win.close(true); 51 | }); 52 | return false; 53 | } else { 54 | win.close(true); 55 | } 56 | 57 | } 58 | } 59 | 60 | })(); -------------------------------------------------------------------------------- /editor/src/app/app.js: -------------------------------------------------------------------------------- 1 | angular.module('app', [ 2 | 'ui.router', 3 | 'ui.bootstrap', 4 | 'ngAnimate', 5 | 'templates' 6 | ]) 7 | 8 | .run(['$rootScope', '$window', '$state', 9 | function Execute($rootScope, $window, $state) { 10 | $rootScope.isDesktop = !!$window.process && !!$window.require; 11 | 12 | $rootScope.go = function(state, params) { 13 | $state.go(state, params); 14 | }; 15 | } 16 | ]) 17 | 18 | .run(['$window', '$animate', '$location', '$document', '$timeout', 'settingsModel', 'projectModel', 19 | function Execute($window, 20 | $animate, 21 | $location, 22 | $document, 23 | $timeout, 24 | settingsModel, 25 | projectModel) { 26 | 27 | // reset path 28 | $location.path('/'); 29 | 30 | // add drop to canvas 31 | angular 32 | .element($window.editor._game.canvas) 33 | .attr('b3-drop-node', true); 34 | 35 | // initialize editor 36 | settingsModel.getSettings(); 37 | projectModel 38 | .getRecentProjects() 39 | .then(function(projects) { 40 | 41 | function closePreload() { 42 | $timeout(function() { 43 | var element = angular.element(document.getElementById('page-preload')); 44 | $animate.addClass(element, 'preload-fade') 45 | .then(function() { 46 | element.remove(); 47 | }); 48 | }, 500); 49 | } 50 | 51 | if (projects.length > 0 && projects[0].isOpen) { 52 | projectModel 53 | .openProject(projects[0].path) 54 | .then(function() { 55 | closePreload(); 56 | }); 57 | } else { 58 | closePreload(); 59 | } 60 | }); 61 | } 62 | ]); 63 | -------------------------------------------------------------------------------- /editor/src/app/app.routes.js: -------------------------------------------------------------------------------- 1 | angular.module('app') 2 | 3 | .config(['$stateProvider', '$urlRouterProvider', 4 | function($stateProvider, $urlRouterProvider) { 5 | $urlRouterProvider.otherwise('/dash/projects'); 6 | 7 | $stateProvider 8 | // Dash 9 | .state('dash', { 10 | url: '/dash', 11 | abstract: true, 12 | templateUrl: 'pages/dash/dash.html', 13 | controller: 'DashController', 14 | controllerAs: 'dash', 15 | }) 16 | // .state('dash.home', { 17 | // url: '/home', 18 | // templateUrl: 'pages/home/home.html', 19 | // controller: 'HomeController', 20 | // controllerAs: 'home', 21 | // }) 22 | .state('dash.projects', { 23 | url: "/projects", 24 | templateUrl: 'pages/projects/projects.html', 25 | controller: 'ProjectsController', 26 | controllerAs: 'projects', 27 | }) 28 | .state('dash.settings', { 29 | url: "/settings", 30 | templateUrl: 'pages/settings/settings.html', 31 | controller: 'SettingsController', 32 | controllerAs: 'settings', 33 | }) 34 | 35 | // Editor 36 | .state('editor', { 37 | url: "/editor", 38 | templateUrl: 'pages/editor/editor.html', 39 | controller: 'EditorController', 40 | controllerAs: 'editor', 41 | }) 42 | .state('editor.editnode', { 43 | url: "/node/:name", 44 | templateUrl: 'pages/editor/modals/editnode.html', 45 | controller: 'EditNodeController', 46 | controllerAs: 'editnode', 47 | }) 48 | .state('editor.export', { 49 | url: "/export/:type/:format", 50 | templateUrl: 'pages/editor/modals/export.html', 51 | controller: 'ExportController', 52 | controllerAs: 'export', 53 | }) 54 | .state('editor.import', { 55 | url: "/import/:type/:format", 56 | templateUrl: 'pages/editor/modals/import.html', 57 | controller: 'ImportController', 58 | controllerAs: 'import', 59 | }); 60 | } 61 | ]); -------------------------------------------------------------------------------- /editor/src/app/directives/dragnode.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3DragNode', dragNode); 7 | 8 | dragNode.$inject = [ 9 | '$window' 10 | ]; 11 | 12 | function dragNode($window) { 13 | var directive = { 14 | restrict : 'A', 15 | link : link, 16 | }; 17 | return directive; 18 | 19 | function link(scope, element, attrs) { 20 | element.attr('draggable', 'true'); 21 | 22 | element.bind('dragstart', function(e) { 23 | var canvas = $window.editor.preview(attrs.name); 24 | var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome')>-1; 25 | 26 | if (isChrome) { 27 | var img = document.createElement('img'); 28 | img.src = canvas.toDataURL(); 29 | 30 | // 10ms delay in order to proper create the image object 31 | // ugly hack =( 32 | var time = (new Date()).getTime(); 33 | var delay = time + 10; 34 | while (time < delay) { 35 | time = (new Date()).getTime(); 36 | } 37 | canvas = img; 38 | } 39 | 40 | e.dataTransfer.setData('name', attrs.name); 41 | e.dataTransfer.setDragImage(canvas, canvas.width/2, canvas.height/2); 42 | }); 43 | } 44 | } 45 | 46 | })(); 47 | 48 | 49 | // .directive('draggableNode', function($window) { 50 | // return { 51 | // restrict: 'A', 52 | // link: function(scope, element, attributes, controller) { 53 | // angular.element(element).attr("draggable", "true"); 54 | // element.bind("dragstart", function(e) { 55 | // var img = $window.app.editor.preview(attributes.id.replace('node-', '')); 56 | 57 | // e.dataTransfer.setData('text', attributes.id); 58 | // e.dataTransfer.setDragImage(img, img.width/2, img.height/2); 59 | // }); 60 | // } 61 | // } 62 | // }) 63 | -------------------------------------------------------------------------------- /editor/src/app/directives/dropnode.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3DropNode', dropNode); 7 | 8 | dropNode.$inject = [ 9 | '$window' 10 | ]; 11 | 12 | function dropNode($window) { 13 | var directive = { 14 | restrict : 'A', 15 | link : link, 16 | }; 17 | return directive; 18 | 19 | function link(scope, element, attrs) { 20 | element.bind('dragover', function(e) { 21 | if (e.preventDefault) { 22 | e.preventDefault(); 23 | } 24 | return false; 25 | }); 26 | element.bind('drop', function(e) { 27 | if (e.preventDefault) { 28 | e.preventDefault(); 29 | } 30 | if (e.stopPropagation) { 31 | e.stopPropagation(); 32 | } 33 | 34 | var name = e.dataTransfer.getData('name'); 35 | 36 | var project = $window.editor.project.get(); 37 | var tree = project.trees.getSelected(); 38 | var point = tree.view.getLocalPoint(e.clientX, e.clientY); 39 | tree.blocks.add(name, point.x, point.y); 40 | 41 | $window.editor._game.canvas.focus(); 42 | }); 43 | } 44 | } 45 | 46 | })(); 47 | -------------------------------------------------------------------------------- /editor/src/app/directives/keytable.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3KeyTable', keytable) 7 | .controller('KeyTableController', KeyTableController); 8 | 9 | keytable.$inject = ['$parse']; 10 | function keytable($parse) { 11 | var directive = { 12 | require : '^ngModel', 13 | restrict : 'EA', 14 | replace : true, 15 | bindToController : true, 16 | controller : 'KeyTableController', 17 | controllerAs : 'keytable', 18 | templateUrl : 'directives/keytable.html', 19 | link: link 20 | }; 21 | return directive; 22 | 23 | function link(scope, element, attrs) { 24 | // get the value of the `ng-model` attribute 25 | scope.keytable.heading = attrs.heading; 26 | scope.keytable._onChange = $parse(attrs.ngChange); 27 | 28 | var variable = attrs.ngModel; 29 | scope.$watch(variable, function(model) { 30 | scope.keytable.reset(model); 31 | }); 32 | } 33 | } 34 | 35 | KeyTableController.$inject = ['$scope']; 36 | function KeyTableController($scope) { 37 | // HEAD // 38 | var vm = this; 39 | vm._onChange = null; 40 | vm.model = $scope.keytable.model || $scope.model || null; 41 | vm.rows = []; 42 | vm.add = add; 43 | vm.remove = remove; 44 | vm.change = change; 45 | vm.reset = reset; 46 | 47 | _activate(); 48 | 49 | // BODY // 50 | function _activate() { 51 | if (vm.model) { 52 | for (var key in vm.model) { 53 | add(key, vm.model[key], false); 54 | } 55 | } else { 56 | vm.model = {}; 57 | } 58 | } 59 | 60 | function reset(model) { 61 | vm.rows = []; 62 | vm.model = model; 63 | _activate(); 64 | } 65 | 66 | function add(key, value, fixed) { 67 | vm.rows.push({key:key, value:value, fixed:fixed===true}); 68 | } 69 | 70 | function remove(i) { 71 | vm.rows.splice(i, 1); 72 | } 73 | 74 | function change() { 75 | for (var key in vm.model){ 76 | if (vm.model.hasOwnProperty(key)){ 77 | delete vm.model[key]; 78 | } 79 | } 80 | for (var i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /editor/src/app/directives/tab.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3Tab', tab); 7 | 8 | function tab() { 9 | var directive = { 10 | require : '^b3Tabset', 11 | restrict : 'EA', 12 | scope : { 13 | active : '=?', 14 | heading : '@' 15 | }, 16 | transclude : true, 17 | templateUrl : 'directives/tab.html', 18 | link : link, 19 | }; 20 | return directive; 21 | 22 | function link(scope, element, attrs, ctrl) { 23 | scope.active = !!scope.active; 24 | ctrl.add(scope); 25 | } 26 | } 27 | 28 | })(); -------------------------------------------------------------------------------- /editor/src/app/directives/tab.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /editor/src/app/directives/tabset.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3Tabset', tabset); 7 | 8 | function tabset() { 9 | var directive = { 10 | restrict : 'EA', 11 | transclude : true, 12 | replace : true, 13 | scope : {}, 14 | templateUrl : 'directives/tabset.html', 15 | bindToController : true, 16 | controllerAs : 'tabset', 17 | controller : tabsetController, 18 | }; 19 | return directive; 20 | } 21 | 22 | function tabsetController() { 23 | // HEAD // 24 | /* jshint validthis: true */ 25 | var vm = this; 26 | vm.tabs = []; 27 | vm.add = add; 28 | vm.select = select; 29 | 30 | // BODY // 31 | function add(tab) { 32 | vm.tabs.push(tab); 33 | } 34 | 35 | function select(tab) { 36 | angular.forEach(vm.tabs, function(t) { 37 | if (t.active && t !== tab) { 38 | t.active = false; 39 | } 40 | }); 41 | 42 | tab.active = true; 43 | } 44 | } 45 | 46 | 47 | })(); -------------------------------------------------------------------------------- /editor/src/app/directives/tabset.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 8 | 9 |
-------------------------------------------------------------------------------- /editor/src/app/models/settings.model.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .factory('settingsModel', settingsModel); 7 | 8 | settingsModel.$inject = [ 9 | '$q', 10 | 'storageService', 11 | 'systemService', 12 | 'editorService' 13 | ]; 14 | 15 | function settingsModel($q, 16 | storageService, 17 | systemService, 18 | editorService) { 19 | 20 | // HEADER // 21 | var settingsPath = systemService.join(systemService.getDataPath(), 'settings.json'); 22 | var settingsCache = null; 23 | 24 | var service = { 25 | getSettings : getSettings, 26 | saveSettings : saveSettings, 27 | resetSettings : resetSettings, 28 | }; 29 | return service; 30 | 31 | // BODY // 32 | function getSettings() { 33 | return $q(function(resolve, reject) { 34 | if (!settingsCache) { 35 | var data; 36 | var defaultData = editorService.getDefaultSettings(); 37 | try { 38 | data = storageService.load(settingsPath); 39 | editorService.applySettings(data); 40 | } catch (e) {} 41 | 42 | // Create if storage file does not exist 43 | if (!data) { 44 | data = defaultData; 45 | storageService.save(settingsPath, data); 46 | } 47 | 48 | settingsCache = tine.merge({}, defaultData, data); 49 | } 50 | 51 | resolve(settingsCache); 52 | }); 53 | } 54 | function saveSettings(settings) { 55 | return $q(function(resolve, reject) { 56 | editorService.applySettings(settings); 57 | storageService.save(settingsPath, settings); 58 | settingsCache = settings; 59 | resolve(); 60 | }); 61 | } 62 | function resetSettings() { 63 | return $q(function(resolve, reject) { 64 | var settings = editorService.getDefaultSettings(); 65 | storageService.save(settingsPath, settings); 66 | settingsCache = settings; 67 | editorService.applySettings(settings); 68 | resolve(); 69 | }); 70 | } 71 | } 72 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/dash/dash.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('DashController', DashController); 7 | 8 | DashController.$inject = [ 9 | '$scope', 10 | 'projectModel' 11 | ]; 12 | 13 | function DashController($scope, projectModel) { 14 | var vm = this; 15 | vm.project = null; 16 | _activate(); 17 | 18 | function _activate() { 19 | vm.project = projectModel.getProject(); 20 | } 21 | $scope.$on('dash-projectchanged', function() { 22 | _activate(); 23 | }); 24 | } 25 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/dash/dash.html: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/nodespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('NodespanelController', NodespanelController); 7 | 8 | NodespanelController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService', 12 | 'notificationService' 13 | ]; 14 | 15 | function NodespanelController($scope, 16 | $window, 17 | dialogService, 18 | notificationService) { 19 | 20 | // HEAD // 21 | var vm = this; 22 | vm.nodes = null; 23 | vm.newTree = newTree; 24 | vm.select = select; 25 | vm.remove = remove; 26 | 27 | _create(); 28 | _activate(); 29 | $scope.$on('$destroy', _destroy); 30 | 31 | // BODY // 32 | function _activate() { 33 | vm.trees = []; 34 | vm.nodes = { 35 | composite : [], 36 | decorator : [], 37 | action : [], 38 | condition : [], 39 | }; 40 | 41 | var p = $window.editor.project.get(); 42 | p.nodes.each(function(node) { 43 | if (node.category === 'tree') return; 44 | 45 | var list = vm.nodes[node.category]; 46 | if (!list) return; 47 | list.push({ 48 | name: node.name, 49 | title: _getTitle(node), 50 | isDefault: node.isDefault 51 | }); 52 | }); 53 | 54 | var selected = p.trees.getSelected(); 55 | p.trees.each(function(tree) { 56 | var root = tree.blocks.getRoot(); 57 | vm.trees.push({ 58 | 'id' : tree._id, 59 | 'name' : root.title || 'A behavior tree', 60 | 'active' : tree===selected, 61 | }); 62 | }); 63 | } 64 | 65 | function _event(e) { 66 | setTimeout(function() {$scope.$apply(function() { _activate(); });}, 0); 67 | } 68 | 69 | function _create() { 70 | $window.editor.on('nodechanged', _event); 71 | $window.editor.on('noderemoved', _event); 72 | $window.editor.on('nodeadded', _event); 73 | $window.editor.on('treeadded', _event); 74 | $window.editor.on('blockchanged', _event); 75 | $window.editor.on('treeselected', _event); 76 | $window.editor.on('treeremoved', _event); 77 | $window.editor.on('treeimported', _event); 78 | } 79 | 80 | function _destroy() { 81 | $window.editor.off('nodechanged', _event); 82 | $window.editor.off('noderemoved', _event); 83 | $window.editor.off('nodeadded', _event); 84 | $window.editor.off('treeadded', _event); 85 | $window.editor.off('blockchanged', _event); 86 | $window.editor.off('treeselected', _event); 87 | $window.editor.off('treeremoved', _event); 88 | $window.editor.off('treeimported', _event); 89 | } 90 | 91 | function _getTitle(node) { 92 | var title = node.title || node.name; 93 | title = title.replace(/(<\w+>)/g, function(match, key) { return '@'; }); 94 | return title; 95 | } 96 | 97 | function newTree() { 98 | var p = $window.editor.project.get(); 99 | p.trees.add(); 100 | } 101 | 102 | function select(id) { 103 | var p = $window.editor.project.get(); 104 | p.trees.select(id); 105 | } 106 | 107 | function remove(id) { 108 | dialogService. 109 | confirm( 110 | 'Remove tree?', 111 | 'Are you sure you want to remove this tree?\n\nNote: all blocks using this tree will be removed.' 112 | ).then(function() { 113 | var p = $window.editor.project.get(); 114 | p.trees.remove(id); 115 | notificationService.success( 116 | 'Tree removed', 117 | 'The tree has been removed from this project.' 118 | ); 119 | }); 120 | } 121 | } 122 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/nodespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 12 | 13 | 14 |
15 |
16 | 21 | 22 | 24 | Trees 25 | 26 |
27 | 28 |
29 |
30 | 31 | 49 |
50 |
51 | 52 |
53 | 58 | 59 | 61 | Nodes 62 | 63 |
64 | 65 |
66 |
67 |
{{category}}s
68 | 87 |
    88 |
  • empty
  • 89 |
90 |
91 | 92 |
93 |
94 | 95 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/propertiespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('PropertiespanelController', PropertiespanelController); 7 | 8 | PropertiespanelController.$inject = [ 9 | '$scope', 10 | '$window' 11 | ]; 12 | 13 | function PropertiespanelController($scope, 14 | $window) { 15 | var vm = this; 16 | vm.original = null; 17 | vm.block = null; 18 | vm.update = update; 19 | vm.keydown = keydown; 20 | 21 | _create(); 22 | _activate(); 23 | 24 | $scope.$on('$destroy', _destroy); 25 | 26 | function _activate() { 27 | var p = $window.editor.project.get(); 28 | var t = p.trees.getSelected(); 29 | var s = t.blocks.getSelected(); 30 | 31 | if (s.length === 1) { 32 | vm.original = s[0]; 33 | vm.block = { 34 | title : vm.original.title, 35 | description : vm.original.description, 36 | properties : tine.merge({}, vm.original.properties) 37 | }; 38 | } else { 39 | vm.original = false; 40 | vm.block = false; 41 | } 42 | } 43 | function _event(e) { 44 | setTimeout(function() {$scope.$apply(function() { _activate(); });}, 0); 45 | 46 | } 47 | function _create() { 48 | $window.editor.on('blockselected', _event); 49 | $window.editor.on('blockdeselected', _event); 50 | $window.editor.on('blockremoved', _event); 51 | $window.editor.on('treeselected', _event); 52 | $window.editor.on('nodechanged', _event); 53 | } 54 | function _destroy() { 55 | $window.editor.off('blockselected', _event); 56 | $window.editor.off('blockdeselected', _event); 57 | $window.editor.off('blockremoved', _event); 58 | $window.editor.off('treeselected', _event); 59 | $window.editor.off('nodechanged', _event); 60 | } 61 | 62 | function keydown(e) { 63 | if (e.ctrlKey && e.keyCode == 90) { 64 | e.preventDefault(); 65 | } 66 | 67 | return false; 68 | } 69 | 70 | function update() { 71 | var p = $window.editor.project.get(); 72 | var t = p.trees.getSelected(); 73 | t.blocks.update(vm.original, vm.block); 74 | } 75 | } 76 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/propertiespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
Properties
4 | 5 |
6 |

Select a single block to change its properties.

7 |

NOTE: The root node represents a tree. Therefore, changes applied to this node will persist on the tree object.

8 |
9 | 10 |
11 |
12 |
13 | 14 | 21 |
22 | 23 |
24 | 25 | 34 |
35 | 36 |
37 | 41 | 42 |
43 |
44 |
45 | 46 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/treespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('TreespanelController', TreespanelController); 7 | 8 | TreespanelController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService', 12 | 'notificationService' 13 | ]; 14 | 15 | function TreespanelController($scope, 16 | $window, 17 | dialogService, 18 | notificationService) { 19 | 20 | // HEAD // 21 | var vm = this; 22 | vm.trees = null; 23 | vm.newTree = newTree; 24 | vm.select = select; 25 | vm.remove = remove; 26 | 27 | _create(); 28 | _activate(); 29 | $scope.$on('$destroy', _destroy); 30 | 31 | // BODY // 32 | function _activate() { 33 | vm.trees = []; 34 | 35 | var p = $window.editor.project.get(); 36 | var selected = p.trees.getSelected(); 37 | p.trees.each(function(tree) { 38 | var root = tree.blocks.getRoot(); 39 | vm.trees.push({ 40 | 'id' : tree._id, 41 | 'name' : root.title || 'A behavior tree', 42 | 'active' : tree===selected, 43 | }); 44 | }); 45 | } 46 | 47 | function _event(e) { 48 | if (e.type !== 'blockchanged' || e._target.category === 'root') { 49 | if (!$scope.$$phase) { 50 | $scope.$apply(function() { _activate(); }); 51 | } else { 52 | _activate(); 53 | } 54 | } 55 | } 56 | 57 | function _create() { 58 | $window.editor.on('blockchanged', _event); 59 | $window.editor.on('treeselected', _event); 60 | $window.editor.on('treeremoved', _event); 61 | $window.editor.on('treeimported', _event); 62 | } 63 | 64 | function _destroy() { 65 | $window.editor.off('blockchanged', _event); 66 | $window.editor.off('treeselected', _event); 67 | $window.editor.off('treeremoved', _event); 68 | $window.editor.off('treeimported', _event); 69 | } 70 | 71 | function newTree() { 72 | var p = $window.editor.project.get(); 73 | p.trees.add(); 74 | } 75 | 76 | function select(id) { 77 | var p = $window.editor.project.get(); 78 | p.trees.select(id); 79 | } 80 | 81 | function remove(id) { 82 | dialogService. 83 | confirm( 84 | 'Remove tree?', 85 | 'Are you sure you want to remove this tree?\n\nNote: all blocks using this tree will be removed.' 86 | ).then(function() { 87 | var p = $window.editor.project.get(); 88 | p.trees.remove(id); 89 | notificationService.success( 90 | 'Tree removed', 91 | 'The tree has been removed from this project.' 92 | ); 93 | }); 94 | 95 | } 96 | } 97 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/components/treespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 10 | 11 |
12 |
13 | 29 |
30 |
31 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/editor.controller.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .controller('EditorController', EditorController); 4 | 5 | EditorController.$inject = []; 6 | 7 | function EditorController() { 8 | 9 | } -------------------------------------------------------------------------------- /editor/src/app/pages/editor/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/editnode.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('EditNodeController', EditNodeController); 7 | 8 | EditNodeController.$inject = [ 9 | '$scope', 10 | '$window', 11 | '$state', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService' 15 | ]; 16 | 17 | function EditNodeController($scope, 18 | $window, 19 | $state, 20 | $stateParams, 21 | dialogService, 22 | notificationService) { 23 | var vm = this; 24 | vm.action = 'New'; 25 | vm.node = null; 26 | vm.blacklist = null; 27 | vm.original = null; 28 | vm.save = save; 29 | vm.remove = remove; 30 | 31 | _active(); 32 | 33 | function _active() { 34 | var p = $window.editor.project.get(); 35 | 36 | if ($stateParams.name) { 37 | var node = p.nodes.get($stateParams.name); 38 | vm.node = node.copy(); 39 | vm.original = node; 40 | vm.action = 'Update'; 41 | } else { 42 | vm.node = new b3e.Node(); 43 | vm.node.category = 'composite'; 44 | } 45 | 46 | var blacklist = []; 47 | p.nodes.each(function(node) { 48 | if (node.name !== vm.node.name) { 49 | blacklist.push(node.name); 50 | } 51 | }); 52 | vm.blacklist = blacklist.join(','); 53 | } 54 | 55 | function save() { 56 | var p = $window.editor.project.get(); 57 | 58 | if (vm.original) { 59 | p.nodes.update(vm.original, vm.node); 60 | } else { 61 | p.nodes.add(vm.node); 62 | } 63 | 64 | $state.go('editor'); 65 | notificationService 66 | .success('Node created', 'Node has been created successfully.'); 67 | } 68 | 69 | function remove() { 70 | dialogService. 71 | confirm( 72 | 'Remove node?', 73 | 'Are you sure you want to remove this node?\n\nNote: all blocks using this node will be removed.' 74 | ).then(function() { 75 | var p = $window.editor.project.get(); 76 | p.nodes.remove(vm.original); 77 | notificationService.success( 78 | 'Node removed', 79 | 'The node has been removed from this project.' 80 | ); 81 | $state.go('editor'); 82 | }); 83 | } 84 | } 85 | 86 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/editnode.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

{{editnode.action}} node

7 |
8 | 9 |
10 |
11 | 12 | 22 | 23 |
24 | 25 |
26 | 27 | 31 |
32 | 33 |
34 | 35 | 43 |
44 |
45 | 46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 | 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 | 62 | 63 | 64 | 65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/export.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('ExportController', ExportController); 7 | 8 | ExportController.$inject = [ 9 | '$scope', 10 | '$document', 11 | '$window', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService', 15 | 'storageService' 16 | ]; 17 | 18 | function ExportController($scope, 19 | $document, 20 | $window, 21 | $stateParams, 22 | dialogService, 23 | notificationService, 24 | storageService) { 25 | var vm = this; 26 | vm.type = null; 27 | vm.format = null; 28 | vm.compact = ''; 29 | vm.pretty = ''; 30 | vm.result = null; 31 | vm.data = null; 32 | vm.hideCompact = false; 33 | vm.showCompact = showCompact; 34 | vm.showPretty = showPretty; 35 | vm.select = select; 36 | vm.save = save; 37 | 38 | _active(); 39 | 40 | function _active() { 41 | vm.type = $stateParams.type; 42 | vm.format = $stateParams.format; 43 | 44 | var e = $window.editor.export; 45 | 46 | if (vm.type === 'project' && vm.format === 'json') { 47 | _createJson(e.projectToData()); 48 | } 49 | else if (vm.type === 'tree' && vm.format === 'json') { 50 | _createJson(e.treeToData()); 51 | } 52 | else if (vm.type === 'nodes' && vm.format === 'json') { 53 | _createJson(e.nodesToData()); 54 | } 55 | } 56 | 57 | function _createJson(data) { 58 | vm.data = data; 59 | vm.compact = JSON3.stringify(data); 60 | vm.pretty = JSON3.stringify(data, null, 2); 61 | vm.result = vm.pretty; 62 | } 63 | 64 | function select(){ 65 | var range = $document[0].createRange(); 66 | range.selectNodeContents($document[0].getElementById('export-result')); 67 | var sel = $window.getSelection(); 68 | sel.removeAllRanges(); 69 | sel.addRange(range); 70 | } 71 | 72 | function save() { 73 | dialogService 74 | .saveAs(null, ['.b3', '.json']) 75 | .then(function(path) { 76 | storageService 77 | .saveAsync(path, vm.pretty) 78 | .then(function() { 79 | notificationService.success( 80 | 'File saved', 81 | 'The file has been saved successfully.' 82 | ); 83 | }); 84 | }); 85 | } 86 | 87 | function showCompact() { 88 | vm.result = vm.compact; 89 | } 90 | function showPretty() { 91 | vm.result = vm.pretty; 92 | } 93 | } 94 | 95 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/export.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Export {{export.type}} as {{export.format}}

6 |
7 |
{{export.result||'Loading...'}}
8 |
9 |
10 | 11 |
12 | 17 | 23 | 29 | 34 | 38 |
39 |
40 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/import.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('ImportController', ImportController); 7 | 8 | ImportController.$inject = [ 9 | '$scope', 10 | '$window', 11 | '$state', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService', 15 | 'storageService' 16 | ]; 17 | 18 | function ImportController($scope, 19 | $window, 20 | $state, 21 | $stateParams, 22 | dialogService, 23 | notificationService, 24 | storageService) { 25 | var vm = this; 26 | vm.type = null; 27 | vm.format = null; 28 | vm.open = open; 29 | vm.loadFromFile = loadFromFile; 30 | vm.data = ''; 31 | 32 | _active(); 33 | 34 | function _active() { 35 | vm.type = $stateParams.type; 36 | vm.format = $stateParams.format; 37 | } 38 | 39 | function loadFromFile() { 40 | dialogService 41 | .openFile(false, ['.b3', '.json']) 42 | .then(function(path) { 43 | storageService 44 | .loadAsync(path) 45 | .then(function(data) { 46 | vm.data = JSON3.stringify(data, null, 2); 47 | }); 48 | }); 49 | } 50 | function open() { 51 | var i = $window.editor.import; 52 | 53 | var data = JSON3.parse(vm.data); 54 | 55 | try { 56 | if (vm.type === 'project' && vm.format === 'json') { 57 | i.projectAsData(data); 58 | } 59 | else if (vm.type === 'tree' && vm.format === 'json') { 60 | i.treeAsData(data); 61 | } 62 | else if (vm.type === 'nodes' && vm.format === 'json') { 63 | i.nodesAsData(data); 64 | } 65 | } catch(e) { 66 | notificationService.error( 67 | 'Invalid data', 68 | 'The provided data is invalid.' 69 | ); 70 | } 71 | 72 | $state.go('editor'); 73 | } 74 | } 75 | 76 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/import.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |

Import {{import.type}} from {{import.format}}

7 |
8 | 13 |
14 |
15 | 16 |
17 | 22 | 26 | 30 |
31 | 32 |
33 |
-------------------------------------------------------------------------------- /editor/src/app/pages/editor/modals/modal.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Node

6 |
7 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo, animi officia minus iusto neque vitae esse voluptas qui. Laborum culpa quasi nisi fuga rerum, cupiditate quibusdam eos consequuntur! Libero, expedita.

8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 |
-------------------------------------------------------------------------------- /editor/src/app/pages/home/home.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('HomeController', HomeController); 7 | 8 | HomeController.$inject = [ 9 | ]; 10 | 11 | function HomeController() { 12 | var vm = this; 13 | 14 | _active(); 15 | 16 | function _active() { 17 | // var e = document.getElementById('prld'); 18 | // if (e) e.remove(); 19 | } 20 | } 21 | 22 | })(); -------------------------------------------------------------------------------- /editor/src/app/pages/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |

Welcome to Behavior3 Editor

3 | 4 |
5 | 6 |

Behavior3 Editor is an open source visual tool to create and design Behavior Trees. It provides a general solution to model agents for games and other applications, such as simulations and robotics. Behavior3 Editor uses an open - and simple - format to describe the behavior trees, thus you can adapt easily to your own library, tool or framework.

7 | 8 |
9 | 10 |
11 |
12 | 18 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
SOFTWARE INFO
31 |
    32 |
  • 33 |
    34 |
    Software version:
    35 |
    [BUILD_VERSION]
    36 |
    37 |
  • 38 |
  • 39 |
    40 |
    Build date:
    41 |
    [BUILD_DATE]
    42 |
    43 |
  • 44 |
45 |
46 | 47 |
48 |
LINKS
49 | 75 |
76 | 77 | 105 | 106 |
107 |
108 | -------------------------------------------------------------------------------- /editor/src/app/pages/projects/projects.html: -------------------------------------------------------------------------------- 1 |
2 |

Projects

3 | 4 | 12 | 13 |
14 | 15 | 16 |
17 |

You don't have any project yet.

18 | 19 |
20 | 21 | 22 | 23 | 24 | 35 | 42 | 43 |
25 |
26 | 27 | 28 | 29 | 30 |
31 | 32 | Current project 33 |

{{item.name}}

34 |
36 |
37 | 38 | 39 |
40 |

{{item.name}}

41 |
44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /editor/src/app/pages/settings/settings.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('SettingsController', SettingsController); 7 | 8 | SettingsController.$inject = [ 9 | 'notificationService', 10 | 'settingsModel', 11 | 'dialogService', 12 | ]; 13 | 14 | function SettingsController(notificationService, 15 | settingsModel, 16 | dialogService) { 17 | 18 | // HEADER // 19 | var vm = this; 20 | vm.settings = {}; 21 | vm.saveSettings = saveSettings; 22 | vm.resetSettings = resetSettings; 23 | 24 | _activate(); 25 | 26 | // BODY // 27 | function _activate() { 28 | settingsModel 29 | .getSettings() 30 | .then(function(settings) { 31 | vm.settings = settings; 32 | }); 33 | } 34 | 35 | function saveSettings() { 36 | settingsModel 37 | .saveSettings(vm.settings) 38 | .then(function() { 39 | notificationService.success( 40 | 'Settings saved', 41 | 'The editor settings has been updated.' 42 | ); 43 | }); 44 | } 45 | 46 | function resetSettings() { 47 | dialogService.confirm( 48 | 'Reset Settings?', 49 | 'Are you sure you want to reset to the default settings?' 50 | ).then(function() { 51 | settingsModel 52 | .resetSettings() 53 | .then(function() { 54 | notificationService.success( 55 | 'Settings reseted', 56 | 'The editor settings has been updated to default values.' 57 | ); 58 | _activate(); 59 | }); 60 | }); 61 | } 62 | } 63 | })(); -------------------------------------------------------------------------------- /editor/src/app/services/dialog.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('dialogService', dialogService); 4 | 5 | dialogService.$inject = ['$window', '$q', '$document', 'nodejsService']; 6 | 7 | function dialogService($window, $q, $document, nodejsService) { 8 | var service = { 9 | alert : alert, 10 | confirm : confirm, 11 | prompt : prompt, 12 | saveAs : saveAs, 13 | openFile : openFile, 14 | openDirectory : openDirectory 15 | }; 16 | return service; 17 | 18 | function _callFileDialog(dialog) { 19 | return $q(function(resolve) { 20 | dialog.addEventListener('change', function() { 21 | resolve(dialog.value); 22 | }); 23 | dialog.click(); 24 | }); 25 | } 26 | 27 | function alert(title, text, type, options) { 28 | options = options || {}; 29 | options.title = title; 30 | options.text = text; 31 | options.type = type; 32 | options.customClass = type; 33 | 34 | return $q(function(resolve) { swal(options, function() { resolve(); }); }); 35 | } 36 | function confirm(title, text, type, options) { 37 | options = options || {}; 38 | options.title = title; 39 | options.text = text; 40 | options.type = type; 41 | options.customClass = type; 42 | options.showCancelButton = true; 43 | 44 | return $q(function(resolve, reject) { 45 | $window.swal(options, function(ok) { 46 | if (ok) { 47 | resolve(); 48 | } else { 49 | reject(); 50 | } 51 | }); 52 | }); 53 | } 54 | function prompt(title, text, type, placeholder, options) { 55 | options = options || {}; 56 | options.title = title; 57 | options.text = text; 58 | options.type = type || 'input'; 59 | options.inputPlaceholder = placeholder; 60 | options.customClass = type; 61 | options.showCancelButton = true; 62 | 63 | return $q(function(resolve, reject) { 64 | swal(options, function(val) { 65 | if (val!==false) { 66 | resolve(val); 67 | } else { 68 | reject(val); 69 | } 70 | }); 71 | }); 72 | } 73 | function saveAs(placeholder, types) { 74 | return $q(function(resolve, reject) { 75 | var value = nodejsService.dialog.showSaveDialog({ 76 | title: 'Save project as...', 77 | defaultPath: placeholder + '.b3', 78 | filters : [ 79 | {name: 'Behavior3 File', extensions: ['b3', 'json']}, 80 | {name: 'All Files', extensions: ['*']} 81 | ] 82 | }); 83 | if (value) { 84 | resolve(value); 85 | } else { 86 | reject(); 87 | } 88 | }); 89 | } 90 | function openFile(multiple, types) { 91 | return $q(function(resolve, reject) { 92 | var value = nodejsService.dialog.showOpenDialog({ 93 | title: 'Open file...', 94 | multiSelections: multiple, 95 | properties: ['openFile'], 96 | filters : [ 97 | {name: 'Behavior3 File', extensions: ['b3', 'json']}, 98 | {name: 'All Files', extensions: ['*']} 99 | ] 100 | }); 101 | 102 | if (value) { 103 | if (!multiple) { 104 | value = value[0]; 105 | } 106 | console.log(value); 107 | resolve(value); 108 | } else { 109 | reject(); 110 | } 111 | }); 112 | } 113 | function openDirectory() { 114 | return $q(function(resolve, reject) { 115 | var value = nodejsService.dialog.showOpenDialog({ 116 | title: 'Open directory...', 117 | properties: ['openDirectory'] 118 | }); 119 | if (value) { 120 | resolve(value); 121 | } else { 122 | reject(); 123 | } 124 | }); 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /editor/src/app/services/editor.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('editorService', editorService); 4 | 5 | editorService.$inject = ['$window']; 6 | 7 | function editorService($window) { 8 | var service = { 9 | getDefaultSettings : getDefaultSettings, 10 | applySettings : applySettings, 11 | newProject : newProject, 12 | openProject : openProject, 13 | closeProject : closeProject, 14 | exportProject : exportProject, 15 | }; 16 | return service; 17 | 18 | function getDefaultSettings() { 19 | return $window.b3e.DEFAULT_SETTINGS; 20 | } 21 | function applySettings(settings) { 22 | $window.editor.applySettings(settings); 23 | } 24 | 25 | function newProject() { 26 | $window.editor.project.create(); 27 | } 28 | function openProject(data) { 29 | $window.editor.project.open(data); 30 | } 31 | function closeProject() { 32 | $window.editor.project.close(); 33 | } 34 | 35 | function exportProject() { 36 | return $window.editor.export.projectToData(); 37 | } 38 | } -------------------------------------------------------------------------------- /editor/src/app/services/nodejs.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('nodejsService', nodejsService); 4 | 5 | nodejsService.$inject = ['$window']; 6 | 7 | function nodejsService($window) { 8 | var ok = !!$window.require; 9 | var remote = (ok?$window.require('remote'):null); 10 | var service = { 11 | ok : ok, 12 | fs : (ok?$window.require('fs'):null), 13 | path : (ok?$window.require('path'):null), 14 | dialog : (ok?remote.require('dialog'):null), 15 | }; 16 | return service; 17 | 18 | } -------------------------------------------------------------------------------- /editor/src/app/services/notification.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('notificationService', notificationService); 4 | 5 | notificationService.$inject = ['$window', '$timeout', '$compile', '$rootScope', '$sce']; 6 | 7 | function notificationService($window, $timeout, $compile, $rootScope, $sce) { 8 | var elementBuffer = []; 9 | var service = { 10 | notify : notify, 11 | simple : simple, 12 | success : success, 13 | error : error, 14 | info : info, 15 | warning : warning, 16 | }; 17 | return service; 18 | 19 | function _reposite() { 20 | var verticalSpacing = 10; 21 | var lastBottom = 20; 22 | 23 | for(var i=elementBuffer.length-1; i>=0; i--) { 24 | var element = elementBuffer[i]; 25 | var height = parseInt(element[0].offsetHeight); 26 | 27 | var bottom = lastBottom; 28 | lastBottom = bottom+height+verticalSpacing; 29 | 30 | element.css('bottom', bottom+'px'); 31 | } 32 | } 33 | 34 | function _note(config) { 35 | var TEMPLATE = ''+ 36 | '
'+ 37 | '
'+ 38 | '
' + 39 | '
'+ 40 | '
'+ 41 | '
' + 42 | '
'; 43 | 44 | var DEFAULT = { 45 | type : 'default', 46 | title : '', 47 | message : '', 48 | icon : false, 49 | delay : 3000, 50 | }; 51 | 52 | // Default parameters 53 | config = tine.merge({}, DEFAULT, config); 54 | 55 | // Set scope variables to fill the template 56 | var scope = $rootScope.$new(); 57 | scope.type = config.type; 58 | scope.title = $sce.trustAsHtml(config.title); 59 | scope.message = $sce.trustAsHtml(config.message); 60 | scope.icon = config.icon; 61 | scope.delay = config.delay; 62 | 63 | // Create the DOM element and add events 64 | var element = $compile(TEMPLATE)(scope); 65 | element.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd click', function(e) { 66 | e = e.originalEvent || e; 67 | if (e.type==='click' || element.hasClass('killed')) { 68 | element.remove(); 69 | elementBuffer.remove(element); 70 | _reposite(); 71 | } 72 | }); 73 | 74 | if (angular.isNumber(config.delay)) { 75 | $timeout(function() { 76 | element.addClass('killed'); 77 | }, config.delay); 78 | } 79 | 80 | $timeout(function() { 81 | element.addClass('started'); 82 | _reposite(); 83 | }, 0); 84 | 85 | elementBuffer.push(element); 86 | angular.element(document.getElementsByTagName('body')).append(element); 87 | } 88 | 89 | function notify(config) { 90 | _note(config); 91 | } 92 | function simple(title, message) { 93 | _note({title:title, message:message, type:'default'}); 94 | } 95 | function success(title, message) { 96 | _note({title:title, message:message, icon:'fa-check', type:'success'}); 97 | } 98 | function error(title, message) { 99 | _note({title:title, message:message, icon:'fa-close', type:'error'}); 100 | } 101 | function info(title, message) { 102 | _note({title:title, message:message, icon:'fa-info', type:'info'}); 103 | } 104 | function warning(title, message) { 105 | _note({title:title, message:message, icon:'fa-warning', type:'warning'}); 106 | } 107 | } -------------------------------------------------------------------------------- /editor/src/app/services/storage/filestorage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('fileStorageService', fileStorageService); 4 | 5 | fileStorageService.$inject = ['nodejsService']; 6 | 7 | function fileStorageService(nodejsService) { 8 | var ok = nodejsService.ok; 9 | var fs = nodejsService.fs; 10 | 11 | var service = { 12 | ok : ok, 13 | save : save, 14 | load : load, 15 | remove : remove 16 | }; 17 | return service; 18 | 19 | function save(path, data) { 20 | if (typeof data !== 'string') { 21 | try { data = JSON3.stringify(data); } catch (e) {} 22 | } 23 | 24 | var file = fs.openSync(path+'~', 'w'); 25 | fs.writeSync(file, data); 26 | fs.closeSync(file); 27 | 28 | // Rename must be async to override correctly. 29 | fs.rename(path+'~', path); 30 | } 31 | function load(path) { 32 | var data = fs.readFileSync(path, 'utf-8'); 33 | try { data = JSON3.parse(data); } catch (e) {} 34 | return data; 35 | } 36 | function remove(path) { 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /editor/src/app/services/storage/localstorage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('localStorageService', localStorageService); 4 | 5 | localStorageService.$inject = ['$window']; 6 | 7 | function localStorageService($window) { 8 | var service = { 9 | ok : !!$window.localStorage, 10 | save : save, 11 | load : load, 12 | remove : remove 13 | }; 14 | return service; 15 | 16 | function save(path, data) { 17 | try { data = JSON.stringify(data); } catch (e) {} 18 | $window.localStorage[path] = data; 19 | } 20 | function load(path) { 21 | var data = $window.localStorage[path]; 22 | try { data = JSON.parse(data); } catch (e) {} 23 | return data; 24 | } 25 | function remove(path) { 26 | delete $window.localStorage[path]; 27 | } 28 | } -------------------------------------------------------------------------------- /editor/src/app/services/storage/storage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('storageService', storageService); 4 | 5 | storageService.$inject = ['$q', 'localStorageService', 'fileStorageService']; 6 | 7 | function storageService($q, localStorageService, fileStorageService) { 8 | var storage = (fileStorageService.ok?fileStorageService:localStorageService); 9 | 10 | var service = { 11 | save : save, 12 | saveAsync : saveAsync, 13 | load : load, 14 | loadAsync : loadAsync, 15 | remove : remove, 16 | removeAsync : removeAsync, 17 | }; 18 | return service; 19 | 20 | function save(path, data) { 21 | storage.save(path, data); 22 | } 23 | function saveAsync(path, data) { 24 | return $q(function(resolve, reject) { 25 | try { 26 | storage.save(path, data); 27 | resolve(); 28 | } catch (e) { 29 | reject(e); 30 | } 31 | }); 32 | } 33 | function load(path) { 34 | return storage.load(path); 35 | } 36 | function loadAsync(path) { 37 | return $q(function(resolve, reject) { 38 | try { 39 | var data = storage.load(path); 40 | resolve(data); 41 | } catch (e) { 42 | reject(e); 43 | } 44 | }); 45 | } 46 | function remove(path) { 47 | storage.remove(path); 48 | } 49 | function removeAsync(path) { 50 | return $q(function(resolve, reject) { 51 | try { 52 | storage.remove(path); 53 | resolve(); 54 | } catch (e) { 55 | reject(e); 56 | } 57 | }); 58 | } 59 | } -------------------------------------------------------------------------------- /editor/src/app/services/system.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('systemService', systemService); 4 | 5 | systemService.$inject = ['$window', 'nodejsService']; 6 | 7 | function systemService($window, nodejsService) { 8 | var isDesktop = !!$window.process; 9 | var service = { 10 | isDesktop : isDesktop, 11 | getDataPath : getDataPath, 12 | join : join, 13 | }; 14 | return service; 15 | 16 | function _createIfNonExist(path) { 17 | try { 18 | var s = nodejsService.fs.statSync(path); 19 | } catch (e) { 20 | nodejsService.fs.mkdirSync(path); 21 | } 22 | } 23 | function getDataPath() { 24 | if (isDesktop) { 25 | var datapath = process.env.APPDATA; 26 | if (!datapath) { 27 | datapath = process.env.HOME + '/.behavior3'; 28 | } 29 | 30 | var path = join(datapath, 'b3editor'); 31 | _createIfNonExist(datapath); 32 | _createIfNonExist(path); 33 | return path; 34 | } else { 35 | return 'b3editor'; 36 | } 37 | } 38 | function join() { 39 | if (isDesktop) { 40 | return nodejsService.path.join.apply(nodejsService.path, arguments); 41 | } else { 42 | var s = arguments[0]; 43 | for (var i=1; i model validation 20 | ctrl.$parsers.unshift(function(value) { 21 | var valid = blacklist.indexOf(value) === -1; 22 | ctrl.$setValidity('blacklist', valid); 23 | return valid ? value : undefined; 24 | }); 25 | 26 | //For model -> DOM validation 27 | ctrl.$formatters.unshift(function(value) { 28 | ctrl.$setValidity('blacklist', blacklist.indexOf(value) === -1); 29 | return value; 30 | }); 31 | } 32 | } 33 | 34 | })(); -------------------------------------------------------------------------------- /editor/src/assets/css/preload.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | padding: 0px; 3 | margin: 0px; 4 | overflow: hidden; 5 | line-height: 1.42857143; 6 | } 7 | 8 | .preloading { 9 | position : absolute; 10 | top : 0; 11 | left : 0; 12 | width : 100%; 13 | height : 100%; 14 | color : #999999; 15 | background : #171717; 16 | z-index : 10000000; 17 | text-align : center; 18 | font-size : 16px !important; 19 | font-family : "Raleway", sans-serif !important; 20 | font-weight: 300 !important; 21 | 22 | -webkit-transition : opacity .5s ease-in-out; 23 | -moz-transition : opacity .5s ease-in-out; 24 | -o-transition : opacity .5s ease-in-out; 25 | transition : opacity .5s ease-in-out; 26 | } 27 | .preloading.preload-fade { 28 | opacity: 0; 29 | } 30 | 31 | .preloading-body { 32 | width : 50vw; 33 | margin : 45vh auto; 34 | } 35 | 36 | .preloading-body-title { 37 | font-size : 2.0em; 38 | margin : 0px; 39 | display : block; 40 | } 41 | 42 | .preloading-body-subtitle { 43 | font-size : 0.9em; 44 | opacity : 0.8; 45 | display : block; 46 | } 47 | -------------------------------------------------------------------------------- /editor/src/assets/imgs/closedhand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/editor/src/assets/imgs/closedhand.cur -------------------------------------------------------------------------------- /editor/src/assets/imgs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/editor/src/assets/imgs/favicon.ico -------------------------------------------------------------------------------- /editor/src/assets/js/preload.js: -------------------------------------------------------------------------------- 1 | function preloadProgress(message) { 2 | var element = document.getElementById('page-preload-progress'); 3 | element.innerHTML = message; 4 | } -------------------------------------------------------------------------------- /editor/src/assets/less/animations.less: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | // NGVIEW 4 | [ui-view].ng-enter, [ui-view].ng-leave { 5 | position:absolute; 6 | height:100%; 7 | width:100%; 8 | .animation-fast; 9 | } 10 | 11 | // APP 12 | [ui-view].app-anim.ng-enter { opacity: 0; } 13 | [ui-view].app-anim.ng-enter-active { opacity: 1; } 14 | [ui-view].app-anim.ng-leave { opacity: 1; } 15 | [ui-view].app-anim.ng-leave-active { opacity: 0; } 16 | 17 | [ui-view].app-anim.ng-enter .sidebar.right, 18 | [ui-view].app-anim.ng-leave .sidebar.right { .animation-fast; } 19 | [ui-view].app-anim.ng-enter .sidebar.right { right: -@size-sidebar; } 20 | [ui-view].app-anim.ng-enter-active .sidebar.right { right: 0; } 21 | [ui-view].app-anim.ng-leave .sidebar.right { right: 0; } 22 | [ui-view].app-anim.ng-leave-active .sidebar.right { right: -@size-sidebar; } 23 | 24 | // DASH 25 | [ui-view].dash-anim.ng-enter { opacity: 0; } 26 | [ui-view].dash-anim.ng-enter-active { opacity: 1; } 27 | [ui-view].dash-anim.ng-leave { opacity: 1; } 28 | [ui-view].dash-anim.ng-leave-active { opacity: 0; } 29 | 30 | // EDITOR 31 | [ui-view].editor-anim.ng-enter .b3modal, 32 | [ui-view].editor-anim.ng-leave .b3modal { .animation-fast; } 33 | [ui-view].editor-anim.ng-enter { opacity: 0; z-index: @z-modal; } 34 | [ui-view].editor-anim.ng-enter-active { opacity: 1; } 35 | [ui-view].editor-anim.ng-leave { opacity: 1; } 36 | [ui-view].editor-anim.ng-leave-active { opacity: 0; } 37 | 38 | [ui-view].editor-anim.ng-enter .b3modal-window, 39 | [ui-view].editor-anim.ng-leave .b3modal-window { 40 | .animation-fast; 41 | transform-origin: left top; 42 | } 43 | // [ui-view].editor-anim.ng-enter .b3modal-window { transform: scale(0.1, 1); } 44 | // [ui-view].editor-anim.ng-enter-active .b3modal-window { transform: scale(1, 1); } 45 | // [ui-view].editor-anim.ng-leave .b3modal-window { transform: scale(1, 1); } 46 | // [ui-view].editor-anim.ng-leave-active .b3modal-window { transform: scale(0.1, 1); } 47 | 48 | 49 | 50 | 51 | // // HIDE 52 | // .anim { .animation } 53 | // .anim.ng-hide { opacity:0; } 54 | 55 | // // INVISIBLE 56 | // .invisible-add, .invisible-remove { .animation-fast } 57 | 58 | // .invisible, .invisible-add.invisible-add-active { opacity: 0; } 59 | // .invisible-remove.css-class-remove-active { opacity: 1; } -------------------------------------------------------------------------------- /editor/src/assets/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .nav, .pagination, .carousel, .panel-title a { cursor: pointer; } 4 | 5 | .panel { 6 | .title { margin-top: 0px; } 7 | .panel-heading { 8 | h1, h2, h3, h4 { margin: 0px; } 9 | } 10 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_app.less: -------------------------------------------------------------------------------- 1 | /** 2 | * APP GLOBALS 3 | */ 4 | 5 | html, body { 6 | padding: 0px; 7 | margin: 0px; 8 | overflow: hidden; 9 | font-size: 16px; 10 | color: @color-light1; 11 | background: @color-dark1; 12 | 13 | .font-base; 14 | } 15 | 16 | .grabbing { 17 | cursor: url(../imgs/closedhand.cur); 18 | cursor: url(../imgs/closedhand.cur) 8 8, move; 19 | } 20 | .no-margin { margin: 0px; } 21 | .no-padding { padding: 0px; } 22 | .sweet-alert input { color: @color-dark1; } 23 | .full-height { height: 100%; } 24 | .no-select{ 25 | -webkit-touch-callout: none; 26 | -webkit-user-select: none; 27 | -khtml-user-select: none; 28 | -moz-user-select: none; 29 | -ms-user-select: none; 30 | user-select: none; 31 | } 32 | 33 | a, .btn, .btn-success, .btn-default, .btn-warning, .btn-info, .btn-danger { 34 | .no-select; 35 | } 36 | 37 | .form-control[disabled], 38 | .form-control[readonly], 39 | fieldset[disabled] .form-control { 40 | cursor: inherit; 41 | } 42 | 43 | // LINKS ====================================================================== 44 | a { 45 | color: @color-blue-dark; 46 | transition: color 100ms ease-in-out; 47 | outline: none; 48 | 49 | &:hover, &:visited, &:link, &:active, &:focus { 50 | text-decoration: none; 51 | outline: none; 52 | } 53 | &:hover { 54 | cursor: pointer; 55 | color: @color-blue; 56 | } 57 | } 58 | 59 | // SCROLLBAR ================================================================== 60 | ::-webkit-scrollbar { 61 | width: 10px; 62 | background: none; 63 | } 64 | ::-webkit-scrollbar-track { 65 | background: none; 66 | } 67 | ::-webkit-scrollbar-thumb { 68 | background: @color-blue-light; 69 | } 70 | ::-webkit-scrollbar-thumb:window-inactive { 71 | background: @color-blue-light; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /editor/src/assets/less/c_dash.less: -------------------------------------------------------------------------------- 1 | .dash-menu { 2 | list-style: none; 3 | text-align: right; 4 | margin-right: @padding-large; 5 | font-size: 2em; 6 | .font-header; 7 | 8 | li { 9 | margin-bottom: 10px; 10 | 11 | a { 12 | color: @color-light2; 13 | &.active { color: @color-light1; } 14 | &:hover { color: @color-light1; } 15 | } 16 | } 17 | } 18 | 19 | .dash-page { 20 | overflow: auto; 21 | position: absolute; 22 | top: 0px; 23 | left: 0px; 24 | width: 100%; 25 | height: 100%; 26 | z-index: @z-page; 27 | background-color: @color-light1; 28 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_keytable.less: -------------------------------------------------------------------------------- 1 | .keytable { 2 | 3 | input[type=button] { 4 | width: 40px; 5 | } 6 | 7 | th { 8 | margin: 0px; 9 | padding: 0px 2px !important; 10 | 11 | label { 12 | padding-bottom: 0px; 13 | } 14 | } 15 | 16 | td { 17 | border: 0px !important; 18 | padding: 2px 2px !important; 19 | 20 | input:not([type=button]) { 21 | padding: 0px @padding-small; 22 | margin: 0px; 23 | } 24 | } 25 | 26 | .form-control { 27 | height: 25px; 28 | } 29 | 30 | &.no-border { 31 | th { 32 | border: 0px; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /editor/src/assets/less/c_menubar.less: -------------------------------------------------------------------------------- 1 | .menubar { 2 | position: absolute; 3 | top: 0px; 4 | width: 100%; 5 | height: @size-menubar; 6 | z-index: @z-menubar; 7 | color: @color-light1; 8 | font-size: 14px; 9 | background-color: @color-dark4; 10 | border-bottom: 1px solid @color-dark2; 11 | 12 | .box-shadow(0px 0px 16px 5px); 13 | 14 | &-left, &-right { 15 | display: inline; 16 | } 17 | 18 | &-right { 19 | float: right; 20 | } 21 | } 22 | 23 | .menubar ul { 24 | padding: 0px; 25 | margin: 0px; 26 | list-style: none; 27 | // position: relative; 28 | display: inline-table; 29 | } 30 | .menubar ul:after { 31 | content: ""; 32 | clear: both; 33 | display: block; 34 | } 35 | .menubar ul li { 36 | float: left; 37 | } 38 | .menubar ul li:hover { 39 | background-color: @color-dark1; 40 | } 41 | .menubar ul li a { 42 | color: #FFFFFF; 43 | display: block; 44 | height: 35px; 45 | padding: 0px 10px; 46 | line-height: 35px; 47 | text-decoration: none; 48 | .no-select; 49 | 50 | .shortcut { 51 | color: #999; 52 | float: right; 53 | font-style: italic; 54 | } 55 | } 56 | .menubar .side { 57 | // width: @size-sidebar; 58 | text-align: left; 59 | display: inline-block; 60 | 61 | a { 62 | display: inline-block; 63 | 64 | &.fastlink { 65 | color: @color-light1; 66 | padding: 0px 6px 0px 7px !important; 67 | border-right: 1px solid @color-dark2; 68 | } 69 | } 70 | 71 | .logo { 72 | color: #4BB2FD; 73 | background-color: #333333; 74 | // border-left: 1px solid @color-dark2; 75 | // border-right: 1px solid @color-dark2; 76 | font-weight: bold; 77 | 78 | .animation-slow; 79 | 80 | &:hover { 81 | color: @color-dark2; 82 | background-color: @color-light2; 83 | } 84 | } 85 | } 86 | 87 | .menubar li.disabled { 88 | a { 89 | color: @color-light3; 90 | cursor: default; 91 | } 92 | &:hover { 93 | background-color: inherit; 94 | } 95 | } 96 | 97 | .menubar ul li:hover > ul { 98 | display: block; 99 | } 100 | .menubar ul ul { 101 | display: none; 102 | background: #454545; 103 | position: absolute; 104 | top: 100%; 105 | min-width: 235px; 106 | border: 1px solid #333333; 107 | } 108 | .menubar .menubar-right ul ul { 109 | right: 0; 110 | } 111 | .menubar ul ul li { 112 | float: none; 113 | position: relative; 114 | } 115 | .menubar ul ul li a { 116 | height: 35px; 117 | padding: 0px 10px; 118 | line-height: 35px; 119 | color: #fff; 120 | text-align: left; 121 | } 122 | .menubar ul ul ul { 123 | position: absolute; 124 | top: 0; 125 | } 126 | .menubar .menubar-left ul ul ul { left: 100%; } 127 | .menubar .menubar-right ul ul ul { right: 100%; } 128 | .menubar .divider { border-top: 1px solid #555; } 129 | 130 | .arrow-right { 131 | width: 0; 132 | height: 0; 133 | margin-top: 12px; 134 | border-top: 5px solid transparent; 135 | border-bottom: 5px solid transparent; 136 | border-left: 5px solid #FFF; 137 | } 138 | 139 | 140 | [ui-view].ng-enter .menubar, 141 | [ui-view].ng-leave .menubar { .animation-fast; } 142 | [ui-view].ng-enter .menubar { top: -@size-menubar; } 143 | [ui-view].ng-enter-active .menubar { top: 0; } 144 | [ui-view].ng-leave .menubar { top: 0; } 145 | [ui-view].ng-leave-active .menubar { top: -@size-menubar; } -------------------------------------------------------------------------------- /editor/src/assets/less/c_modal.less: -------------------------------------------------------------------------------- 1 | .b3modal-top { 2 | position: fixed; 3 | top: 0px; 4 | left: 0px; 5 | right: 0px; 6 | bottom: 0px; 7 | } 8 | 9 | .b3modal { 10 | z-index: @z-modal; 11 | 12 | &-background { 13 | .b3modal-top; 14 | z-index: @z-modal; 15 | background: @color-dark1; 16 | opacity: 0.9; 17 | } 18 | 19 | &-window { 20 | .b3modal-top; 21 | top: @size-menubar; 22 | left: @size-sidebar; 23 | right: @size-sidebar; 24 | bottom: @size-menubar; 25 | z-index: @z-modal+1; 26 | 27 | background-color: @color-light1; 28 | color: @color-dark1; 29 | overflow: hidden; 30 | padding-bottom: 75px; 31 | 32 | .box-shadow(0px 0px 16px 0px); 33 | } 34 | 35 | &-wrap { 36 | overflow-y: auto; 37 | height: 100%; 38 | } 39 | 40 | &-title { 41 | color: @color-light3; 42 | margin: 0px; 43 | padding: @padding-large; 44 | padding-bottom: 0px; 45 | font-size: 2.5em; 46 | .font-header; 47 | } 48 | 49 | &-content { 50 | padding: @padding-large; 51 | } 52 | 53 | &-buttons { 54 | position: absolute; 55 | bottom: 0px; 56 | height: 75px; 57 | width: 100%; 58 | text-align: right; 59 | padding: @padding-medium @padding-large; 60 | background-color: @color-light2; 61 | } 62 | } 63 | 64 | .serialize-result { 65 | overflow: auto; 66 | border: 1px solid @color-light3; 67 | height: 100%; 68 | padding: @padding-medium; 69 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_nodes.less: -------------------------------------------------------------------------------- 1 | .node-list { 2 | .empty { 3 | padding: 0px @padding-small; 4 | color: @color-light2; 5 | } 6 | 7 | &-content { 8 | padding: 0px @padding-small; 9 | } 10 | 11 | &-title { 12 | cursor: pointer; 13 | display: block; 14 | margin-top: 10px; 15 | padding: 2px 5px; 16 | // text-transform: uppercase; 17 | text-transform: capitalize; 18 | color: @color-light3; 19 | // border-bottom: 1px solid @color-dark4; 20 | .font-base; 21 | } 22 | 23 | &-category { 24 | padding: 0px 0px @padding-small-2x 0px; 25 | 26 | ul { 27 | list-style: none; 28 | margin: 0px; 29 | padding: 0px; 30 | 31 | li { 32 | .edit { 33 | float: right; 34 | color: @color-light1; 35 | &:hover { 36 | text-decoration: none; 37 | background-color: @color-yellow; 38 | } 39 | } 40 | .remove { 41 | float: right; 42 | color: @color-light1; 43 | &:hover { 44 | text-decoration: none; 45 | background-color: @color-red; 46 | } 47 | } 48 | a { 49 | padding: 2px @padding-small; 50 | display: block; 51 | color: @color-light1; 52 | &:hover { 53 | background-color: @color-dark2; 54 | } 55 | &.active { 56 | color: @color-blue; 57 | // font-weight: 600; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /editor/src/assets/less/c_notification.less: -------------------------------------------------------------------------------- 1 | @icon-size : 35px; 2 | 3 | .notification { 4 | position: fixed; 5 | bottom: 20px; 6 | right: -@size-sidebar+-@icon-size; 7 | z-index: @z-notification; 8 | opacity: 0; 9 | 10 | width: @size-sidebar+@icon-size; 11 | color: @color-dark1; 12 | transition: all 0.5s ease; 13 | 14 | &:hover { 15 | opacity: 0.7; 16 | } 17 | &.started { 18 | right: 0px; 19 | opacity: 1; 20 | } 21 | &.killed { 22 | opacity: 0; 23 | right: -@size-sidebar+-@icon-size; 24 | } 25 | 26 | &.default { 27 | background-color: @color-light4; 28 | border: 1px solid @color-light4; 29 | border-right: 0px; 30 | .notification-content { 31 | background-color: @color-light2; 32 | } 33 | } 34 | &.error { 35 | background-color: @color-red-dark; 36 | border: 1px solid @color-red-dark; 37 | border-right: 0px; 38 | .notification-content { 39 | background-color: @color-red-light; 40 | } 41 | } 42 | &.success { 43 | background-color: @color-green-dark; 44 | border: 1px solid @color-green-dark; 45 | border-right: 0px; 46 | .notification-content { 47 | background-color: @color-green-light; 48 | } 49 | } 50 | &.warning { 51 | background-color: @color-yellow-dark; 52 | border: 1px solid @color-yellow-dark; 53 | border-right: 0px; 54 | .notification-content { 55 | background-color: @color-yellow-light; 56 | } 57 | } 58 | &.info { 59 | background-color: @color-blue-dark; 60 | border: 1px solid @color-blue-dark; 61 | border-right: 0px; 62 | .notification-content { 63 | background-color: @color-blue-light; 64 | } 65 | } 66 | 67 | 68 | 69 | &-icon { 70 | color: @color-light1; 71 | position: absolute; 72 | top: 50%; 73 | left: 18px; 74 | margin-top: -15px; 75 | font-size: 1.3em; 76 | } 77 | 78 | &-content { 79 | padding: @padding-small @padding-large @padding-small @padding-large; 80 | min-height: 30px; 81 | 82 | &.has-icon { 83 | margin-left: @padding-large+@icon-size; 84 | } 85 | } 86 | 87 | &-title { 88 | font-size: 1em; 89 | font-weight: bold; 90 | margin-bottom: 5px; 91 | } 92 | 93 | &-message { 94 | font-size: 0.85em; 95 | } 96 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_page.less: -------------------------------------------------------------------------------- 1 | 2 | .page { 3 | color: @color-dark1; 4 | overflow-y: auto; 5 | padding-bottom: 100px; 6 | min-width: 850px; 7 | 8 | h1.header { 9 | color: @color-light3; 10 | margin: 0px; 11 | overflow-y: hidden; 12 | padding: 0px; 13 | min-width: @size-sidebar+350px; 14 | padding-left: @padding-large+@size-sidebar; 15 | height: @size-header; 16 | line-height: @size-header*1.1; 17 | font-size: 2.5em; 18 | .font-header; 19 | } 20 | 21 | .content { 22 | max-width: @size-page-content; 23 | min-width: @size-sidebar+350px; 24 | width: 100%; 25 | padding: @padding-large; 26 | padding-left: @padding-large+@size-sidebar; 27 | 28 | &.no-header { padding-top: @size-header; } 29 | .current-project { margin-top: 10px; } 30 | } 31 | 32 | &-operations { 33 | background-color: @color-light1; 34 | border: 1px solid #E7E7E7; 35 | 36 | &-content { 37 | max-width: @size-page-content; 38 | min-width: @size-sidebar+350px; 39 | width: 100%; 40 | padding-right: @padding-large; 41 | padding-left: @size-sidebar; 42 | text-align: right; 43 | ul { 44 | margin: 0px; 45 | padding: 0px; 46 | list-style: none; 47 | li { 48 | display: inline; 49 | a, button { 50 | border: 0px; 51 | display: inline; 52 | // height: 22px; 53 | padding: 3.5px 13px; 54 | cursor: pointer; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_properties.less: -------------------------------------------------------------------------------- 1 | .properties { 2 | padding: @padding-small-2x; 3 | 4 | input:not([type=button]), textarea { 5 | padding: @padding-small; 6 | } 7 | 8 | label { 9 | display: block; 10 | text-transform: uppercase; 11 | color: @color-light3; 12 | .font-base; 13 | } 14 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_sidebar.less: -------------------------------------------------------------------------------- 1 | .sidebar-stub { 2 | position: absolute; 3 | top: 0px; 4 | width: @size-sidebar; 5 | height: 100%; 6 | z-index: 1; 7 | background-color: @color-dark3; 8 | } 9 | 10 | .sidebar { 11 | overflow-y: auto; 12 | position: absolute; 13 | top: 0px; 14 | width: @size-sidebar; 15 | height: 100%; 16 | z-index: @z-sidebar; 17 | color: @color-light3; 18 | background-color: @color-dark3; 19 | .box-shadow(0px 0px 16px 0px); 20 | 21 | &.left { left: 0px; } 22 | &.right { right: 0px; } 23 | &::-webkit-scrollbar { 24 | width: 3px; 25 | background: none; 26 | 27 | &-track { background: none; } 28 | &-thumb { background: @color-blue; } 29 | &-thumb:window-inactive { background: @color-blue; } 30 | } 31 | 32 | .title { 33 | color: @color-dark1; 34 | list-style: none; 35 | background-color: @color-dark2+#222; 36 | margin: @padding-medium 0px @padding-small-2x 0px; 37 | padding: 0px @padding-small-2x; 38 | text-align: left; 39 | text-transform: capitalize; 40 | font-size: 1.2em; 41 | .font-base; 42 | 43 | .new { 44 | margin-top: 3px; 45 | float: right; 46 | color: @color-light1; 47 | &:hover { 48 | text-decoration: none; 49 | background-color: @color-green; 50 | } 51 | } 52 | 53 | a { 54 | color: @color-dark1; 55 | display: block; 56 | } 57 | } 58 | 59 | .header-button { 60 | color: @color-light3; 61 | padding-left: @padding-large; 62 | height: @size-header; 63 | line-height: @size-header*1.1; 64 | font-size: 1.5em; 65 | background-color: @color-dark2; 66 | border-bottom: 1px solid @color-dark4; 67 | .font-header; 68 | 69 | a { 70 | display: block; 71 | color: @color-light2; 72 | } 73 | } 74 | 75 | .content { 76 | padding: @padding-medium 0px; 77 | 78 | &.content.no-header { padding-top: @size-header; } 79 | &.content.has-menubar { padding-top: @size-menubar; } 80 | 81 | } 82 | } 83 | 84 | .side-panel { 85 | .panel-operations { 86 | border-top: 1px solid @color-light4; 87 | border-bottom: 1px solid @color-light4; 88 | position: absolute; 89 | width:100%; 90 | bottom: 0px; 91 | background-color: @color-dark2; 92 | 93 | &-content { 94 | width: 100%; 95 | text-align: right; 96 | ul { 97 | margin: 0px; 98 | padding: 0px; 99 | list-style: none; 100 | li { 101 | display: inline; 102 | a { 103 | display: inline-block; 104 | padding: 3.5px 12px; 105 | cursor: pointer; 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_tabset.less: -------------------------------------------------------------------------------- 1 | .tabset { 2 | height: 100%; 3 | 4 | ul.tabset-header { 5 | list-style: none; 6 | margin: 0px; 7 | padding: @padding-medium 0px; 8 | text-align: center; 9 | font-size: 1.3em; 10 | .font-header; 11 | 12 | li { 13 | display: inline; 14 | margin: 0px 10px; 15 | 16 | a { 17 | color: @color-light2; 18 | &:hover { 19 | color: @color-light1; 20 | } 21 | } 22 | 23 | &.active { 24 | a { 25 | color: @color-blue; 26 | &:hover { 27 | color: @color-blue; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | .tab-panel { 36 | height: 100%; 37 | } -------------------------------------------------------------------------------- /editor/src/assets/less/c_trees.less: -------------------------------------------------------------------------------- 1 | .tree-list { 2 | overflow-y: auto; 3 | height: 80%; 4 | 5 | &-content { 6 | padding: 0px @padding-small; 7 | 8 | ul { 9 | list-style: none; 10 | margin: 0px; 11 | padding: 0px; 12 | 13 | li { 14 | .remove { 15 | float: right; 16 | color: @color-light1; 17 | &:hover { 18 | text-decoration: none; 19 | background-color: @color-red; 20 | } 21 | } 22 | a { 23 | padding: 2px @padding-small; 24 | display: block; 25 | color: @color-light1; 26 | &:hover { 27 | background-color: @color-dark2; 28 | } 29 | &.active { 30 | color: @color-blue; 31 | font-weight: 600; 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | 39 | } -------------------------------------------------------------------------------- /editor/src/assets/less/index.less: -------------------------------------------------------------------------------- 1 | @import "variables.less"; 2 | @import "bootstrap.less"; 3 | @import "animations.less"; 4 | 5 | @import "c_app.less"; 6 | @import "c_dash.less"; 7 | @import "c_keytable.less"; 8 | @import "c_menubar.less"; 9 | @import "c_modal.less"; 10 | @import "c_nodes.less"; 11 | @import "c_notification.less"; 12 | @import "c_page.less"; 13 | @import "c_properties.less"; 14 | @import "c_sidebar.less"; 15 | @import "c_tabset.less"; 16 | @import "c_trees.less"; -------------------------------------------------------------------------------- /editor/src/assets/less/variables.less: -------------------------------------------------------------------------------- 1 | // COLORS ===================================================================== 2 | @color-dark1 : #0B0B0B; 3 | @color-dark2 : #333333; 4 | @color-dark3 : #2F2F2F; 5 | @color-dark4 : #454545; 6 | @color-light1 : #FFFFFF; 7 | @color-light2 : #CECECE; 8 | @color-light3 : #999999; 9 | @color-light4 : #545454; 10 | @color-blue : #5bc0de; 11 | @color-red : #D9534F; 12 | @color-yellow : #D58512; 13 | @color-green : #398439; 14 | @color-blue-light : lighten(@color-blue, 10%); 15 | @color-blue-dark : darken(@color-blue, 30%); 16 | @color-red-light : lighten(@color-red, 10%); 17 | @color-red-dark : darken(@color-red, 30%); 18 | @color-yellow-light : lighten(@color-yellow, 10%); 19 | @color-yellow-dark : darken(@color-yellow, 30%); 20 | @color-green-light : lighten(@color-green, 10%); 21 | @color-green-dark : darken(@color-green, 30%); 22 | 23 | // LAYER DEPTH ================================================================ 24 | @z-editor: 0; 25 | @z-page: 10; 26 | @z-sidebar: 20; 27 | @z-menubar: 30; 28 | @z-modal: 40; 29 | @z-notification: 50; 30 | 31 | // SIZES ====================================================================== 32 | @size-sidebar: 250px; 33 | @size-menubar: 35px; 34 | @size-page-content: 700px+@size-sidebar; 35 | 36 | @size-header: 100px; 37 | 38 | // PADDING ==================================================================== 39 | @padding-small: 5px; 40 | @padding-small-2x: 10px; 41 | @padding-medium: 15px; 42 | @padding-large: 25px; 43 | @padding-huge: 35px; 44 | @padding-large-2x: 25px; 45 | @padding-huge-2x: 70px; 46 | 47 | 48 | // FONTS ====================================================================== 49 | .font-base { 50 | font-family: sans-serif; 51 | font-weight: 400; 52 | } 53 | 54 | .font-header { 55 | font-family: sans-serif; 56 | font-weight: 300; 57 | } 58 | 59 | .box-shadow(@style) { 60 | -webkit-box-shadow: @style @color-dark1; 61 | box-shadow: @style @color-dark1; 62 | } 63 | 64 | // ANIMATIONS ================================================================= 65 | .animation-slow { 66 | -webkit-transition : all .50s ease-in-out; 67 | -moz-transition : all .50s ease-in-out; 68 | -o-transition : all .50s ease-in-out; 69 | transition : all .50s ease-in-out; 70 | } 71 | 72 | .animation { 73 | -webkit-transition : all .30s ease-in-out; 74 | -moz-transition : all .30s ease-in-out; 75 | -o-transition : all .30s ease-in-out; 76 | transition : all .30s ease-in-out; 77 | } 78 | 79 | .animation-fast { 80 | -webkit-transition : all .15s ease-in-out; 81 | -moz-transition : all .15s ease-in-out; 82 | -o-transition : all .15s ease-in-out; 83 | transition : all .15s ease-in-out; 84 | } -------------------------------------------------------------------------------- /editor/src/assets/libs/mousetrap.min.js: -------------------------------------------------------------------------------- 1 | /* mousetrap v1.5.2 craig.is/killing/mice */ 2 | (function(C,r,g){function t(a,b,h){a.addEventListener?a.addEventListener(b,h,!1):a.attachEvent("on"+b,h)}function x(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return l[a.which]?l[a.which]:p[a.which]?p[a.which]:String.fromCharCode(a.which).toLowerCase()}function D(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function u(a){return"shift"==a||"ctrl"==a||"alt"==a|| 3 | "meta"==a}function y(a,b){var h,c,e,g=[];h=a;"+"===h?h=["+"]:(h=h.replace(/\+{2}/g,"+plus"),h=h.split("+"));for(e=0;em||l.hasOwnProperty(m)&&(k[l[m]]=m)}e=k[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function B(a,b){return a===r?!1:a===b?!0:B(a.parentNode,b)}function c(a){function b(a){a=a||{}; 4 | var b=!1,n;for(n in q)a[n]?b=!0:q[n]=0;b||(v=!1)}function h(a,b,n,f,c,h){var g,e,l=[],m=n.type;if(!d._callbacks[a])return[];"keyup"==m&&u(a)&&(b=[a]);for(g=0;g":".","?":"/","|":"\\"},z={option:"alt",command:"meta","return":"enter",escape:"esc", 9 | plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},k;for(g=1;20>g;++g)l[111+g]="f"+g;for(g=0;9>=g;++g)l[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap={};return this}; 10 | c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||B(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.init=function(){var a=c(r),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};c.init();C.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"=== 11 | typeof define&&define.amd&&define(function(){return c})})(window,document); -------------------------------------------------------------------------------- /editor/src/desktop.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); // Module to control application life. 2 | var BrowserWindow = require('browser-window'); // Module to create native browser window. 3 | 4 | // Report crashes to our server. 5 | require('crash-reporter').start(); 6 | 7 | // Keep a global reference of the window object, if you don't, the window will 8 | // be closed automatically when the JavaScript object is garbage collected. 9 | var mainWindow = null; 10 | 11 | // Quit when all windows are closed. 12 | app.on('window-all-closed', function() { 13 | // On OS X it is common for applications and their menu bar 14 | // to stay active until the user quits explicitly with Cmd + Q 15 | if (process.platform != 'darwin') { 16 | app.quit(); 17 | } 18 | }); 19 | 20 | // This method will be called when Electron has finished 21 | // initialization and is ready to create browser windows. 22 | app.on('ready', function() { 23 | // Create the browser window. 24 | mainWindow = new BrowserWindow({width: 1000, height: 800}); 25 | 26 | // and load the index.html of the app. 27 | mainWindow.loadUrl('file://' + __dirname + '/index.html'); 28 | 29 | // Open the DevTools. 30 | // mainWindow.openDevTools(); 31 | 32 | // Emitted when the window is closed. 33 | mainWindow.on('closed', function() { 34 | // Dereference the window object, usually you would store windows 35 | // in an array if your app supports multi windows, this is the time 36 | // when you should delete the corresponding element. 37 | mainWindow = null; 38 | }); 39 | }); -------------------------------------------------------------------------------- /editor/src/editor/editor/Editor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | /** 5 | * Editor main class. 6 | */ 7 | var Editor = function() { 8 | this.Container_constructor(); 9 | 10 | // Variables 11 | this._project = null; 12 | this._game = null; 13 | this._settings = new b3e.SettingsManager(); 14 | this._dirty = 0; 15 | 16 | // Systems 17 | this._systems = []; 18 | 19 | // Managers 20 | this.project = null; 21 | this.export = null; 22 | this.import = null; 23 | this.shortcuts = null; 24 | 25 | this._createGame(); 26 | }; 27 | var p = createjs.extend(Editor, createjs.Container); 28 | 29 | p._createGame = function() { 30 | var self = this; 31 | this._game = new tine.Game(null, { 32 | update : function() { self._update(); }, 33 | }); 34 | 35 | this._initialize(); 36 | }; 37 | 38 | /** 39 | * Initializes DOM, DOM events, managers and display objects. 40 | */ 41 | p._initialize = function() { 42 | var self = this; 43 | 44 | // RESIZE 45 | var resize = function() { 46 | self._game.canvas.width = window.innerWidth; 47 | self._game.canvas.height = window.innerHeight; 48 | }; 49 | window.onresize = resize; 50 | resize(); 51 | 52 | // GAME 53 | this._game.stage.addChild(this); 54 | 55 | // MANAGERS 56 | this.project = new b3e.editor.ProjectManager(this); 57 | this.export = new b3e.editor.ExportManager(this); 58 | this.import = new b3e.editor.ImportManager(this); 59 | this.shortcuts = new b3e.editor.ShortcutManager(this); 60 | 61 | // SYSTEMS 62 | this._systems.push(new b3e.editor.CameraSystem(this)); 63 | this._systems.push(new b3e.editor.ConnectionSystem(this)); 64 | this._systems.push(new b3e.editor.SelectionSystem(this)); 65 | this._systems.push(new b3e.editor.DragSystem(this)); 66 | this._systems.push(new b3e.editor.CollapseSystem(this)); 67 | this._systems.push(new b3e.editor.ShortcutSystem(this)); 68 | 69 | // SETTINGS 70 | this.applySettings('default'); 71 | }; 72 | 73 | /** 74 | * Called by creatine game. 75 | */ 76 | p._update = function() { 77 | var delta = this._game.time.delta; 78 | this._systems.forEach(function(system) { 79 | system.update(delta); 80 | }); 81 | }; 82 | 83 | p.trigger = function(name, target, variables) { 84 | variables = variables || {}; 85 | 86 | var event = new createjs.Event(name); 87 | event._target = target; 88 | event._data = variables; 89 | this.dispatchEvent(event); 90 | }; 91 | 92 | p.applySettings = function(settings) { 93 | if (settings === 'default') { 94 | settings = b3e.DEFAULT_SETTINGS; 95 | this._settings.clear(); 96 | } 97 | 98 | if (settings) { 99 | this._settings.load(settings); 100 | } 101 | 102 | var canvas = this._game.canvas; 103 | canvas.style.backgroundColor = this._settings.get('background_color'); 104 | 105 | this.project._applySettings(this._settings); 106 | this.export._applySettings(this._settings); 107 | this.import._applySettings(this._settings); 108 | this.shortcuts._applySettings(this._settings); 109 | }; 110 | 111 | p.preview = function(name) { 112 | var canvas = document.createElement('canvas'); 113 | 114 | var p = this.project.get(); 115 | var node = p.nodes.get(name); 116 | var tree = p.trees.getSelected(); 117 | 118 | if (!node) return; 119 | var block = new b3e.Block(node); 120 | block._applySettings(this._settings); 121 | block.x = block._width; 122 | block.y = block._height; 123 | 124 | canvas.setAttribute('width', block._width*tree.scaleX*2); 125 | canvas.setAttribute('height', block._height*tree.scaleY*2); 126 | 127 | var stage = new createjs.Stage(canvas); 128 | stage.scaleX = tree.scaleX; 129 | stage.scaleY = tree.scaleY; 130 | stage.addChild(block); 131 | stage.update(); 132 | 133 | return canvas; 134 | }; 135 | 136 | p.isDirty = function() { 137 | return this._dirty !== 0; 138 | }; 139 | 140 | p.clearDirty = function() { 141 | this._dirty = 0; 142 | }; 143 | 144 | b3e.editor.Editor = createjs.promote(Editor, 'Container'); 145 | })(); -------------------------------------------------------------------------------- /editor/src/editor/editor/managers/ExportManager.js: -------------------------------------------------------------------------------- 1 | b3e.editor.ExportManager = function(editor) { 2 | "use strict"; 3 | 4 | function getBlockChildrenIds(block) { 5 | var conns = block._outConnections.slice(0); 6 | if (editor._settings.get('layout') === 'horizontal') { 7 | conns.sort(function(a, b) { 8 | return a._outBlock.y - 9 | b._outBlock.y; 10 | }); 11 | } else { 12 | conns.sort(function(a, b) { 13 | return a._outBlock.x - 14 | b._outBlock.x; 15 | }); 16 | } 17 | 18 | var nodes = []; 19 | for (var i=0; i 0) { 92 | tree.view.zoomIn(); 93 | } else { 94 | tree.view.zoomOut(); 95 | } 96 | } 97 | }; 98 | 99 | 100 | var self = this; 101 | editor._game.stage.on('stagemousedown', this.onMouseDown, this); 102 | editor._game.stage.on('stagemousemove', this.onMouseMove, this); 103 | editor._game.stage.on('stagemouseup', this.onMouseUp, this); 104 | editor._game.canvas.addEventListener('wheel', function(e) { 105 | self.onMouseWheel(e); 106 | }, false); 107 | editor._game.canvas.addEventListener('mousewheel', function(e) { 108 | self.onMouseWheel(e); 109 | }, false); 110 | editor._game.canvas.addEventListener('DOMMouseScroll ', function(e) { 111 | self.onMouseWheel(e); 112 | }, false); 113 | }; 114 | -------------------------------------------------------------------------------- /editor/src/editor/editor/systems/CollapseSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.CollapseSystem = function(editor) { 2 | "use strict"; 3 | 4 | this.update = function(delta) { 5 | 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /editor/src/editor/editor/systems/ConnectionSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.ConnectionSystem = function(editor) { 2 | "use strict"; 3 | 4 | var connection = null; 5 | var lastOutBlock = null; 6 | 7 | this.update = function(delta) {}; 8 | 9 | this.onMouseDown = function(e) { 10 | if (e.nativeEvent.which !== 1) return; 11 | 12 | var project = editor.project.get(); 13 | if (!project) return; 14 | 15 | var tree = project.trees.getSelected(); 16 | if (!tree) return; 17 | 18 | // if clicked on block 19 | var point = tree.view.getLocalPoint(); 20 | var x = point.x; 21 | var y = point.y; 22 | var block = tree.blocks.getUnderPoint(x, y); 23 | 24 | if (connection || !block) return; 25 | 26 | if (block._hitOutAnchor(x, y)) { 27 | // if user clicked at the outAnchor 28 | connection = tree.connections.add(block, null); 29 | 30 | } else if (block._hitInAnchor(x, y)) { 31 | // if user clicked at the inAnchor 32 | var c = block._inConnection; 33 | if (!c) 34 | return; 35 | 36 | block._inConnection = null; 37 | c._outBlock = null; 38 | lastOutBlock = block; 39 | 40 | connection = c; 41 | } 42 | }; 43 | 44 | this.onMouseMove = function(e) { 45 | // if no connection, return 46 | if (!connection) return; 47 | 48 | var project = editor.project.get(); 49 | if (!project) return; 50 | 51 | var tree = project.trees.getSelected(); 52 | if (!tree) return; 53 | 54 | var point = tree.view.getLocalPoint(); 55 | var x = point.x; 56 | var y = point.y; 57 | 58 | // redraw 59 | connection._redraw(null, null, x, y); 60 | }; 61 | 62 | this.onMouseUp = function(e) { 63 | if (e.nativeEvent.which !== 1) return; 64 | 65 | // if no connection, return 66 | if (!connection) return; 67 | 68 | var project = editor.project.get(); 69 | if (!project) return; 70 | 71 | var tree = project.trees.getSelected(); 72 | if (!tree) return; 73 | 74 | 75 | var point = tree.view.getLocalPoint(); 76 | var x = point.x; 77 | var y = point.y; 78 | var block = tree.blocks.getUnderPoint(x, y); 79 | 80 | // if not connection or connection but no block 81 | project.history._beginBatch(); 82 | if (!block || block === connection._inBlock || block.category === 'root') { 83 | if (lastOutBlock) { 84 | // Add again to connection in order to create history 85 | lastOutBlock._inConnection = connection; 86 | connection._outBlock = lastOutBlock; 87 | } 88 | tree.connections.remove(connection); 89 | } else { 90 | var c; 91 | 92 | // if double parent on node 93 | if (block._inConnection) { 94 | 95 | c = block._inConnection; 96 | tree.connections.remove(c); 97 | } 98 | 99 | // if double children on root 100 | if ((connection._inBlock.category === 'root' || 101 | connection._inBlock.category === 'decorator') && 102 | connection._inBlock._outConnections.length > 1) { 103 | 104 | c = connection._inBlock._outConnections[0]; 105 | tree.connections.remove(c); 106 | } 107 | 108 | connection._outBlock = block; 109 | block._inConnection = connection; 110 | 111 | var _old = [tree.connections, tree.connections._remove, [block]]; 112 | var _new = [tree.connections, tree.connections.add, [connection._inBlock, block]]; 113 | project.history._add(new b3e.Command(_old, _new)); 114 | 115 | connection._redraw(); 116 | } 117 | project.history._endBatch(); 118 | 119 | connection = null; 120 | }; 121 | 122 | editor._game.stage.on('stagemousedown', this.onMouseDown, this); 123 | editor._game.stage.on('stagemousemove', this.onMouseMove, this); 124 | editor._game.stage.on('stagemouseup', this.onMouseUp, this); 125 | }; 126 | -------------------------------------------------------------------------------- /editor/src/editor/editor/systems/DragSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.DragSystem = function(editor) { 2 | "use strict"; 3 | 4 | var isDragging = false; 5 | var dragX0 = 0; 6 | var dragY0 = 0; 7 | 8 | this.update = function(delta) {}; 9 | 10 | this.onMouseDown = function(e) { 11 | if (e.nativeEvent.which !== 1 || 12 | e.nativeEvent.ctrlKey || 13 | isDragging) return; 14 | 15 | var project = editor.project.get(); 16 | if (!project) return; 17 | 18 | var tree = project.trees.getSelected(); 19 | if (!tree) return; 20 | 21 | var point = tree.view.getLocalPoint(); 22 | var x = point.x; 23 | var y = point.y; 24 | var block = tree.blocks.getUnderPoint(x, y); 25 | 26 | // if mouse not on block 27 | if (!block) return; 28 | 29 | // if no block selected 30 | if (!block._isSelected) return; 31 | 32 | // if mouse in anchor 33 | if (!block._hitBody(x, y)) return; 34 | 35 | // start dragging 36 | isDragging = true; 37 | dragX0 = x; 38 | dragY0 = y; 39 | 40 | for (var i=0; i0; 38 | }; 39 | this.canRedo = function() { 40 | return index 0) return; 50 | 51 | // Crear all after index 52 | if (queue.length > index) { 53 | queue.splice(index, queue.length-index); 54 | } 55 | 56 | // Add instruction 57 | if (batchRequests > 0) { 58 | commandBuffer.push(command); 59 | } else { 60 | index++; 61 | command.context = project.trees.getSelected(); 62 | queue.push(command); 63 | 64 | if (editor._dirty < 0) editor._dirty = 0; 65 | editor._dirty++; 66 | } 67 | 68 | // Clear excess 69 | var max = editor._settings.get('max_history'); 70 | if (queue.length > max) { 71 | queue.splice(0, 1); 72 | } 73 | }; 74 | 75 | /** 76 | * Lock the manager, so it can't receive more commands. 77 | */ 78 | this._lock = function() { 79 | // if (lockRequests===0) console.log('------- LOCK -------'); 80 | lockRequests++; 81 | }; 82 | this._unlock = function() { 83 | lockRequests--; 84 | // if (lockRequests===0) console.log('------- UNLOCK -------'); 85 | }; 86 | 87 | /** 88 | * While in batch, merges all added commands to a single command 89 | */ 90 | this._beginBatch = function() { 91 | batchRequests++; 92 | }; 93 | this._endBatch = function() { 94 | batchRequests = Math.max(0, batchRequests-1); 95 | 96 | if (batchRequests === 0) { 97 | if (commandBuffer.length > 0) { 98 | var command = new b3e.Commands(commandBuffer); 99 | command.context = project.trees.getSelected(); 100 | this._add(command); 101 | } 102 | commandBuffer = []; 103 | } 104 | }; 105 | 106 | 107 | this._applySettings = function(settings) { 108 | var max = settings.get('max_history'); 109 | if (queue.length > max) { 110 | queue.splice(0, queue.length-max); 111 | } 112 | }; 113 | }; -------------------------------------------------------------------------------- /editor/src/editor/project/managers/NodeManager.js: -------------------------------------------------------------------------------- 1 | b3e.project.NodeManager = function(editor, project) { 2 | "use strict"; 3 | 4 | /** 5 | * Register a node to the node list. You can provide: 6 | * 7 | * - a `b3.BaseNode` instance. 8 | * - a `b3e.Node` instance. 9 | * - a generic object containing the node prototype. 10 | */ 11 | this.add = function(node, isDefault) { 12 | if (node.prototype) node = node.prototype; 13 | 14 | if (project._nodes[node.name]) { 15 | return false; 16 | } 17 | 18 | if (!(node instanceof b3e.Node)) { 19 | var n = new b3e.Node(isDefault); 20 | n.name = node.name; 21 | n.category = node.category; 22 | n.title = node.title; 23 | n.description = node.description; 24 | n.properties = tine.merge({}, node.properties||node.parameters); 25 | 26 | node = n; 27 | } 28 | 29 | project._nodes[node.name] = node; 30 | if (isDefault !== true) editor.trigger('nodeadded', node); 31 | 32 | var _old = [this, this.remove, [node]]; 33 | var _new = [this, this.add, [node]]; 34 | project.history._add(new b3e.Command(_old, _new)); 35 | 36 | return node; 37 | }; 38 | 39 | /** 40 | * 41 | */ 42 | this.get = function(node) { 43 | if (typeof node !== 'string') return node; 44 | return project._nodes[node]; 45 | }; 46 | 47 | /** 48 | * 49 | */ 50 | this.update = function(node, template) { 51 | node = this.get(node); 52 | var oldName = node.name; 53 | 54 | delete project._nodes[node.name]; 55 | 56 | if (node.name !== template.name && this.get(template.name)) return false; 57 | 58 | 59 | var _oldValues = { 60 | name : node.name, 61 | title : node.title, 62 | description : node.description, 63 | category : node.category, 64 | properties : node.properties, 65 | }; 66 | 67 | if (typeof template.name !== 'undefined') { 68 | node.name = template.name; 69 | } 70 | if (typeof template.title !== 'undefined') { 71 | node.title = template.title; 72 | } 73 | if (typeof template.category !== 'undefined') { 74 | node.category = template.category; 75 | } 76 | if (typeof template.description !== 'undefined') { 77 | node.description = template.description; 78 | } 79 | if (typeof template.properties !== 'undefined') { 80 | node.properties = tine.merge({}, template.properties); 81 | } 82 | 83 | var _newValues = { 84 | name : node.name, 85 | title : node.title, 86 | description : node.description, 87 | category : node.category, 88 | properties : node.properties, 89 | }; 90 | 91 | project.history._beginBatch(); 92 | 93 | project.trees.each(function(tree) { 94 | var blocks = tree.blocks.getAll(); 95 | for (var i=blocks.length-1; i>=0; i--) { 96 | if (blocks[i].name === oldName) { 97 | tree.blocks.update(blocks[i]); 98 | } 99 | } 100 | }); 101 | 102 | project._nodes[node.name] = node; 103 | 104 | var _old = [this, this.update, [node, _oldValues]]; 105 | var _new = [this, this.update, [node, _newValues]]; 106 | project.history._add(new b3e.Command(_old, _new)); 107 | project.history._endBatch(); 108 | 109 | editor.trigger('nodechanged', node); 110 | }; 111 | 112 | /** 113 | * 114 | */ 115 | this.remove = function(node) { 116 | project.history._beginBatch(); 117 | 118 | var name = node.name||node; 119 | delete project._nodes[name]; 120 | 121 | project.trees.each(function(tree) { 122 | var blocks = tree.blocks.getAll(); 123 | for (var i=blocks.length-1; i>=0; i--) { 124 | if (blocks[i].name === name) { 125 | tree.blocks.remove(blocks[i]); 126 | } 127 | } 128 | }); 129 | 130 | var _old = [this, this.add, [node]]; 131 | var _new = [this, this.remove, [node]]; 132 | project.history._add(new b3e.Command(_old, _new)); 133 | 134 | project.history._endBatch(); 135 | 136 | editor.trigger('noderemoved', node); 137 | }; 138 | 139 | /** 140 | * Iterates over node list. 141 | */ 142 | this.each = function(callback, thisarg) { 143 | Object.keys(project._nodes).forEach(function(key) { 144 | callback.call(thisarg, project._nodes[key]); 145 | }); 146 | }; 147 | 148 | this._applySettings = function(settings) {}; 149 | }; -------------------------------------------------------------------------------- /editor/src/editor/project/managers/TreeManager.js: -------------------------------------------------------------------------------- 1 | b3e.project.TreeManager = function(editor, project) { 2 | "use strict"; 3 | 4 | /** 5 | * Adds a new tree to the project. 6 | */ 7 | this.add = function(_id) { 8 | var tree; 9 | 10 | if (_id instanceof b3e.tree.Tree) { 11 | tree = _id; 12 | project.addChild(tree); 13 | editor.trigger('treeadded', tree); 14 | this.select(tree); 15 | 16 | } else { 17 | project.history._beginBatch(); 18 | tree = new b3e.tree.Tree(editor, project); 19 | var root = tree.blocks.getRoot(); 20 | project.addChild(tree); 21 | editor.trigger('treeadded', tree); 22 | 23 | if (_id) tree._id = _id; 24 | 25 | var node = { 26 | name : tree._id, 27 | title : root.title, 28 | category : 'tree', 29 | }; 30 | project.nodes.add(node, true); 31 | 32 | // select if this is the only tree 33 | this.select(tree); 34 | 35 | 36 | var _old = [this, this.remove, [tree]]; 37 | var _new = [this, this.add, [tree]]; 38 | project.history._add(new b3e.Command(_old, _new)); 39 | project.history._endBatch(); 40 | } 41 | 42 | return tree; 43 | }; 44 | 45 | /** 46 | * Gets a tree by id. 47 | */ 48 | this.get = function(tree) { 49 | if (typeof tree === 'string') { 50 | for (var i=0; i=0; i--) { 30 | this.deselect(tree._selectedBlocks[i]); 31 | } 32 | }; 33 | 34 | this.invertSelection = function(block) { 35 | var blocks = (block)?[block]:tree.blocks.getAll(); 36 | 37 | blocks.forEach(function(block) { 38 | if (block._isSelected) { 39 | this.deselect(block); 40 | } else { 41 | this.select(block); 42 | } 43 | }, this); 44 | }; 45 | 46 | this.selectSubtree = function(block) { 47 | var blocks = (block)?[block]:tree._selectedBlocks; 48 | var fSelect = function(block) { 49 | blocks.remove(block); 50 | this.select(block); 51 | }; 52 | 53 | while (blocks.length > 0) { 54 | blocks.pop().traversal(fSelect, this); 55 | } 56 | }; 57 | 58 | this.deselectSubtree = function(block) { 59 | var blocks = (block)?[block]:tree._selectedBlocks; 60 | 61 | var fDeselect = function(block) { 62 | blocks.remove(block); 63 | this.deselect(block); 64 | }; 65 | 66 | while (blocks.length > 0) { 67 | blocks.pop().traversal(fDeselect, this); 68 | } 69 | }; 70 | 71 | this._applySettings = function(settings) {}; 72 | 73 | }; 74 | -------------------------------------------------------------------------------- /editor/src/editor/tree/managers/ViewManager.js: -------------------------------------------------------------------------------- 1 | b3e.tree.ViewManager = function(editor, project, tree) { 2 | "use strict"; 3 | 4 | this.reset = function() { 5 | tree.x = 0; 6 | tree.y = 0; 7 | tree.scaleX = 1; 8 | tree.scaleY = 1; 9 | }; 10 | this.zoom = function(factor) { 11 | tree.scaleX = factor; 12 | tree.scaleY = factor; 13 | }; 14 | this.zoomIn = function() { 15 | var min = editor._settings.get('zoom_min'); 16 | var max = editor._settings.get('zoom_max'); 17 | var step = editor._settings.get('zoom_step'); 18 | 19 | var zoom = tree.scaleX; 20 | this.zoom(tine.clip(zoom+step, min, max)); 21 | }; 22 | this.zoomOut = function() { 23 | var min = editor._settings.get('zoom_min'); 24 | var max = editor._settings.get('zoom_max'); 25 | var step = editor._settings.get('zoom_step'); 26 | 27 | var zoom = tree.scaleX; 28 | this.zoom(tine.clip(zoom-step, min, max)); 29 | }; 30 | this.pan = function(dx, dy) { 31 | tree.x += dx; 32 | tree.y += dy; 33 | }; 34 | this.setCam = function(x, y) { 35 | tree.x = x; 36 | tree.y = y; 37 | }; 38 | this.center = function() { 39 | var canvas = editor._game.canvas; 40 | var hw = canvas.width/2; 41 | var hh = canvas.height/2; 42 | this.setCam(hw, hh); 43 | }; 44 | this.getLocalPoint = function(x, y) { 45 | if (typeof x == 'undefined') x = editor._game.mouse.x; 46 | if (typeof y == 'undefined') y = editor._game.mouse.y; 47 | return tree.globalToLocal(x, y); 48 | }; 49 | 50 | this._applySettings = function(settings) {}; 51 | }; 52 | -------------------------------------------------------------------------------- /editor/src/editor/utils/Command.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | /** 7 | * Represents a command for the history manager. Each command must have an 8 | * undo and a redo specification. The specification must have the following 9 | * format: 10 | * 11 | * var spec = [thisarg, methodOrFunction, params] 12 | * 13 | * For example: 14 | * 15 | * var undo = [this, this.add, [block]]; 16 | * var redo = [this, this.remove, [block]]; 17 | * var command = new b3e.Command(undo, redo); 18 | * 19 | * @class Command 20 | * @param {Array} undo The undo specification. 21 | * @param {Array} redo The redo specification. 22 | * @constructor 23 | */ 24 | b3e.Command = function(undo, redo) { 25 | 26 | if (undo.length !== 3) throw 'Invalid undo command, must have [target, method, args]'; 27 | if (redo.length !== 3) throw 'Invalid redo command, must have [target, method, args]'; 28 | 29 | function execute(target, method, args) { 30 | method.apply(target, args); 31 | } 32 | 33 | /** 34 | * The tree that is selected in the moment of command is added to the 35 | * history manager. This is set by the manager. 36 | * 37 | * @property {b3e.tree.Tree} context; 38 | */ 39 | this.context = null; 40 | 41 | /** 42 | * Execute the redo command. 43 | * 44 | * @method redo 45 | */ 46 | this.redo = function() { 47 | execute(redo[0], redo[1], redo[2]); 48 | }; 49 | 50 | /** 51 | * Execute the undo command. 52 | * 53 | * @method undo 54 | */ 55 | this.undo = function() { 56 | execute(undo[0], undo[1], undo[2]); 57 | }; 58 | }; 59 | 60 | /** 61 | * A list of commands created by the history manager. 62 | * 63 | * @class Command 64 | * @param {Array} undo The undo specification. 65 | * @param {Array} redo The redo specification. 66 | * @constructor 67 | */ 68 | b3e.Commands = function(commands) { 69 | 70 | /** 71 | * The tree that is selected in the moment of command is added to the 72 | * history manager. This is set by the manager. 73 | * 74 | * @property {b3e.tree.Tree} context; 75 | */ 76 | this.context = null; 77 | 78 | /** 79 | * Execute the redo command. 80 | * 81 | * @method redo 82 | */ 83 | this.redo = function() { 84 | for (var i=0; i=0; i--) { 96 | commands[i].undo(); 97 | } 98 | }; 99 | }; 100 | })(); -------------------------------------------------------------------------------- /editor/src/editor/utils/Connection.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function () { 4 | "use strict"; 5 | 6 | /** 7 | * Represents a connection between two blocks. 8 | * 9 | * @class Connection 10 | * @constructor 11 | */ 12 | var Connection = function() { 13 | this.Shape_constructor(); 14 | 15 | this._settings = null; 16 | this._inBlock = null; 17 | this._outBlock = null; 18 | }; 19 | var p = createjs.extend(Connection, createjs.Shape); 20 | 21 | /** 22 | * Apply the editor settings to this connection. 23 | * 24 | * @method _applySettings 25 | * @param Object {b3e.SettingsManager} The settings object. 26 | * @protected 27 | */ 28 | p._applySettings = function(settings) { 29 | this._settings = settings; 30 | this._redraw(); 31 | }; 32 | 33 | /** 34 | * Redraw the connection. 35 | * 36 | * @method _redraw 37 | * @protected 38 | */ 39 | p._redraw = function(x1, y1, x2, y2) { 40 | if (! ((this._inBlock||x1||y1) && (this._outBlock||x2||y2)) ) { 41 | return; 42 | } 43 | 44 | var s = this._settings; 45 | var graphics = this.graphics; 46 | var width = s.get('connection_width'); 47 | var color = s.get('connection_color'); 48 | var diff = s.get('anchor_radius') + s.get('anchor_border_width'); 49 | var arrowWidth = s.get('anchor_radius')/2; 50 | var layout = s.get('layout'); 51 | 52 | var dx=0; var dy=0; var angle=0; var ax=0; var ay=0; 53 | // var inAnchor = this._outBlock._getInAnchorPosition(); 54 | // var outAnchor = this._inBlock._getOutAnchorPosition(); 55 | 56 | if (!(x1 === 0||x1)) { 57 | var outAnchor = this._inBlock._getOutAnchorPosition(); 58 | if (layout === 'horizontal') { 59 | x1 = outAnchor.x; 60 | y1 = this._inBlock.y; 61 | } else { 62 | x1 = this._inBlock.x; 63 | y1 = outAnchor.y; 64 | } 65 | } 66 | 67 | if (!(x2 === 0||x2)) { 68 | var inAnchor = this._outBlock._getInAnchorPosition(); 69 | if (layout === 'horizontal') { 70 | x2 = inAnchor.x - diff; 71 | y2 = this._outBlock.y; 72 | } else { 73 | x2 = this._outBlock.x; 74 | y2 = inAnchor.y - diff; 75 | } 76 | } 77 | 78 | if (layout === 'horizontal') { 79 | dx = 2.5*(x2 - x1)/4; 80 | ax = -arrowWidth; 81 | } else { 82 | dy = 2.5*(y2 - y1)/4; 83 | ay = -arrowWidth; 84 | angle = 90; 85 | } 86 | 87 | graphics.clear(); 88 | graphics.setStrokeStyle(width, 'round'); 89 | graphics.beginStroke(color); 90 | graphics.moveTo(x1, y1); 91 | graphics.bezierCurveTo(x1+dx, y1+dy, x2-dx, y2-dy, x2, y2); 92 | graphics.beginFill(color); 93 | graphics.drawPolyStar(x2+ax, y2+ay, arrowWidth, 3, 0, angle); 94 | graphics.endFill(); 95 | graphics.endStroke(); 96 | }; 97 | 98 | b3e.Connection = createjs.promote(Connection, 'Shape'); 99 | })(); -------------------------------------------------------------------------------- /editor/src/editor/utils/EditorError.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/editor/src/editor/utils/EditorError.js -------------------------------------------------------------------------------- /editor/src/editor/utils/Node.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | (function() { 3 | 'use strict'; 4 | 5 | /** 6 | * A node specification. 7 | * 8 | * @class Node 9 | * @param {Boolean} isDefault Whether the node is provided by default or not. 10 | * @constructor 11 | */ 12 | b3e.Node = function(isDefault) { 13 | this.spec = null; 14 | this.name = null; 15 | this.title = null; 16 | this.category = null; 17 | this.description = null; 18 | this.properties = {}; 19 | this.isDefault = !!isDefault; 20 | 21 | /** 22 | * Copy this node. 23 | * 24 | * @method copy 25 | * @returns {b3e.Node} A copy of this node 26 | */ 27 | this.copy = function() { 28 | var n = new b3e.Node(this.isDefault); 29 | n.spec = this.spec; 30 | n.name = this.name; 31 | n.title = this.title; 32 | n.category = this.category; 33 | n.description = this.description; 34 | n.properties = this.properties; 35 | 36 | return n; 37 | }; 38 | }; 39 | })(); -------------------------------------------------------------------------------- /editor/src/editor/utils/Root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Root node specification. 3 | */ 4 | b3e.Root = { 5 | name : 'Root', 6 | category : 'root', 7 | title : 'A behavior tree' 8 | }; -------------------------------------------------------------------------------- /editor/src/editor/utils/SelectionBox.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | /** 5 | * A selecion box in the screen. 6 | * 7 | * @class SelectionBox 8 | * @constructor 9 | */ 10 | var SelectionBox = function() { 11 | this.Shape_constructor(); 12 | 13 | this._settings = null; 14 | this.alpha = 0.3; 15 | this.visible = false; 16 | }; 17 | var p = createjs.extend(SelectionBox, createjs.Shape); 18 | 19 | /** 20 | * Apply the editor settings to the selection box. 21 | * 22 | * @method _applySettings 23 | * @param Object {b3e.SettingsManager} The settings object. 24 | * @protected 25 | */ 26 | p._applySettings = function(settings) { 27 | this._settings = settings; 28 | this._redraw(); 29 | }; 30 | 31 | /** 32 | * Redraw the box. 33 | * 34 | * @method _redraw 35 | * @protected 36 | */ 37 | p._redraw = function(x1, y1, x2, y2) { 38 | var color = this._settings.get('selection_color'); 39 | var graphics = this.graphics; 40 | 41 | var x = Math.min(x1, x2); 42 | var y = Math.min(y1, y2); 43 | var w = Math.abs(x1 -x2); 44 | var h = Math.abs(y1 -y2); 45 | 46 | graphics.clear(); 47 | graphics.beginFill(color); 48 | graphics.drawRect(x, y, w, h); 49 | graphics.endFill(); 50 | }; 51 | 52 | b3e.SelectionBox = createjs.promote(SelectionBox, 'Shape'); 53 | })(); -------------------------------------------------------------------------------- /editor/src/editor/utils/SettingsManager.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function () { 4 | "use strict"; 5 | 6 | /** 7 | * The settings manager handle the editor settings. 8 | * 9 | * @class SettingsManager 10 | * @constructor 11 | */ 12 | var SettingsManager = function() { 13 | this._dict = {}; 14 | }; 15 | var p = SettingsManager.prototype; 16 | 17 | /** 18 | * Erases all current settings. 19 | * 20 | * @method clear 21 | */ 22 | p.clear = function() { 23 | this._dict = {}; 24 | }; 25 | 26 | /** 27 | * Stores a value into the settings. 28 | * 29 | * @method set 30 | * @param {String} key The unique identifier. 31 | * @param {Object} value The value. 32 | */ 33 | p.set = function(key, value) { 34 | this._dict[key] = value; 35 | }; 36 | 37 | /** 38 | * Retrieves a value from the settings. 39 | * 40 | * @method get 41 | * @param {String} key The unique identifier. 42 | * @return {Object} The value. 43 | */ 44 | p.get = function(key) { 45 | return this._dict[key]; 46 | }; 47 | 48 | /** 49 | * Stores a set of values into the settings. 50 | * 51 | * @method load 52 | * @param {String} data An object containing pairs of `key, values`. 53 | */ 54 | p.load = function(data) { 55 | for (var key in data) { 56 | this.set(key, data[key]); 57 | } 58 | }; 59 | 60 | b3e.SettingsManager = SettingsManager; 61 | })(); -------------------------------------------------------------------------------- /editor/src/editor/utils/functions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Polyfill and util functions. 3 | */ 4 | 5 | /** 6 | * JSON 7 | */ 8 | if (!JSON3) { 9 | JSON3 = JSON; 10 | } 11 | 12 | /** 13 | * String.format function 14 | */ 15 | if (!String.prototype.format) { 16 | String.prototype.format = function() { 17 | var args = arguments; 18 | return this.replace(/{(\d+)}/g, function(match, number) { 19 | return typeof args[number] != 'undefined'? 20 | args[number] : match 21 | ; 22 | }); 23 | }; 24 | } 25 | 26 | /** 27 | * Remove function 28 | */ 29 | if (!Array.prototype.remove) { 30 | Array.prototype.remove = function() { 31 | var what, a = arguments, L = a.length, ax; 32 | while (L && this.length) { 33 | what = a[--L]; 34 | while ((ax = this.indexOf(what)) !== -1) { 35 | this.splice(ax, 1); 36 | } 37 | } 38 | return this; 39 | }; 40 | } 41 | 42 | /** 43 | * Array.forEach function 44 | * 45 | * Production steps of ECMA-262, Edition 5, 15.4.4.18 46 | * Reference: http://es5.github.io/#x15.4.4.18 47 | */ 48 | if (!Array.prototype.forEach) { 49 | Array.prototype.forEach = function(callback, thisArg) { 50 | 51 | var T, k; 52 | 53 | if (this === null) { 54 | throw new TypeError(' this is null or not defined'); 55 | } 56 | 57 | // 1. Let O be the result of calling ToObject passing the |this| value as the argument. 58 | var O = Object(this); 59 | 60 | // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". 61 | // 3. Let len be ToUint32(lenValue). 62 | var len = O.length >>> 0; 63 | 64 | // 4. If IsCallable(callback) is false, throw a TypeError exception. 65 | // See: http://es5.github.com/#x9.11 66 | if (typeof callback !== "function") { 67 | throw new TypeError(callback + ' is not a function'); 68 | } 69 | 70 | // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. 71 | if (arguments.length > 1) { 72 | T = thisArg; 73 | } 74 | 75 | // 6. Let k be 0 76 | k = 0; 77 | 78 | // 7. Repeat, while k < len 79 | while (k < len) { 80 | 81 | var kValue; 82 | 83 | // a. Let Pk be ToString(k). 84 | // This is implicit for LHS operands of the in operator 85 | // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. 86 | // This step can be combined with c 87 | // c. If kPresent is true, then 88 | if (k in O) { 89 | 90 | // i. Let kValue be the result of calling the Get internal method of O with argument Pk. 91 | kValue = O[k]; 92 | 93 | // ii. Call the Call internal method of callback with T as the this value and 94 | // argument list containing kValue, k, and O. 95 | callback.call(T, kValue, k, O); 96 | } 97 | // d. Increase k by 1. 98 | k++; 99 | } 100 | // 8. return undefined 101 | }; 102 | } 103 | 104 | /** 105 | * Object.keys function 106 | * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 107 | */ 108 | if (!Object.keys) { 109 | Object.keys = (function() { 110 | 'use strict'; 111 | var hasOwnProperty = Object.prototype.hasOwnProperty, 112 | hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), 113 | dontEnums = [ 114 | 'toString', 115 | 'toLocaleString', 116 | 'valueOf', 117 | 'hasOwnProperty', 118 | 'isPrototypeOf', 119 | 'propertyIsEnumerable', 120 | 'constructor' 121 | ], 122 | dontEnumsLength = dontEnums.length; 123 | 124 | return function(obj) { 125 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 126 | throw new TypeError('Object.keys called on non-object'); 127 | } 128 | 129 | var result = [], prop, i; 130 | 131 | for (prop in obj) { 132 | if (hasOwnProperty.call(obj, prop)) { 133 | result.push(prop); 134 | } 135 | } 136 | 137 | if (hasDontEnumBug) { 138 | for (i = 0; i < dontEnumsLength; i++) { 139 | if (hasOwnProperty.call(obj, dontEnums[i])) { 140 | result.push(dontEnums[i]); 141 | } 142 | } 143 | } 144 | return result; 145 | }; 146 | }()); 147 | } -------------------------------------------------------------------------------- /editor/src/editor/utils/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default settings of the editor. 3 | * 4 | * @constant {Object} DEFAULT_SETTINGS 5 | * @memberOf b3e 6 | */ 7 | 8 | (function () { 9 | "use strict"; 10 | 11 | var DEFAULT_SETTINGS = { 12 | // CAMERA 13 | zoom_initial : 1.0, 14 | zoom_min : 0.25, 15 | zoom_max : 2.0, 16 | zoom_step : 0.25, 17 | 18 | // EDITOR 19 | snap_x : 12, 20 | snap_y : 12, 21 | snap_offset_x : 0, 22 | snap_offset_y : 0, 23 | layout : 'horizontal', // vertical 24 | max_history : 100, 25 | 26 | // COLORS 27 | background_color : '#171717', 28 | selection_color : '#4BB2FD', 29 | block_border_color : '#6D6D6D', 30 | block_symbol_color : '#333333', 31 | anchor_background_color : '#EFEFEF', 32 | 33 | connection_color : '#6D6D6D', 34 | root_color : '#FFFFFF', 35 | decorator_color : '#FFFFFF', 36 | composite_color : '#FFFFFF', 37 | tree_color : '#FFFFFF', 38 | action_color : '#FFFFFF', 39 | condition_color : '#FFFFFF', 40 | 41 | // CONNECTION 42 | connection_width : 2, 43 | 44 | // ANCHOR 45 | anchor_border_width : 2, 46 | anchor_radius : 7, 47 | anchor_offset_x : 4, 48 | anchor_offset_y : 0, 49 | 50 | // BLOCK 51 | block_border_width : 2, 52 | block_root_width : 40, 53 | block_root_height : 40, 54 | block_tree_width : 160, 55 | block_tree_height : 40, 56 | block_composite_width : 40, 57 | block_composite_height : 40, 58 | block_decorator_width : 60, 59 | block_decorator_height : 60, 60 | block_action_width : 160, 61 | block_action_height : 40, 62 | block_condition_width : 160, 63 | block_condition_height : 40, 64 | }; 65 | 66 | b3e.DEFAULT_SETTINGS = DEFAULT_SETTINGS; 67 | })(); -------------------------------------------------------------------------------- /editor/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Behavior3 Editor - v[BUILD_VERSION] - http://behavior3.com 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | Behavior3 Editor 22 | 23 | 24 | Loading... 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /editor/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "behavior3editor", 3 | "version" : "[BUILD_VERSION]", 4 | "main" : "desktop.js", 5 | "window": { 6 | "title" : "Behavior3 Editor (v[BUILD_VERSION]) http://behavior3.com", 7 | "position" : "center", 8 | "toolbar" : false, 9 | "frame" : true, 10 | "width" : 1000, 11 | "height" : 800, 12 | "min_width" : 1000, 13 | "min_height" : 800 14 | } 15 | } -------------------------------------------------------------------------------- /editor/src/start.js: -------------------------------------------------------------------------------- 1 | var editor; 2 | 3 | function startApp() { 4 | var domProgress = document.getElementById('page-preload'); 5 | 6 | editor = new b3e.editor.Editor(); 7 | angular.bootstrap(document, ['app']); 8 | } -------------------------------------------------------------------------------- /lua/src/BTInit.lua: -------------------------------------------------------------------------------- 1 | bt = {} 2 | 3 | json = require("cjson") 4 | 5 | bt.BTNode = require("BTNode") 6 | bt.BTAction = require("action.BTAction") 7 | bt.BTPrecondition = require("BTPrecondition") 8 | 9 | bt.BT_TREE_NODE = { 10 | BTCondition = "condition.BTCondition", 11 | 12 | BTPrioritySelector = "composite.BTPrioritySelector", 13 | BTSequence = "composite.BTSequence", 14 | BTParallel = "composite.BTParallel", 15 | BTParallelFlexible = "composite.BTParallelFlexible", 16 | 17 | BTRunAction = "action.BTRunAction", 18 | BTWaitAction = "action.BTWaitAction", 19 | } 20 | 21 | 22 | bt.BTResult = { 23 | Ended = 1, 24 | Running = 2, 25 | } 26 | bt.BTActionStatus = { 27 | Ready = 1, 28 | Running = 2, 29 | } 30 | 31 | --调试日志开关 32 | BTLogEnabled = true 33 | --调试UI开关 34 | BTDrawEnabled = true 35 | 36 | function bt.Log(...) 37 | if BTLogEnabled then 38 | print("[BTLOG] ", ...) 39 | end 40 | end 41 | 42 | 43 | function bt.loadFromJson(path, binder) 44 | local index = 0 45 | if io.exists(path) then 46 | local data = json.decode(io.readfile(path)) 47 | local function loadChild(key) 48 | if data.nodes[key] then 49 | local node = data.nodes[key] 50 | local precondition = nil 51 | if node.properties.precondition and node.properties.precondition ~= "" then 52 | if binder and binder[node.properties.precondition] then 53 | precondition = bt.BTPrecondition.new(node.title, nil, node.properties, handler(binder, binder[node.properties.precondition])) 54 | else 55 | bt.Log("Precondition not found: ", node.properties.precondition) 56 | end 57 | end 58 | if bt.BT_TREE_NODE[node.name] then 59 | local treeNode = require(bt.BT_TREE_NODE[node.name]).new(node.title, precondition, node.properties) 60 | if node.children then 61 | for i,child in ipairs(node.children) do 62 | treeNode:addChild(loadChild(child)) 63 | end 64 | end 65 | treeNode.id = index 66 | index = index + 1 67 | treeNode.display = node.display 68 | return treeNode 69 | else 70 | bt.Log("No BTNode: ", node.name) 71 | end 72 | 73 | else 74 | bt.Log("Key: ", key , " not found!") 75 | end 76 | end 77 | local root = loadChild(data.root) 78 | return root 79 | else 80 | bt.Log("File: "..path.." not Found!") 81 | end 82 | end 83 | 84 | --Using Cococs2dx to Debug 85 | function bt._genDisplayTree(root, nodeRoot, drawNode, activeDrawNode) 86 | local node = cc.Node:create() 87 | node:setPosition(root.display.x, -root.display.y) 88 | root.display.node = node 89 | root.display.drawNode = activeDrawNode 90 | local name = root.name 91 | local t,t2 = name:find("<.*>") 92 | if t then 93 | local key = name:sub(t+1, t2-1) 94 | name = (name:gsub("<.*>", root.properties[key])) 95 | -- print(name) 96 | end 97 | 98 | local text = name.."("..root.id..")\n" 99 | if root.properties and root.properties.precondition then 100 | text = text.."前置条件:"..root.properties.precondition.."\n" 101 | end 102 | 103 | text = text..root.__cname 104 | display.newTTFLabel({text=text, size=20}):setTag(1):addTo(node) 105 | 106 | for i,child in ipairs(root.children) do 107 | bt._genDisplayTree(child, nodeRoot, drawNode, activeDrawNode) 108 | drawNode:drawLine(cc.p(node:getPosition()), cc.p(child.display.x, -child.display.y), cc.c4f(0,1,1,0.2)) 109 | end 110 | nodeRoot:addChild(node) 111 | end 112 | 113 | --Using Cococs2dx to Debug 114 | function bt.debugDisplayTree(root) 115 | local nodeRoot = display.newNode() 116 | local drawNode = cc.DrawNode:create() 117 | local activeDrawNode = cc.DrawNode:create() 118 | nodeRoot:addChild(drawNode) 119 | nodeRoot:addChild(activeDrawNode) 120 | bt._genDisplayTree(root, nodeRoot, drawNode, activeDrawNode) 121 | return nodeRoot 122 | end 123 | 124 | return bt -------------------------------------------------------------------------------- /lua/src/BTNode.lua: -------------------------------------------------------------------------------- 1 | local BTNode = class("BTNode") 2 | 3 | function BTNode:ctor(name, precondition, properties) 4 | self.name = name 5 | self.precondition = precondition 6 | self.properties = properties 7 | self.children = {} 8 | self.interval = 0 9 | self.lastTimeEvaluated = 0 10 | self.activated = false 11 | self.database = nil 12 | self.tmp = 0 13 | 14 | end 15 | 16 | function BTNode:activate(database) 17 | if self.activated then 18 | return 19 | end 20 | 21 | self.database = database 22 | if self.precondition then 23 | self.precondition:activate(database) 24 | end 25 | if #self.children > 0 then 26 | for k,child in ipairs(self.children) do 27 | child:activate(database) 28 | end 29 | end 30 | self.activated = true 31 | end 32 | 33 | function BTNode:doEvaluate() 34 | return true 35 | end 36 | 37 | function BTNode:evaluate() 38 | local coolDownOK = self:_checkTimer() 39 | local ret = self.activated and coolDownOK and 40 | (self.precondition == nil or self.precondition:check()) and self:doEvaluate() 41 | 42 | return ret 43 | end 44 | 45 | function BTNode:tick(delta) 46 | return bt.BTResult.Ended 47 | end 48 | 49 | function BTNode:clear() 50 | end 51 | 52 | function BTNode:addChild(node) 53 | if node then 54 | table.insert(self.children, node) 55 | end 56 | end 57 | 58 | function BTNode:removeChild(node) 59 | if node then 60 | for k,v in ipairs(self.children) do 61 | if v == node then 62 | table.remove(self.children, k) 63 | return v 64 | end 65 | end 66 | end 67 | end 68 | 69 | function BTNode:_checkTimer() 70 | return true 71 | end 72 | 73 | function BTNode:debugClearDrawNode() 74 | if BTDrawEnabled and self.display.drawNode then 75 | self.display.drawNode:clear() 76 | end 77 | end 78 | 79 | function BTNode:debugDrawLineTo(nodes, index) 80 | if BTDrawEnabled and self.display.drawNode then 81 | if not index then index = #nodes end 82 | for i=1,index do 83 | local v = nodes[i] 84 | self.display.drawNode:drawLine(cc.p(self.display.node:getPosition()), cc.p(v.display.node:getPosition()), cc.c4f(0,1,0,0.4)) 85 | end 86 | end 87 | end 88 | 89 | function BTNode:debugSetHighlight(flag) 90 | -- print(self.__cname, self.display.node) 91 | if BTDrawEnabled and self.display.node then 92 | if flag then 93 | self.display.node:getChildByTag(1):setColor(cc.c3b(0,255,0)) 94 | else 95 | self.display.node:getChildByTag(1):setColor(cc.c3b(255,255,255)) 96 | end 97 | end 98 | end 99 | 100 | function BTNode:debugByID(id, ...) 101 | if self.id == id then 102 | self.tmp = self.tmp + 1 103 | print(self.tmp, ...) 104 | end 105 | end 106 | return BTNode 107 | 108 | -------------------------------------------------------------------------------- /lua/src/BTPrecondition.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTPrecondition = class("BTPrecondition", BTNode) 3 | 4 | function BTPrecondition:ctor(name, precondition, properties, func) 5 | BTNode.ctor(self, name, precondition, properties) 6 | 7 | self.func = func 8 | end 9 | 10 | function BTPrecondition:check() 11 | if self.func then 12 | return self.func() 13 | end 14 | return true 15 | end 16 | 17 | function BTPrecondition:tick(delta) 18 | local t = self:check() 19 | if t then 20 | return bt.BTResult.Ended 21 | else 22 | return bt.BTResult.Running 23 | end 24 | end 25 | 26 | return BTPrecondition -------------------------------------------------------------------------------- /lua/src/BTReference.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/lua/src/BTReference.lua -------------------------------------------------------------------------------- /lua/src/action/BTAction.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTAction = class("BTAction", BTNode) 3 | 4 | function BTAction:ctor(name, precondition, properties) 5 | BTNode.ctor(self, name, precondition, properties) 6 | self._status = bt.BTActionStatus.Ready 7 | end 8 | 9 | function BTAction:enter() 10 | bt.Log(self.database.__cname.." On enter action: ", self.name) 11 | self:debugSetHighlight(true) 12 | end 13 | 14 | function BTAction:exit() 15 | bt.Log(self.database.__cname.." On exit action: ", self.name) 16 | self:debugSetHighlight(false) 17 | end 18 | 19 | function BTAction:execute(delta) 20 | 21 | return bt.BTResult.Ended 22 | end 23 | 24 | function BTAction:clear() 25 | if self._status ~= bt.BTActionStatus.Ready then 26 | self:exit() 27 | self._status = bt.BTActionStatus.Ready 28 | end 29 | end 30 | 31 | function BTAction:tick(delta) 32 | local result = bt.BTResult.Ended 33 | if self._status == bt.BTActionStatus.Ready then 34 | self:enter() 35 | self._status = bt.BTActionStatus.Running 36 | end 37 | 38 | if self._status == bt.BTActionStatus.Running then 39 | result = self:execute(delta) 40 | if result ~= bt.BTResult.Running then 41 | self:exit() 42 | self._status = bt.BTActionStatus.Ready 43 | end 44 | end 45 | return result 46 | end 47 | 48 | function BTAction:addChild(node) 49 | bt.Log("ERROR: BTAction: Cannot add a node into BTAction..") 50 | end 51 | 52 | function BTAction:removeChild(node) 53 | bt.Log("ERROR: BTAction: Cannot remove a node into BTAction.") 54 | end 55 | return BTAction -------------------------------------------------------------------------------- /lua/src/action/BTRunAction.lua: -------------------------------------------------------------------------------- 1 | local BTAction = bt.BTAction 2 | local BTRunAction = class("BTRunAction", BTAction) 3 | 4 | function BTRunAction:enter() 5 | self.timer = 0 6 | 7 | end 8 | 9 | function BTRunAction:exit() 10 | end 11 | --------------- 12 | -- Action: 执行函数 13 | function BTRunAction:execute(delta) 14 | self.timer = self.timer + delta 15 | if self.database then 16 | local ret = handler(self.database, self.database[self.properties.operation])(self.timer, self.properties) 17 | if ret then 18 | return bt.BTResult.Ended 19 | end 20 | end 21 | return bt.BTResult.Running 22 | end 23 | return BTRunAction -------------------------------------------------------------------------------- /lua/src/action/BTWaitAction.lua: -------------------------------------------------------------------------------- 1 | local BTAction = bt.BTAction 2 | local BTWaitAction = class("BTWaitAction", BTAction) 3 | 4 | function BTWaitAction:enter() 5 | BTAction.enter(self) 6 | self._duration = 0 7 | self._endTime = self.database:getBattleValue(self.properties.key) 8 | end 9 | 10 | function BTWaitAction:exit() 11 | BTAction.exit(self) 12 | end 13 | 14 | function BTWaitAction:execute(delta) 15 | self._duration = self._duration + delta 16 | local result = self._duration < self._endTime and bt.BTResult.Running or bt.BTResult.Ended 17 | return result 18 | 19 | end 20 | return BTWaitAction -------------------------------------------------------------------------------- /lua/src/cjson.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/lua/src/cjson.so -------------------------------------------------------------------------------- /lua/src/composite/BTParallel.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTParallel = class("BTParallel", BTNode) 3 | 4 | ---------------------- 5 | -- BTParallel evaluates all children, if any of them fails the evaluation, BTParallel fails. 6 | -- BTParallel ticks all children, if flag 7 | -- false: ends when all children ends 8 | -- true: ends when any of the children ends 9 | -- NOTE: Order of child node added does matter! 10 | 11 | -- BTParallel【并行结点 12 | -- 评估所有的子结点,如果其中一个子结点评估失败,那么BTParallel评估失败 13 | -- 当评估成功时,BTParallel会执行每一个子结点的tick,如果shouldEndWhenOneEnd设置为: 14 | -- false: 当所有子结点tick返回Ended时,BTParallel才返回Ended 15 | -- true: 当其中任何一个子结点返回Ended时,BTParallel即返回Ended 16 | -- 注意:子结点的顺序不影响运行 17 | function BTParallel:ctor(name, precondition, properties) 18 | BTNode.ctor(self, name, precondition, properties) 19 | self.shouldEndWhenOneEnd = properties.shouldEndWhenOneEnd 20 | self._results = {} 21 | end 22 | 23 | function BTParallel:doEvaluate() 24 | for k,v in ipairs(self.children) do 25 | if not v:evaluate() then 26 | return false 27 | end 28 | end 29 | 30 | return true 31 | end 32 | function BTParallel:tick(delta) 33 | self:debugSetHighlight(true) 34 | local endingResultCount = 0 35 | for i,v in ipairs(self.children) do 36 | 37 | if self._results[i] == bt.BTResult.Running then 38 | self._results[i] = self.children[i]:tick(delta) 39 | end 40 | if self.shouldEndWhenOneEnd and self.shouldEndWhenOneEnd ~= "false" then 41 | if self._results[i] ~= bt.BTResult.Running then 42 | self:resetResults() 43 | return bt.BTResult.Ended 44 | end 45 | else 46 | if self._results[i] ~= bt.BTResult.Running then 47 | endingResultCount = endingResultCount + 1 48 | end 49 | end 50 | end 51 | 52 | if endingResultCount == #self.children then 53 | self:resetResults() 54 | return bt.BTResult.Ended 55 | end 56 | self:debugDrawLineTo(self.children) 57 | return bt.BTResult.Running 58 | end 59 | 60 | function BTParallel:clear() 61 | self:resetResults() 62 | for k,v in ipairs(self.children) do 63 | v:clear() 64 | end 65 | self:debugSetHighlight(false) 66 | end 67 | 68 | function BTParallel:addChild(node) 69 | BTNode.addChild(self, node) 70 | table.insert(self._results, bt.BTResult.Running) 71 | end 72 | 73 | function BTParallel:removeChild(node) 74 | local index = BTNode.removeChild(self, node) 75 | table.remove(self._results, index) 76 | end 77 | 78 | function BTParallel:resetResults() 79 | for i,v in ipairs(self._results) do 80 | self._results[i] = bt.BTResult.Running 81 | end 82 | end 83 | 84 | return BTParallel 85 | -------------------------------------------------------------------------------- /lua/src/composite/BTParallelFlexible.lua: -------------------------------------------------------------------------------- 1 | local BTParallelFlexible = class("BTParallelFlexible", BTNode) 2 | 3 | ------------------ 4 | -- BTParallelFlexible evaluates all children, if all children fails evaluation, it fails. 5 | -- Any child passes the evaluation will be regarded as active. 6 | -- BTParallelFlexible ticks all active children, if all children ends, it ends. 7 | -- NOTE: Order of child node added does matter! 8 | 9 | -- BTParallelFlexible【松散检查并行结点】 10 | -- BTParallelFlexible和BTParallel有点类似,它会评估所有子结点, 11 | -- 只要其中一个子结点评估成功,则BTParallelFlexible评估成功 12 | -- 所有评估成功的子结点才会执行tick。 13 | -- 当所有子结点返回Ended时,BTParallelFlexible返回Ended 14 | function BTParallelFlexible:ctor(name, precondition, properties) 15 | BTNode.ctor(self, name, precondition, properties) 16 | 17 | self._activeList = {} 18 | end 19 | 20 | function BTParallelFlexible:doEvaluate() 21 | local numActiveChildren = 0 22 | for i,child in ipairs(self.children) do 23 | if child:evaluate() then 24 | self._activeList[i] = true 25 | numActiveChildren = numActiveChildren + 1 26 | else 27 | self._activeList[i] = false 28 | end 29 | end 30 | if numActiveChildren == 0 then 31 | return false 32 | end 33 | return true 34 | end 35 | 36 | function BTParallelFlexible:tick(delta) 37 | self:debugSetHighlight(true) 38 | local numActiveChildren = 0 39 | for i,child in ipairs(self.children) do 40 | local active = self._activeList[i] 41 | if active then 42 | local result = child:tick(delta) 43 | if result == bt.BTResult.Running then 44 | numActiveChildren = numActiveChildren + 1 45 | end 46 | end 47 | end 48 | if numActiveChildren == 0 then 49 | return bt.BTResult.Ended 50 | end 51 | self:debugDrawLineTo(self.children) 52 | return bt.BTResult.Running 53 | end 54 | 55 | function BTParallelFlexible:clear() 56 | for k,v in ipairs(self.children) do 57 | v:clear() 58 | end 59 | self:debugSetHighlight(false) 60 | end 61 | 62 | function BTParallelFlexible:addChild(node) 63 | BTNode.addChild(self, node) 64 | table.insert(self._activeList, false) 65 | end 66 | 67 | function BTParallelFlexible:removeChild(node) 68 | local index = BTNode.removeChild(self, node) 69 | table.remove(self._activeList, index) 70 | end 71 | 72 | return BTParallelFlexible 73 | -------------------------------------------------------------------------------- /lua/src/composite/BTPrioritySelector.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTPrioritySelector = class("BTPrioritySelector", BTNode) 3 | 4 | ----------------- 5 | -- BTPrioritySelector【优先选择结点】 6 | -- 按优先顺序开始评估子结点,当某个子结点评估失败时,才开始评估下一个子结点 7 | -- 一旦某个子结点评估成功,则BTPrioritySelector评估成功,并开始执行该子结点的tick, 8 | -- 直至该子结点返回Ended时,BTPrioritySelector返回Ended。 9 | 10 | -- 若无任何子结点评估成成,则BTPrioritySelector评估失败。 11 | -- 注意:先加入(排在上面)的子结点顺序优先级更高。 12 | function BTPrioritySelector:ctor(name, precondition, properties) 13 | BTNode.ctor(self, name, precondition, properties) 14 | self._activeChild = nil 15 | end 16 | 17 | function BTPrioritySelector:doEvaluate() 18 | self:debugClearDrawNode() 19 | self:debugSetHighlight(true) 20 | for k,v in ipairs(self.children) do 21 | if v:evaluate() then 22 | if self._activeChild ~= nil and self._activeChild ~= v then 23 | self._activeChild:clear() 24 | end 25 | self._activeChild = v 26 | return true 27 | end 28 | end 29 | 30 | if self._activeChild then 31 | self._activeChild:clear() 32 | self._activeChild = nil 33 | end 34 | return false 35 | end 36 | 37 | function BTPrioritySelector:clear() 38 | if self._activeChild then 39 | self._activeChild:clear() 40 | self._activeChild = nil 41 | end 42 | self:debugSetHighlight(false) 43 | end 44 | function BTPrioritySelector:tick(delta) 45 | if not self._activeChild then 46 | return bt.BTResult.Ended 47 | end 48 | self:debugDrawLineTo({self._activeChild}) 49 | local result = self._activeChild:tick(delta) 50 | if result ~= bt.BTResult.Running then 51 | self._activeChild:clear() 52 | self._activeChild = nil 53 | end 54 | 55 | return result 56 | end 57 | 58 | return BTPrioritySelector -------------------------------------------------------------------------------- /lua/src/composite/BTRandomSelector.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/lua/src/composite/BTRandomSelector.lua -------------------------------------------------------------------------------- /lua/src/composite/BTSequence.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTSequence = class("BTSequence", BTNode) 3 | 4 | ----------------- 5 | -- BTSequence evaluteas the current active child, or the first child (if no active child). 6 | -- 7 | -- If passed the evaluation, BTSequence ticks the current active child, or the first child (if no active child available), 8 | -- and if it's result is BTEnded, then change the active child to the next one. 9 | 10 | -- BTSequence【序列结点】 11 | -- 1) BTSequence评估当前被激活的子结点(若当前无被激活子结点,则评估第一个子结点) 12 | -- 2) 如果当前被激活的子结点评估失败,则BTSequence评估失败 13 | -- 3) 否则执行当前被激活子结点的tick,直至该结点返回Ended, 14 | -- 4) 若BTSequence里的子结点全部执行完毕返回Ended,则BTSequence返回Ended 15 | -- 5) BTSequence开始将下一个子结点设为激活,重新开始第一步评估。 16 | function BTSequence:ctor(name, precondition, properties) 17 | BTNode.ctor(self, name, precondition, properties) 18 | self._activeIndex = 0 19 | self._activeChild = nil 20 | end 21 | 22 | function BTSequence:doEvaluate() 23 | self:debugSetHighlight(true) 24 | local ret = false 25 | 26 | if self._activeChild then 27 | ret = self._activeChild:evaluate() 28 | if not ret then 29 | self._activeChild:clear() 30 | self._activeChild = nil 31 | self._activeIndex = 0 32 | end 33 | else 34 | ret = self.children[1]:evaluate() 35 | end 36 | return ret 37 | end 38 | 39 | function BTSequence:tick(delta) 40 | if self._activeChild == nil then 41 | self._activeIndex = 1 42 | self._activeChild = self.children[1] 43 | end 44 | 45 | local result = self._activeChild:tick(delta) 46 | if result == bt.BTResult.Ended then 47 | self._activeIndex = self._activeIndex + 1 48 | if self._activeIndex > #self.children then 49 | self._activeChild:clear() 50 | self._activeChild = nil 51 | self._activeIndex = 0 52 | else 53 | self._activeChild:clear() 54 | self._activeChild = self.children[self._activeIndex] 55 | result = bt.BTResult.Running 56 | end 57 | end 58 | self:debugDrawLineTo(self.children, self._activeIndex) 59 | -- self:debugByID(14, "~~~~~", result) 60 | return result 61 | end 62 | 63 | function BTSequence:clear() 64 | if self._activeChild then 65 | self._activeChild = nil 66 | self._activeIndex = 0 67 | end 68 | 69 | for k,v in ipairs(self.children) do 70 | v:clear() 71 | end 72 | self:debugSetHighlight(false) 73 | end 74 | return BTSequence -------------------------------------------------------------------------------- /lua/src/condition/BTCondition.lua: -------------------------------------------------------------------------------- 1 | local BTNode = bt.BTNode 2 | local BTCondition = class("BTCondition", BTNode) 3 | 4 | -------------------- 5 | -- BTCondition【条件判断结点】 6 | 7 | function BTCondition:ctor(name, precondition, properties) 8 | BTNode.ctor(self, name, precondition, properties) 9 | self.assertValue = properties.assertValue 10 | end 11 | 12 | 13 | function BTCondition:tick(delta) 14 | local assertValue = handler(self.database, self.database[self.properties.assertFunc])() 15 | if assertValue then 16 | return bt.BTResult.Ended 17 | else 18 | return bt.BTResult.Running 19 | end 20 | end 21 | 22 | return BTCondition -------------------------------------------------------------------------------- /lua/src/decorator/BTConditionEvulator.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Evaluates the specified conditional task. If the 3 | conditional task returns success then the child task 4 | is run and the child status is returned. If the 5 | conditional task does not return success then the 6 | child task is not run and a failure status is immediately 7 | returned. The conditional task is only evaluated once 8 | at the start. 9 | --]] 10 | -------------------------------------------------------------------------------- /lua/src/decorator/BTDecorator.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/lua/src/decorator/BTDecorator.lua -------------------------------------------------------------------------------- /lua/src/decorator/BTInverse.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The inverter task will invert the return value of the 3 | child task after it has finished executing. 4 | If the child returns success, the inverter task will 5 | return failure. If the child returns failure, the 6 | inverter task will return success. 7 | --]] -------------------------------------------------------------------------------- /lua/src/decorator/BTRepeat.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The repeater task will repeat execution of its child 3 | task until the child task has been run a specified 4 | number of times. 5 | It has the option of continuing to execute the child 6 | task even if the child task returns a failure. 7 | --]] -------------------------------------------------------------------------------- /lua/src/decorator/BTUntil.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The until success task will keep executing 3 | its child task until the child task returns result(success or failed). 4 | --]] -------------------------------------------------------------------------------- /lua/src/global.lua: -------------------------------------------------------------------------------- 1 | function io.readfile(path) 2 | local file = io.open(path, "rb") 3 | if file then 4 | local content = file:read("*a") 5 | io.close(file) 6 | return content 7 | end 8 | return nil 9 | end 10 | 11 | function io.exists(path) 12 | local file = io.open(path, "r") 13 | if file then 14 | io.close(file) 15 | return true 16 | end 17 | return false 18 | end 19 | 20 | function handler(obj, method) 21 | return function(...) 22 | return method(obj, ...) 23 | end 24 | end 25 | 26 | function class(classname, super) 27 | local superType = type(super) 28 | local cls 29 | 30 | if superType ~= "function" and superType ~= "table" then 31 | superType = nil 32 | super = nil 33 | end 34 | 35 | if superType == "function" or (super and super.__ctype == 1) then 36 | -- inherited from native C++ Object 37 | cls = {} 38 | 39 | if superType == "table" then 40 | -- copy fields from super 41 | for k,v in pairs(super) do cls[k] = v end 42 | cls.__create = super.__create 43 | cls.super = super 44 | else 45 | cls.__create = super 46 | cls.ctor = function() end 47 | end 48 | 49 | cls.__cname = classname 50 | cls.__ctype = 1 51 | 52 | function cls.new(...) 53 | local instance = cls.__create(...) 54 | -- copy fields from class to native object 55 | for k,v in pairs(cls) do instance[k] = v end 56 | instance.class = cls 57 | instance:ctor(...) 58 | return instance 59 | end 60 | 61 | else 62 | -- inherited from Lua Object 63 | if super then 64 | cls = {} 65 | setmetatable(cls, {__index = super}) 66 | cls.super = super 67 | else 68 | cls = {ctor = function() end} 69 | end 70 | 71 | cls.__cname = classname 72 | cls.__ctype = 2 -- lua 73 | cls.__index = cls 74 | 75 | function cls.new(...) 76 | local instance = setmetatable({}, cls) 77 | instance.class = cls 78 | instance:ctor(...) 79 | return instance 80 | end 81 | end 82 | 83 | return cls 84 | end -------------------------------------------------------------------------------- /lua/src/test.lua: -------------------------------------------------------------------------------- 1 | require("global") 2 | require("BTInit") 3 | 4 | --init btroot 5 | local btroot 6 | function init() 7 | local binder = {isUnderControl=function()return true end,targetValid=function()return true end,notAimed=function()return true end,isBeforeAttack=function()return true end,isAfterAttack=function()return true end,targetNotValid=function()return true end} 8 | btroot = bt.loadFromJson("../../examples/ai.json", binder) 9 | btroot:activate(binder) 10 | end 11 | 12 | --tick per frame 13 | function update(dt) 14 | if btroot:evaluate() then 15 | btroot:tick(dt) 16 | end 17 | end 18 | 19 | init() 20 | update() -------------------------------------------------------------------------------- /unityeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/donnki/bttree/44be266ff6653c276837606fcab130a81bb35653/unityeditor.png --------------------------------------------------------------------------------