1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177 |
- var fs = require('fs');
- // Constants
- var con =
- {
- allowable_connections:
- [
- ['dialogue.Text', 'dialogue.Text'],
- ['dialogue.Text', 'dialogue.Node'],
- ['dialogue.Text', 'dialogue.Choice'],
- ['dialogue.Text', 'dialogue.Set'],
- ['dialogue.Text', 'dialogue.Branch'],
- ['dialogue.Text', 'dialogue.Function'],
- ['dialogue.Text', 'dialogue.Tree'],
- ['dialogue.Text', 'dialogue.GoToLabel'],
- ['dialogue.Node', 'dialogue.Text'],
- ['dialogue.Node', 'dialogue.Node'],
- ['dialogue.Node', 'dialogue.Choice'],
- ['dialogue.Node', 'dialogue.Set'],
- ['dialogue.Node', 'dialogue.Branch'],
- ['dialogue.Node', 'dialogue.Function'],
- ['dialogue.Node', 'dialogue.Tree'],
- ['dialogue.Node', 'dialogue.GoToLabel'],
- ['dialogue.Tree', 'dialogue.Node'],
- ['dialogue.Tree', 'dialogue.Text'],
- ['dialogue.Tree', 'dialogue.Choice'],
- ['dialogue.Tree', 'dialogue.Set'],
- ['dialogue.Tree', 'dialogue.Branch'],
- ['dialogue.Tree', 'dialogue.Function'],
- ['dialogue.Tree', 'dialogue.Tree'],
- ['dialogue.Tree', 'dialogue.GoToLabel'],
- ['dialogue.StartNode', 'dialogue.Text'],
- ['dialogue.StartNode', 'dialogue.Node'],
- ['dialogue.StartNode', 'dialogue.Choice'],
- ['dialogue.StartNode', 'dialogue.Set'],
- ['dialogue.StartNode', 'dialogue.Branch'],
- ['dialogue.StartNode', 'dialogue.Function'],
- ['dialogue.StartNode', 'dialogue.Tree'],
- ['dialogue.StartNode', 'dialogue.GoToLabel'],
- ['dialogue.Choice', 'dialogue.Text'],
- ['dialogue.Choice', 'dialogue.Node'],
- ['dialogue.Choice', 'dialogue.Set'],
- ['dialogue.Choice', 'dialogue.Branch'],
- ['dialogue.Choice', 'dialogue.Function'],
- ['dialogue.Choice', 'dialogue.Tree'],
- ['dialogue.Choice', 'dialogue.GoToLabel'],
- ['dialogue.Set', 'dialogue.Text'],
- ['dialogue.Set', 'dialogue.Node'],
- ['dialogue.Set', 'dialogue.Set'],
- ['dialogue.Set', 'dialogue.Branch'],
- ['dialogue.Set', 'dialogue.Function'],
- ['dialogue.Set', 'dialogue.Tree'],
- ['dialogue.Set', 'dialogue.GoToLabel'],
- ['dialogue.Function', 'dialogue.Text'],
- ['dialogue.Function', 'dialogue.Node'],
- ['dialogue.Function', 'dialogue.Set'],
- ['dialogue.Function', 'dialogue.Branch'],
- ['dialogue.Function', 'dialogue.Function'],
- ['dialogue.Function', 'dialogue.Tree'],
- ['dialogue.Function', 'dialogue.GoToLabel'],
- ['dialogue.Branch', 'dialogue.Text'],
- ['dialogue.Branch', 'dialogue.Node'],
- ['dialogue.Branch', 'dialogue.Set'],
- ['dialogue.Branch', 'dialogue.Branch'],
- ['dialogue.Branch', 'dialogue.Function'],
- ['dialogue.Branch', 'dialogue.Tree'],
- ['dialogue.Branch', 'dialogue.GoToLabel'],
- ],
- default_link: new joint.dia.Link(
- {
- attrs:
- {
- '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', },
- '.link-tools .tool-remove circle, .marker-vertex': { r: 8 },
- },
- }),
- };
- con.default_link.set('smooth', true);
- // State
- var state =
- {
- graph: new joint.dia.Graph(),
- paper: null,
- filepath: null,
- panning: false,
- mouse_position: { x: 0, y: 0 },
- context_position: { x: 0, y: 0 },
- menu: null,
- };
- // Models
- joint.shapes.dialogue = {};
- joint.shapes.dialogue.Base = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Base',
- size: { width: 100, height: 64 },
- name: '',
- attrs:
- {
- rect: { stroke: 'none', 'fill-opacity': 0 },
- text: { display: 'none' },
- },
- },
- joint.shapes.devs.Model.prototype.defaults
- ),
- });
- joint.shapes.dialogue.BaseView = joint.shapes.devs.ModelView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '<textarea class="name" placeholder="Text"></textarea>',
- '</div>',
- ].join(''),
- initialize: function()
- {
- _.bindAll(this, 'updateBox');
- joint.shapes.devs.ModelView.prototype.initialize.apply(this, arguments);
- this.$box = $(_.template(this.template)());
- // Prevent paper from handling pointerdown.
- this.$box.find('textarea, input').on('mousedown click', function(evt) { evt.stopPropagation(); });
- // This is an example of reacting on the input change and storing the input data in the cell model.
- this.$box.find('textarea.name, input.name').on('change', _.bind(function(evt)
- {
- this.model.set('name', $(evt.target).val());
- }, this));
- this.$box.find('.delete').on('click', _.bind(this.model.remove, this.model));
- // Update the box position whenever the underlying model changes.
- this.model.on('change', this.updateBox, this);
- // Remove the box when the model gets removed from the graph.
- this.model.on('remove', this.removeBox, this);
- this.updateBox();
- },
- render: function()
- {
- joint.shapes.devs.ModelView.prototype.render.apply(this, arguments);
- this.paper.$el.prepend(this.$box);
- this.updateBox();
- return this;
- },
- updateBox: function()
- {
- // Set the position and dimension of the box so that it covers the JointJS element.
- var bbox = this.model.getBBox();
- // Example of updating the HTML with a data stored in the cell model.
- var nameField = this.$box.find('textarea.name, input.name');
- if (!nameField.is(':focus'))
- nameField.val(this.model.get('name'));
- var label = this.$box.find('.label');
- var type = this.model.get('type').slice('dialogue.'.length);
- label.text(type);
- label.attr('class', 'label ' + type);
- this.$box[0].classList.add(type);
- this.$box.css({ width: bbox.width, left: bbox.x, top: bbox.y, transform: 'rotate(' + (this.model.get('angle') || 0) + 'deg)' });
- },
- removeBox: function(evt)
- {
- this.$box.remove();
- },
- });
- joint.shapes.dialogue.Node = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Node',
- size: { width: 100, height: 65, },
- inPorts: ['input'],
- outPorts: ['output'],
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.NodeView = joint.shapes.dialogue.BaseView;
- joint.shapes.dialogue.Tree = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Tree',
- size: { width: 150, height: 65, },
- inPorts: ['input'],
- outPorts: ['output'],
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.TreeView = joint.shapes.dialogue.BaseView;
- joint.shapes.dialogue.Text = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Text',
- size: { width: 350 },
- inPorts: ['input'],
- outPorts: ['output'],
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.TextView = joint.shapes.dialogue.BaseView;
- joint.shapes.dialogue.Function = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Function',
- size: { width: 200, height: 65, },
- inPorts: ['input'],
- outPorts: ['output'],
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.FunctionView = joint.shapes.dialogue.BaseView;
- joint.shapes.dialogue.GoToLabel = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.GoToLabel',
- size: { width: 125, height: 65, },
- inPorts: ['input'],
- outPorts: ['output'],
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.GoToLabelView = joint.shapes.dialogue.BaseView;
- joint.shapes.dialogue.Branch = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Branch',
- size: { width: 150, height: 95, },
- inPorts: ['input'],
- outPorts: ['output0'],
- values: [],
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.BranchView = joint.shapes.dialogue.BaseView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '<button class="add">+</button>',
- '<button class="remove">-</button>',
- '<input type="text" class="name" placeholder="Variable" />',
- '<input type="text" value="Default" readonly/>',
- '</div>',
- ].join(''),
- initialize: function()
- {
- joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
- this.$box.find('.add').on('click', _.bind(this.addPort, this));
- this.$box.find('.remove').on('click', _.bind(this.removePort, this));
- },
- removePort: function()
- {
- if (this.model.get('outPorts').length > 1)
- {
- var outPorts = this.model.get('outPorts').slice(0);
- outPorts.pop();
- this.model.set('outPorts', outPorts);
- var values = this.model.get('values').slice(0);
- values.pop();
- this.model.set('values', values);
- this.updateSize();
- }
- },
- addPort: function()
- {
- var outPorts = this.model.get('outPorts').slice(0);
- outPorts.push('output' + outPorts.length.toString());
- this.model.set('outPorts', outPorts);
- var values = this.model.get('values').slice(0);
- values.push(null);
- this.model.set('values', values);
- this.updateSize();
- },
- updateBox: function()
- {
- joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
- var values = this.model.get('values');
- var valueFields = this.$box.find('input.value');
- // Add value fields if necessary
- for (var i = valueFields.length; i < values.length; i++)
- {
- // Prevent paper from handling pointerdown.
- var $field = $('<input type="text" class="value" />');
- $field.attr('placeholder', 'Value ' + (i + 1).toString());
- $field.attr('index', i);
- this.$box.append($field);
- $field.on('mousedown click', function(evt) { evt.stopPropagation(); });
- // This is an example of reacting on the input change and storing the input data in the cell model.
- $field.on('change', _.bind(function(evt)
- {
- var values = this.model.get('values').slice(0);
- values[$(evt.target).attr('index')] = $(evt.target).val();
- this.model.set('values', values);
- }, this));
- }
- // Remove value fields if necessary
- for (var i = values.length; i < valueFields.length; i++)
- $(valueFields[i]).remove();
- // Update value fields
- valueFields = this.$box.find('input.value');
- for (var i = 0; i < valueFields.length; i++)
- {
- var field = $(valueFields[i]);
- if (!field.is(':focus'))
- field.val(values[i]);
- }
- },
- updateSize: function()
- {
- var textField = this.$box.find('input.name');
- var height = textField.outerHeight(true);
- this.model.set('size', { width: 150, height: 95 + Math.max(0, (this.model.get('outPorts').length - 1) * height) });
- },
- });
- joint.shapes.dialogue.StartNode = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.StartNode',
- inPorts: [],
- outPorts: ['output'],
- size: { width: 100, height: 30, },
- value: 'DialogueNode.START',
- attrs:
- {
- '.outPorts circle': { unlimitedConnections: ['dialogue.Choice'], }
- },
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.StartNodeView = joint.shapes.dialogue.BaseView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '</div>',
- ].join(''),
- initialize: function()
- {
- joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
- //this.$box.find('input.value').on('change', _.bind(function(evt)
- //{
- // this.model.set('value', $(evt.target).val());
- //}, this));
- },
- updateBox: function()
- {
- joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
- //var field = this.$box.find('input.value');
- //if (!field.is(':focus'))
- // field.val(this.model.get('value'));
- },
- });
- joint.shapes.dialogue.Choice = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Choice',
- inPorts: ['input'],
- outPorts: ['output'],
- size: { width: 175, height: 95, },
- value: '',
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.ChoiceView = joint.shapes.dialogue.BaseView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '<input type="text" class="name" placeholder="Text" />',
- '<input type="text" class="value" placeholder="Conditions" />',
- '</div>',
- ].join(''),
- initialize: function()
- {
- joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
- this.$box.find('input.value').on('change', _.bind(function(evt)
- {
- this.model.set('value', $(evt.target).val());
- }, this));
- },
- updateBox: function()
- {
- joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
- var field = this.$box.find('input.value');
- if (!field.is(':focus'))
- field.val(this.model.get('value'));
- },
- });
- joint.shapes.dialogue.Set = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.Set',
- inPorts: ['input'],
- outPorts: ['output'],
- size: { width: 125, height: 90, },
- value: '',
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.SetView = joint.shapes.dialogue.BaseView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '<input type="text" class="name" placeholder="Variable" />',
- '<input type="text" class="value" placeholder="Value" />',
- '</div>',
- ].join(''),
- initialize: function()
- {
- joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
- this.$box.find('input.value').on('change', _.bind(function(evt)
- {
- this.model.set('value', $(evt.target).val());
- }, this));
- },
- updateBox: function()
- {
- joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
- var field = this.$box.find('input.value');
- if (!field.is(':focus'))
- field.val(this.model.get('value'));
- },
- });
- /**
- *
- * AI Hook
- *
- */
- joint.shapes.dialogue.AIHook = joint.shapes.devs.Model.extend(
- {
- defaults: joint.util.deepSupplement
- (
- {
- type: 'dialogue.AIHook',
- inPorts: [],
- outPorts: [],
- size: { width: 300, height: 124, },
- value: '',
- },
- joint.shapes.dialogue.Base.prototype.defaults
- ),
- });
- joint.shapes.dialogue.AIHookView = joint.shapes.dialogue.BaseView.extend(
- {
- template:
- [
- '<div class="node">',
- '<span class="label"></span>',
- '<button class="delete">x</button>',
- '<input type="text" class="hooktype" placeholder="AskAbout/Circumstance/Informative/Critical" />',
- '<input type="text" class="identifier" placeholder="Selection Text" />',
- '<input type="text" class="conditions" placeholder="Conditions (greeter, answerer)" />',
- '</div>',
- ].join(''),
- initialize: function()
- {
- joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
- this.$box.find('input.conditions').on('change', _.bind(function(evt)
- {
- this.model.set('conditions', $(evt.target).val());
- }, this));
- this.$box.find('input.hooktype').on('change', _.bind(function(evt)
- {
- this.model.set('hooktype', $(evt.target).val());
- }, this));
- this.$box.find('input.identifier').on('change', _.bind(function(evt)
- {
- this.model.set('identifier', $(evt.target).val());
- }, this));
- },
- updateBox: function()
- {
- joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
- var field = this.$box.find('input.hooktype');
- if (!field.is(':focus'))
- field.val(this.model.get('hooktype'));
- var field = this.$box.find('input.conditions');
- if (!field.is(':focus'))
- field.val(this.model.get('conditions'));
- var field = this.$box.find('input.identifier');
- if (!field.is(':focus'))
- field.val(this.model.get('identifier'));
- },
- });
- // Functions
- var func = {};
- func.validate_connection = function(cellViewS, magnetS, cellViewT, magnetT, end, linkView)
- {
- // Prevent loop linking
- if (magnetS === magnetT)
- return false;
- if (cellViewS === cellViewT)
- return false;
- if ($(magnetT).parents('.outPorts').length > 0) // Can't connect to an output port
- return false;
- var sourceType = cellViewS.model.attributes.type;
- var targetType = cellViewT.model.attributes.type;
- var valid = false;
- for (var i = 0; i < con.allowable_connections.length; i++)
- {
- var rule = con.allowable_connections[i];
- if (sourceType === rule[0] && targetType === rule[1])
- {
- valid = true;
- break;
- }
- }
- if (!valid)
- return false;
- var links = state.graph.getConnectedLinks(cellViewS.model);
- for (var i = 0; i < links.length; i++)
- {
- var link = links[i];
- if (link.attributes.source.id === cellViewS.model.id && link.attributes.source.port === magnetS.attributes.port.nodeValue && link.attributes.target.id)
- {
- var targetCell = state.graph.getCell(link.attributes.target.id);
- if (targetCell.attributes.type !== targetType)
- return false; // We can only connect to multiple targets of the same type
- if (targetCell === cellViewT.model)
- return false; // Already connected
- }
- }
- return true;
- };
- func.validate_magnet = function(cellView, magnet)
- {
- if ($(magnet).parents('.outPorts').length === 0)
- return false; // we only want output ports
- // If unlimited connections attribute is null, we can only ever connect to one object
- // If it is not null, it is an array of type strings which are allowed to have unlimited connections
- var unlimitedConnections = magnet.getAttribute('unlimitedConnections');
- var links = state.graph.getConnectedLinks(cellView.model);
- for (var i = 0; i < links.length; i++)
- {
- var link = links[i];
- if (link.attributes.source.id === cellView.model.id && link.attributes.source.port === magnet.attributes.port.nodeValue)
- {
- // This port already has a connection
- if (unlimitedConnections && link.attributes.target.id)
- {
- var targetCell = state.graph.getCell(link.attributes.target.id);
- if (unlimitedConnections.indexOf(targetCell.attributes.type) !== -1)
- return true; // It's okay because this target type has unlimited connections
- }
- return false;
- }
- }
- return true;
- };
- func.optimized_data = function()
- {
- var cells = state.graph.toJSON().cells;
- var nodesByID = {};
- var cellsByID = {};
- var nodes = [];
- for (var i = 0; i < cells.length; i++)
- {
- var cell = cells[i];
- if (cell.type != 'link')
- {
- var node =
- {
- type: cell.type.slice('dialogue.'.length),
- id: cell.id,
- };
- if (node.type === 'Branch')
- {
- node.variable = cell.name;
- node.branches = {};
- for (var j = 0; j < cell.values.length; j++)
- {
- var branch = cell.values[j];
- node.branches[branch] = null;
- }
- }
- else if (node.type === 'Set')
- {
- node.variable = cell.name;
- node.value = cell.value;
- node.next = null;
- }
- else if (node.type === 'AIHook')
- {
- node.hooktype = cell.hooktype;
- node.identifier = cell.identifier;
- node.conditions = cell.conditions;
- node.next = null;
- }
- else
- {
- node.name = cell.name;
- node.next = null;
- }
- if (node.type === "Choice") {
- node.value = cell.value;
- }
- nodes.push(node);
- nodesByID[cell.id] = node;
- cellsByID[cell.id] = cell;
- }
- }
- for (var i = 0; i < cells.length; i++)
- {
- var cell = cells[i];
- if (cell.type === 'link')
- {
- var source = nodesByID[cell.source.id];
- var target = cell.target ? nodesByID[cell.target.id] : null;
- if (source)
- {
- if (source.type === 'Branch')
- {
- var portNumber = parseInt(cell.source.port.slice('output'.length));
- var value;
- if (portNumber === 0)
- value = '_default';
- else
- {
- var sourceCell = cellsByID[source.id];
- value = sourceCell.values[portNumber - 1];
- }
- source.branches[value] = target ? target.id : null;
- }
- else if ((source.type === 'Text' || source.type === 'Node' || source.type === 'StartNode') && target && target.type === 'Choice')
- {
- if (!source.choices)
- {
- source.choices = [];
- delete source.next;
- }
- source.choices.push(target.id);
- }
- else
- source.next = target ? target.id : null;
- }
- }
- }
- return nodes;
- };
- // Menu actions
- func.flash = function(text)
- {
- var $flash = $('#flash');
- $flash.text(text);
- $flash.stop(true, true);
- $flash.show();
- $flash.css('opacity', 1.0);
- $flash.fadeOut({ duration: 1500 });
- };
- func.apply_fields = function()
- {
- $('input[type=text], select').blur();
- };
- func.save = function()
- {
- func.apply_fields();
- if (!state.filepath)
- func.show_save_dialog();
- else
- func.do_save();
- };
- func.optimized_filename = function(f)
- {
- return f.substring(0, f.length - 2) + 'dlz';
- };
- func.perfect_typescript = function(f)
- {
- return f.substring(0, f.length - 2) + 'dl.ts';
- };
- func.just_name = function (f) {
- return f.substring(0, f.length - 3);
- };
- func.do_save = function()
- {
- if (state.filepath)
- {
- fs.writeFileSync(state.filepath, JSON.stringify(state.graph), 'utf8');
- //fs.writeFileSync(func.optimized_filename(state.filepath), JSON.stringify(func.optimized_data()), 'utf8');
- let name = func.just_name(func.filename_from_filepath(state.filepath));
- let cleants = "// File created automatically by custom Dialogger on " + new Date().toLocaleString() + "\n";
- cleants += "// Do not tamper with this file.\n// It will be replaced automatically by Dialogger and all changes will be lost.\n// Instead change " + name + ".dl.";
- cleants += "\nmodule DialogueTrees {\n export let " + name + " = (function () {\n";
- cleants += func.ts_data(func.optimized_data(), name).replace(/^/gm, ' ') + "\n return tree;\n })();\n}";
-
- fs.writeFileSync(func.perfect_typescript(state.filepath), cleants, 'utf8');
- func.flash('Saved ' + state.filepath);
- }
- };
- func.filename_from_filepath = function(f)
- {
- return f.replace(/^.*[\\\/]/, '');
- };
- func.show_open_dialog = function()
- {
- $('#file_open').click();
- };
- func.show_save_dialog = function()
- {
- $('#file_save').click();
- };
- func.add_node = function(constructor)
- {
- return function()
- {
- var container = $('#container')[0];
- var element = new constructor(
- {
- position: { x: state.context_position.x + container.scrollLeft, y: state.context_position.y + container.scrollTop },
- });
- state.graph.addCells([element]);
- };
- };
- func.clear = function()
- {
- state.graph.clear();
- state.filepath = null;
- document.title = 'Dialogger';
- };
- func.handle_open_files = function(files)
- {
- state.filepath = files[0].path;
- var data = fs.readFileSync(state.filepath);
- document.title = func.filename_from_filepath(state.filepath);
- state.graph.clear();
- state.graph.fromJSON(JSON.parse(data));
- };
- func.handle_save_files = function(files)
- {
- state.filepath = files[0].path;
- func.do_save();
- };
- func.ts_data = function(nodes, name) {
- let declarations = [];
- let addDeclaration = (dec) => {
- if (declarations.indexOf(dec) == -1) {
- declarations.push(dec);
- }
- }
- addDeclaration("let tree : DialogueTree = new DialogueTree(" + JSON.stringify(name) + ");\n");
-
- let finalString = "";
- for (let i = 0; i < nodes.length; i++) {
- finalString += "\n\n";
- let node = nodes[i];
- if (node.type == "Node") {
- finalString += "node = new DialogueNode(" + JSON.stringify(node.id) + ");\n";
- finalString += "node.setName(" + JSON.stringify(node.name) + ");\n";
- if (node.next != null && node.next != undefined) {
- finalString += "node.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "node.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addNode(node);\n";
- addDeclaration("let node : DialogueNode;\n");
- } else if (node.type == "GoToLabel") {
- finalString += "node = new DialogueNode(" + JSON.stringify(node.id) + ");\n";
- finalString += "node.setNext(" + JSON.stringify(node.name) + ");\n";
- finalString += "tree.addNode(node);\n";
- addDeclaration("let node : DialogueNode;\n");
- } else if (node.type == "Tree") {
- finalString += "nodetree = new DialogueNodeTree(" + JSON.stringify(node.id) + ");\n";
- finalString += "nodetree.setTree(() => { return " + node.name + "; });\n";
- if (node.next != null && node.next != undefined) {
- finalString += "nodetree.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "nodetree.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addNode(nodetree);\n";
- addDeclaration("let nodetree : DialogueNodeTree;\n");
- } else if (node.type == "StartNode") {
- finalString += "node = new DialogueNode(" + JSON.stringify(node.id) + ");\n";
- if (node.next != null && node.next != undefined) {
- finalString += "node.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "node.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addStartNode(node);\n";
- addDeclaration("let node : DialogueNode;\n");
- } else if (node.type == "Text") {
- finalString += "text = new DialogueText(" + JSON.stringify(node.id) + ");\n";
- //finalString += "node.setSay(" + node.name + ");\n"; // this way all code we placed will be added right away!
- // SetSay needs to be a function, otherwise data might not be updated when printing!
- finalString += "text.setSay(() => { return new Say(" + node.name + ");});\n";
- if (node.next != null && node.next != undefined) {
- finalString += "text.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "text.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addNode(text);\n";
- addDeclaration("let text : DialogueText;\n");
- } else if (node.type == "Choice") {
- finalString += "choice = new DialogueChoice(" + JSON.stringify(node.id) + ");\n";
- //finalString += "node.setSay(" + node.name + ");\n"; // this way all code we placed will be added right away!
- // SetSay needs to be a function, otherwise data might not be updated when printing!
- finalString += "choice.setSay(() => { return new Say(" + node.name + ");});\n";
- if (node.value != undefined && node.value != null && node.value != "") {
- finalString += "choice.setConditions(() => { return " + node.value + ";});\n"
- }
- if (node.next != null && node.next != undefined) {
- finalString += "choice.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "choice.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addNode(choice);\n";
- addDeclaration("let choice : DialogueChoice;\n");
- } else if (node.type == "Branch") {
- finalString += "branch = new DialogueBranch(" + JSON.stringify(node.id) + ");\n";
- finalString += "branch.setVariable(() => { return " + node.variable + ";});\n";
- for (let key in node.branches) {
- let branchCondition = key;
- let branchTarget = node.branches[key];
- if (branchCondition == "_default") {
- finalString += "branch.setNext(" + JSON.stringify(branchTarget) + ");\n";
- } else {
- finalString += "branch.addBranch(" + JSON.stringify(branchTarget) + ", () => { return " + branchCondition + ";});\n";
- }
- }
- finalString += "tree.addNode(branch);\n";
- addDeclaration("let branch : DialogueBranch;\n");
- } else if (node.type == "Set" || node.type == "Function") {
- finalString += "set = new DialogueSet(" + JSON.stringify(node.id) + ");\n";
- if (node.type == "Function") {
- finalString += "set.setFunction(() => {" + node.name + "});\n";
- } else {
- finalString += "set.setFunction(() => { " + node.variable + " = " + node.value + ";});\n";
- }
- if (node.next != null && node.next != undefined) {
- finalString += "set.setNext(" + JSON.stringify(node.next) + ");\n";
- }
- if (node.choices != undefined && node.choices != null) {
- finalString += "set.setChoices(" + JSON.stringify(node.choices) + ");\n";
- }
- finalString += "tree.addNode(set);\n";
- addDeclaration("let set : DialogueSet;\n");
- } else if (node.type == "AIHook") {
- // hooktype, identifier, conditions
- // AskAbout/Circumstance/Informative/Critical
- node.hooktype = node.hooktype != undefined ? node.hooktype : "";
- node.identifier = node.identifier != undefined ? node.identifier : "";
- node.conditions = node.conditions != undefined ? node.conditions : "";
- let hookType = node.hooktype.toLowerCase().trim();
- finalString += " AI." + (hookType == "askabout" ? "investigateRules" : "talktoRules") + ".createAndAddRule({\n" +
- " name : " + JSON.stringify("AIHook for " + name) + ",\n";
- finalString += " conditions : (runner : RulebookRunner<TalkingHeads>) => {\n" +
- " // @ts-ignore\n" +
- " let player = WorldState.player, greeter = runner.noun.greeter, answerer = runner.noun.answerer;\n" +
- "\n" +
- " return (" + node.conditions + ");\n" +
- " },\n";
- if (hookType == "critical" || hookType == "informative") {
- finalString += " priority : Rule.PRIORITY_HIGHEST,\n" +
- " firstPriority : Rule.PRIORITY_HIGHEST,\n";
- } else {
- finalString += " priority : Rule.PRIORITY_MEDIUM,\n" +
- " firstPriority : Rule.PRIORITY_MEDIUM,\n"
- }
- if (hookType == "informative") {
- finalString += " code : (runner : RulebookRunner<TalkingHeads>) => {\n" +
- " runner.noun.runFirst.push(tree);\n" +
- " },\n";
- } else if (hookType == "critical") {
- finalString += " code : (runner : RulebookRunner<TalkingHeads>) => {\n" +
- " runner.noun.runAndStop.push(tree);\n" +
- " },\n";
- } else {
- finalString += " code : (runner : RulebookRunner<TalkingHeads>) => {\n" +
- " runner.noun.options.push({\n" +
- " tree: tree,\n" +
- " text : new Say(" + node.identifier + ")\n" +
- " })\n" +
- " },\n";
- }
- finalString += "});\n\n";
- }
- }
- return declarations.join("") + finalString;
- };
- // Initialize
- (function()
- {
- state.paper = new joint.dia.Paper(
- {
- el: $('#paper'),
- width: 32000,
- height: 16000,
- model: state.graph,
- gridSize: 1,
- defaultLink: con.default_link,
- validateConnection: func.validate_connection,
- validateMagnet: func.validate_magnet,
- snapLinks: { radius: 75 }, // enable link snapping within 75px lookup radius
- markAvailable: true,
- });
- state.paper.on('blank:pointerdown', function(e, x, y)
- {
- if (e.button === 0 || e.button === 1)
- {
- state.panning = true;
- state.mouse_position.x = e.pageX;
- state.mouse_position.y = e.pageY;
- $('body').css('cursor', 'move');
- func.apply_fields();
- }
- });
- state.paper.on('cell:pointerdown', function(e, x, y)
- {
- func.apply_fields();
- });
- $('#container').mousemove(function(e)
- {
- if (state.panning)
- {
- var $this = $(this);
- $this.scrollLeft($this.scrollLeft() + state.mouse_position.x - e.pageX);
- $this.scrollTop($this.scrollTop() + state.mouse_position.y - e.pageY);
- state.mouse_position.x = e.pageX;
- state.mouse_position.y = e.pageY;
- }
- });
- $('#container').mouseup(function(e)
- {
- state.panning = false;
- $('body').css('cursor', 'default');
- });
- $('#file_open').on('change', function()
- {
- if (this.files)
- func.handle_open_files(this.files);
- // clear files from this input
- var $this = $(this);
- $this.wrap('<form>').parent('form').trigger('reset');
- $this.unwrap();
- });
- $('#file_save').on('change', function()
- {
- func.handle_save_files(this.files);
- });
- $('body').on('dragenter', function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- });
- $('body').on('dragexit', function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- });
- $('body').on('dragover', function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- });
- $('body').on('drop', function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- func.handle_open_files(e.originalEvent.dataTransfer.files);
- });
- $(window).on('keydown', function(event)
- {
- // Catch Ctrl-S or key code 19 on Mac (Cmd-S)
- if (((event.ctrlKey || event.metaKey) && String.fromCharCode(event.which).toLowerCase() === 's') || event.which === 19)
- {
- event.stopPropagation();
- event.preventDefault();
- func.save();
- return false;
- }
- else if ((event.ctrlKey || event.metaKey) && String.fromCharCode(event.which).toLowerCase() === 'o')
- {
- event.stopPropagation();
- event.preventDefault();
- func.show_open_dialog();
- return false;
- }
- return true;
- });
- $(window).resize(function()
- {
- func.apply_fields();
- var $window = $(window);
- var $container = $('#container');
- $container.height($window.innerHeight());
- $container.width($window.innerWidth());
- return this;
- });
- $(window).trigger('resize');
- // Context menu
- state.menu = new nw.Menu();
- state.menu.append(new nw.MenuItem({ label: 'Text', click: func.add_node(joint.shapes.dialogue.Text) }));
- state.menu.append(new nw.MenuItem({ label: 'Choice', click: func.add_node(joint.shapes.dialogue.Choice) }));
- state.menu.append(new nw.MenuItem({ label: 'Branch', click: func.add_node(joint.shapes.dialogue.Branch) }));
- state.menu.append(new nw.MenuItem({ label: 'Set', click: func.add_node(joint.shapes.dialogue.Set) }));
- state.menu.append(new nw.MenuItem({ label: 'Function', click: func.add_node(joint.shapes.dialogue.Function) }));
- state.menu.append(new nw.MenuItem({ label: 'GoToLabel', click: func.add_node(joint.shapes.dialogue.GoToLabel) }));
- state.menu.append(new nw.MenuItem({ label: 'Tree', click: func.add_node(joint.shapes.dialogue.Tree) }));
- state.menu.append(new nw.MenuItem({ label: 'Label', click: func.add_node(joint.shapes.dialogue.Node) }));
- state.menu.append(new nw.MenuItem({ label: 'Start Point', click: func.add_node(joint.shapes.dialogue.StartNode) }));
- state.menu.append(new nw.MenuItem({ label: 'AI Hook', click: func.add_node(joint.shapes.dialogue.AIHook) }));
- state.menu.append(new nw.MenuItem({ type: 'separator' }));
- state.menu.append(new nw.MenuItem({ label: 'Save', click: func.save }));
- state.menu.append(new nw.MenuItem({ label: 'Open', click: func.show_open_dialog }));
- state.menu.append(new nw.MenuItem({ label: 'New', click: func.clear }));
- document.body.addEventListener('contextmenu', function(e)
- {
- e.preventDefault();
- state.context_position.x = e.x;
- state.context_position.y = e.y;
- state.menu.popup(e.x, e.y);
- return false;
- }, false);
- })();
- function updateTextareas () {
- window.requestAnimationFrame(updateTextareas);
- let textareas = new Array(...document.getElementsByTagName("textarea"));
- for (let i = 0; i < textareas.length; i++) {
- let textarea = textareas[i];
- textarea.style.height = "0 px";
- textarea.style.height = textarea.scrollHeight + "px";
- }
- }
- window.requestAnimationFrame(updateTextareas);
|