Refactor content editor - Remove conversation tree. Provide links to follow conversation topics.

This commit is contained in:
Oskar Wiksten
2013-01-30 14:22:53 +01:00
parent 8ec233096a
commit 4a4450d20d
5 changed files with 39 additions and 133 deletions

View File

@@ -1,4 +1,4 @@
var ATEditor = (function(ATEditor, model, defaults, _) {
var ATEditor = (function(ATEditor, model, defaults, importExport, _) {
function DialogueController($scope, $routeParams) {
$scope.datasource = model.dialogue;
@@ -6,92 +6,6 @@ var ATEditor = (function(ATEditor, model, defaults, _) {
$scope.phrase = $scope.rootPhrase;
$scope.reply = null;
function rebuildTree(rootPhrase) {
console.log("rebuilding tree for " + rootPhrase.id);
rootPhrase.tree = rootPhrase.tree || {};
rootPhrase.tree.dirty = true;
rebuildPhraseTree(rootPhrase, {});
}
function rebuildPhraseTree(phrase, visitedPhraseIDs) {
if (visitedPhraseIDs[phrase.id]) {
var phraseNode = {};
phraseNode.image = 'imgphrase.png';
phraseNode.text = "(conversation loop)";
phraseNode.phrase = phrase;
phraseNode.reply = null;
phraseNode.children = [];
return phraseNode;
}
visitedPhraseIDs[phrase.id] = true;
if (_.keys(visitedPhraseIDs).length > 1000) { return {}; }
if (phrase.tree && !phrase.tree.dirty) { return phrase.tree; }
console.log("Constructing " + phrase.id);
var phraseNode = {};
phraseNode.dirty = false;
phraseNode.image = 'imgphrase.png';
phraseNode.text = phrase.message || '(no text)';
phraseNode.phrase = phrase;
phraseNode.reply = null;
phraseNode.children = [];
phrase.tree = phraseNode;
if (phrase.hasOnlyNextReply) {
var nextPhrase = model.dialogue.findById(phrase.nextPhraseID);
if (nextPhrase) {
var childNode = rebuildPhraseTree(nextPhrase, visitedPhraseIDs);
phraseNode.children = [ childNode ];
}
} else {
_.each(phrase.replies, function(reply) {
var replyNode = {}
replyNode.image = 'imgreply.png';
replyNode.text = reply.text || '(no text)';
replyNode.phrase = phrase;
replyNode.reply = reply;
replyNode.children = [];
phraseNode.children.push(replyNode);
if (reply.nextPhraseID) {
var nextPhrase = model.dialogue.findById(reply.nextPhraseID);
if (nextPhrase) {
var childNode = rebuildPhraseTree(nextPhrase, visitedPhraseIDs);
replyNode.children.push(childNode);
}
}
});
}
return phraseNode;
}
$scope.onclick = function(node) {
$scope.phrase = node.phrase;
$scope.reply = node.reply;
};
$scope.$watch('phrase.message'
+' + phrase.nextPhraseID'
+' + phrase.hasOnlyNextReply'
+' + reply.text'
+' + reply.nextPhraseID'
, function() {
rebuildTree($scope.phrase);
$scope.node = $scope.rootPhrase.tree;
});
$scope.refreshTree = function() {
function setDirty(node) {
if (node) {
node.dirty = true;
_.each(node.children, function(c) {
setDirty(c);
});
}
}
setDirty($scope.rootPhrase.tree);
rebuildTree($scope.rootPhrase);
};
$scope.removeReward = function(phrase, reward) {
var idx = phrase.rewards.indexOf(reward);
phrase.rewards.splice(idx, 1);
@@ -99,7 +13,25 @@ var ATEditor = (function(ATEditor, model, defaults, _) {
$scope.addReward = function(phrase) {
phrase.rewards.push({});
};
$scope.followNextReply = function(nextPhraseID) {
$scope.proceedToPhrase = function(obj, prop) {
var phraseId = obj[prop];
if (phraseId) {
var nextPhrase = model.dialogue.findById(phraseId);
if (nextPhrase) {
window.location = "#/" + model.dialogue.id + "/edit/" + phraseId;
return;
}
} else {
phraseId = $scope.phrase.id;
}
var newPhrase = model.dialogue.addNew(phraseId);
importExport.prepareObjectsForEditor(model.dialogue, [ newPhrase ]);
newPhrase.hasOnlyNextReply = true;
phraseId = newPhrase.id;
obj[prop] = phraseId;
window.location = "#/" + model.dialogue.id + "/edit/" + phraseId;
};
$scope.selectReply = function(reply) {
$scope.reply = reply;
@@ -121,4 +53,4 @@ var ATEditor = (function(ATEditor, model, defaults, _) {
ATEditor.controllers.DialogueController = DialogueController;
return ATEditor;
})(ATEditor, ATEditor.model, ATEditor.defaults, _);
})(ATEditor, ATEditor.model, ATEditor.defaults, ATEditor.importExport, _);

View File

@@ -25,9 +25,10 @@ var ATEditor = (function(ATEditor, _) {
this.getName = function(obj) {
return obj[options.nameField];
};
this.addNew = function() {
this.addNew = function(suggestedId) {
var obj = { };
obj[options.idField] = 'new_' + options.id;
if (!suggestedId) { suggestedId = 'new_' + options.id; }
obj[options.idField] = suggestedId;
if (options.idField != options.nameField) {
obj[options.nameField] = 'New ' + options.id;
}

View File

@@ -84,22 +84,6 @@ var ATEditor = (function(ATEditor, app, tilesets, $) {
}
});
// http://jsfiddle.net/ag5zC/22/
app.directive('treenode', function ($compile) {
var link;
return {
restrict: 'E',
terminal: true,
scope: { node: '=', onclick: '=' },
link: function (scope, element, attrs) {
if(!link) {
link = $compile(element.html());
}
element.replaceWith(link(scope.$new(), function(clone) { }));
}
}
});
return ATEditor;
})(ATEditor, ATEditor.app, ATEditor.tilesets, jQuery);

View File

@@ -1,24 +1,5 @@
<h3>Dialogue</h3>
<fieldset>
<legend>Conversation flow</legend>
<div id="dialogueTree">
<div>
<a ng-click="refreshTree()" class="btn"><i class="icon-refresh"></i> Refresh</a>
</div>
<treenode node="node" onclick="onclick"><div>
<a ng-click="onclick(node)">
<img ng-src="{{node.image}}" alt="" />
{{node.text}}
</a><br />
<ul>
<li ng-repeat="child in node.children"><treenode node="child" onclick="onclick"></treenode></li>
</ul>
</div></treenode>
</div>
<!-- http://acidjs.wemakesites.net/css3-treevew-no-javascript.html -->
</fieldset>
<fieldset id="dialoguePhrase">
<legend>NPC phrase</legend>
<div class="fieldWithLabel">
@@ -28,7 +9,7 @@
<div class="fieldWithLabel">
<label for="message">NPC says:</label>
<textarea rows="4" cols="40" id="message" ng-model="phrase.message"></textarea>
<p ng-ds-fade="!phrase.message && phrase.replies.length > 0" style="color: #633">
<p ng-ds-fade="!phrase.message && phrase.replies.length > 0" class="dialogueConditional">
Since this phrase does not have any displayed<br />
text, it will not be shown to the player.<br />
Instead, the first reply that matches all<br />
@@ -77,8 +58,9 @@
<label for="nextPhraseID">Phrase ID:</label>
<div class="field">
<input type="text" size="30" id="nextPhraseID" class="at-input-id" ng-model="phrase.nextPhraseID" placeholder="leave empty to generate id" />
<a ng-click="followNextReply(phrase.nextPhraseID)" class="btn" title="Open editor for the indicated phrase. Leave empty to automatically generate a new phrase id based on the prefix for this phrase, or supply a new phrase id that should be used.">
<i class="icon-forward"></i> Go
<a ng-click="proceedToPhrase(phrase, 'nextPhraseID')" class="btn" title="Open editor for the indicated phrase. Leave empty to automatically generate a new phrase id based on the prefix for this phrase, or supply a new phrase id that should be used.">
<span ng-show="phrase.nextPhraseID"><i class="icon-forward"></i> Go</span>
<span ng-show="!phrase.nextPhraseID"><i class="icon-star-empty"></i> Generate</span>
</a>
</div>
</div>
@@ -88,11 +70,16 @@
<thead><tr>
<th>Reply</th>
<th></th>
<th></th>
</tr></thead>
<tbody>
<tr ng-repeat="reply in phrase.replies">
<td ng-click="selectReply(reply)" class="clickToEdit">{{reply.text.substring(0, 40)}}</td>
<td><a ng-click="removeReply(phrase, reply)" class="btn btn-mini" title="Remove reply"><i class="icon-trash"></i></a></td>
<td ng-click="selectReply(reply)" class="clickToEdit">
<span ng-show="reply.text">{{reply.text.substring(0, 40)}}</span>
<span ng-show="!reply.text" class="dialogueConditional">(conditional evaluation)</span>
</td>
<td><a ng-click="removeReply(phrase, reply)" class="btn" title="Remove reply"><i class="icon-trash"></i></a></td>
<td><a ng-click="proceedToPhrase(reply, 'nextPhraseID')" class="btn" title="Follow" ng-show="reply.nextPhraseID && reply.nextPhraseID.length > 1"><i class="icon-forward"></i> Go</a></td>
</tr>
</tbody>
</table>
@@ -120,8 +107,9 @@
<label for="nextPhraseID">Next phrase ID:</label>
<div class="field">
<input type="text" size="30" id="nextPhraseID" class="at-input-id" ng-model="reply.nextPhraseID" placeholder="leave empty to generate id" />
<a ng-click="followNextReply(reply.nextPhraseID)" class="btn" title="Open editor for the indicated phrase. Leave empty to automatically generate a new phrase id based on the prefix for this phrase, or supply a new phrase id that should be used.">
<i class="icon-forward"></i> Go
<a ng-click="proceedToPhrase(reply, 'nextPhraseID')" class="btn" title="Open editor for the indicated phrase. Leave empty to automatically generate a new phrase id based on the prefix for this phrase, or supply a new phrase id that should be used.">
<span ng-show="reply.nextPhraseID"><i class="icon-forward"></i> Go</span>
<span ng-show="!reply.nextPhraseID"><i class="icon-star-empty"></i> Generate</span>
</a>
</div>
</div>

View File

@@ -43,6 +43,7 @@ html, body { margin:0; padding:0; }
#dialogueTree ul { list-style: none; margin-left: 10px; }
#dialogueTree li { }
#dialogueTree .selected { background-color: #d7d7ff; }
.dialogueConditional { color: #855; }
.tools-buttons { padding-bottom: 10px; }
#importExportTextArea { width: 500px; }