mirror of
https://github.com/OMGeeky/andors-trail.git
synced 2025-12-26 16:07:57 +01:00
Initial version of updated consolidated editor.
git-svn-id: https://andors-trail.googlecode.com/svn/trunk@66 08aca716-68be-ccc6-4d58-36f5abd142ac
This commit is contained in:
453
AndorsTrailEdit/AndorsTrailEditor.js
Normal file
453
AndorsTrailEdit/AndorsTrailEditor.js
Normal file
@@ -0,0 +1,453 @@
|
||||
function IncludeJavascript(jsFile) {
|
||||
document.write('<script type="text/javascript" src="' + jsFile + '"></scr' + 'ipt>');
|
||||
}
|
||||
|
||||
IncludeJavascript("FieldList.js");
|
||||
IncludeJavascript("DataStore.js");
|
||||
IncludeJavascript("ImageSelector.js");
|
||||
IncludeJavascript("EditorTabs.js");
|
||||
IncludeJavascript("inc/jquery.shorten.min.js");
|
||||
|
||||
|
||||
var model;
|
||||
var imageSelector;
|
||||
var tabs;
|
||||
var questlogDialog;
|
||||
var onHitConditionsDialog;
|
||||
var equipConditionsDialog;
|
||||
var droplistItemDialog;
|
||||
|
||||
|
||||
function checkboxHidesElement(checkbox, element, visibleCondition) {
|
||||
checkbox.change(function () {
|
||||
if (checkbox.attr("checked")) {
|
||||
element.fadeIn("slow");
|
||||
} else {
|
||||
element.fadeOut("slow");
|
||||
}
|
||||
});
|
||||
var visible = bool(visibleCondition);
|
||||
checkbox.attr("checked", visible);
|
||||
element.toggle(visible);
|
||||
}
|
||||
|
||||
function bool(v) {
|
||||
return v ? true : false;
|
||||
}
|
||||
|
||||
function setInputFieldsToObjectValues(div, obj) {
|
||||
div.find("input,select,textarea").each(function() {
|
||||
$(this).val(obj[$(this).attr("id")]);
|
||||
});
|
||||
div.find("input:checkbox").each(function() {
|
||||
//$(this).unbind();
|
||||
$(this).attr("checked", bool(obj[$(this).attr("id")]));
|
||||
});
|
||||
}
|
||||
|
||||
function bindInputFieldChangesToObject(div, obj) {
|
||||
div.find("input,select,textarea").unbind().change(function() {
|
||||
obj[$(this).attr("id")] = $(this).val();
|
||||
});
|
||||
div.find("input:checkbox").unbind("change").change(function() {
|
||||
obj[$(this).attr("id")] = $(this).attr("checked") ? 1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
function applyEditorBindingsForObject(div, obj) {
|
||||
div.find("input").addClass("ui-widget-content ui-corner-all");
|
||||
setInputFieldsToObjectValues(div, obj);
|
||||
bindInputFieldChangesToObject(div, obj);
|
||||
}
|
||||
|
||||
function applyCommonEditorBindings(div, obj, dataStore) {
|
||||
applyEditorBindingsForObject(div, obj);
|
||||
div.find("#" + dataStore.nameField).change(function() { dataStore.onNameChanged(obj, $(this).val()); });
|
||||
}
|
||||
|
||||
function createMonsterEditor(obj) {
|
||||
var div = $( "#templates #editMonster" ).clone();
|
||||
applyCommonEditorBindings(div, obj, model.monsters);
|
||||
checkboxHidesElement(div.find('#hasConversation'), div.find('#hasConversationDisplay'), obj.phraseID);
|
||||
checkboxHidesElement(div.find('#hasCombat'), div.find('#hasCombatDisplay'), obj.attackChance);
|
||||
checkboxHidesElement(div.find('#hasCritical'), div.find('#hasCriticalDisplay'), obj.criticalChance || obj.criticalMultiplier);
|
||||
checkboxHidesElement(div.find('#hasHitEffect'), div.find('#hasHitEffectDisplay'), obj.hasHitEffect);
|
||||
imageSelector.imageify(div.find('#monsterimage'), div.find('#iconID'), 'monsters');
|
||||
bindFieldToDataStore( $( "#droplistID", div ), model.droplists , function(obj) { return obj.id; } );
|
||||
|
||||
|
||||
var createNewCondition = function() { return { chance: 100, magnitude: 1 }; }
|
||||
if (!obj.onHit_conditionsSource) obj.onHit_conditionsSource = [];
|
||||
if (!obj.onHit_conditionsTarget) obj.onHit_conditionsTarget = [];
|
||||
var setupEditor = function(div) {
|
||||
bindFieldToDataStore( $( "#condition", div ), model.actorEffects , function(obj) { return obj.id; } );
|
||||
}
|
||||
applyTableEditor( $( "#onHit_conditionsSource", div ) , onHitConditionsDialog, obj.onHit_conditionsSource, createNewCondition, setupEditor);
|
||||
applyTableEditor( $( "#onHit_conditionsTarget", div ) , onHitConditionsDialog, obj.onHit_conditionsTarget, createNewCondition, setupEditor);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
function createItemEditor(obj) {
|
||||
var div = $( "#templates #editItem" ).clone();
|
||||
applyCommonEditorBindings(div, obj, model.items);
|
||||
checkboxHidesElement(div.find('#hasEquipEffect'), div.find('#hasEquipEffectDisplay'), obj.hasEquipEffect);
|
||||
checkboxHidesElement(div.find('#hasUseEffect'), div.find('#hasUseEffectDisplay'), obj.hasUseEffect);
|
||||
checkboxHidesElement(div.find('#equip_hasCritical'), div.find('#equip_hasCriticalDisplay'), obj.equip_criticalChance || obj.equip_criticalMultiplier);
|
||||
checkboxHidesElement(div.find('#hasHitEffect'), div.find('#hasHitEffectDisplay'), obj.hasHitEffect);
|
||||
checkboxHidesElement(div.find('#hasKillEffect'), div.find('#hasKillEffectDisplay'), obj.hasKillEffect);
|
||||
imageSelector.imageify(div.find('#itemimage'), div.find('#iconID'), 'items');
|
||||
|
||||
var createNewCondition = function() { return { chance: 100, magnitude: 1 }; }
|
||||
if (!obj.equip_conditions) obj.equip_conditions = [];
|
||||
if (!obj.use_conditionsSource) obj.use_conditionsSource = [];
|
||||
if (!obj.hit_conditionsSource) obj.hit_conditionsSource = [];
|
||||
if (!obj.hit_conditionsTarget) obj.hit_conditionsTarget = [];
|
||||
if (!obj.kill_conditionsSource) obj.kill_conditionsSource = [];
|
||||
var setupEditor = function(div) {
|
||||
bindFieldToDataStore( $( "#condition", div ), model.actorEffects , function(obj) { return obj.id; } );
|
||||
}
|
||||
applyTableEditor( $( "#equip_conditions", div ) , equipConditionsDialog, obj.equip_conditions, createNewCondition, setupEditor);
|
||||
applyTableEditor( $( "#use_conditionsSource", div ) , onHitConditionsDialog, obj.use_conditionsSource, createNewCondition, setupEditor);
|
||||
applyTableEditor( $( "#hit_conditionsSource", div ) , onHitConditionsDialog, obj.hit_conditionsSource, createNewCondition, setupEditor);
|
||||
applyTableEditor( $( "#hit_conditionsTarget", div ) , onHitConditionsDialog, obj.hit_conditionsTarget, createNewCondition, setupEditor);
|
||||
applyTableEditor( $( "#kill_conditionsSource", div ) , onHitConditionsDialog, obj.kill_conditionsSource, createNewCondition, setupEditor);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
function bindFieldToDataStore(field, dataStore, converter) {
|
||||
var dataCallback = function(request, response) {
|
||||
var result = [];
|
||||
var pattern = new RegExp(request.term, "i");
|
||||
dataStore.items.forEach(function(obj) {
|
||||
var name = converter(obj);
|
||||
if (name.match(pattern)) {
|
||||
result.push(name);
|
||||
}
|
||||
});
|
||||
response(result);
|
||||
};
|
||||
field.autocomplete( "destroy" ).autocomplete({ source: dataCallback, minLength: 0 });
|
||||
}
|
||||
|
||||
function createStatusEffectEditor(obj) {
|
||||
var div = $( "#templates #editActorEffect" ).clone();
|
||||
applyCommonEditorBindings(div, obj, model.actorEffects);
|
||||
checkboxHidesElement(div.find('#hasRoundEffect'), div.find('#hasRoundEffectDisplay'), obj.hasRoundEffect);
|
||||
checkboxHidesElement(div.find('#hasFullRoundEffect'), div.find('#hasFullRoundEffectDisplay'), obj.hasFullRoundEffect);
|
||||
checkboxHidesElement(div.find('#hasAbilityEffect'), div.find('#hasAbilityEffectDisplay'), obj.hasAbilityEffect);
|
||||
checkboxHidesElement(div.find('#hasCritical'), div.find('#hasCriticalDisplay'), obj.criticalChance || obj.criticalMultiplier);
|
||||
imageSelector.imageify(div.find('#statuseffectimage'), div.find('#iconID'), 'effects');
|
||||
return div;
|
||||
}
|
||||
|
||||
function applyTableEditor(table, dialog, array, templateFunction, editorSetup) {
|
||||
var updateRowText = function(row, obj) {
|
||||
$( "td", row ).each(function() {
|
||||
var id = $( this ).attr("id");
|
||||
var val = obj[id];
|
||||
val = val ? val : "";
|
||||
$( "td#" + id, row ).text(val).shorten({
|
||||
width: '200'
|
||||
}).css('display','');
|
||||
});
|
||||
};
|
||||
|
||||
var addToList = function(obj) {
|
||||
var row = $( "<tr>" );
|
||||
table.find("th").each(function() {
|
||||
var id = $( this ).attr("id");
|
||||
row.append( $( "<td>" ).attr("id", id) );
|
||||
});
|
||||
updateRowText(row, obj);
|
||||
table.append(row);
|
||||
row.click(function() {
|
||||
applyEditorBindingsForObject( dialog, obj );
|
||||
if (editorSetup) { editorSetup(dialog); }
|
||||
dialog.unbind( "dialogclose" ).bind( "dialogclose", function() {
|
||||
updateRowText(row, obj);
|
||||
});
|
||||
dialog.dialog( "open" );
|
||||
});
|
||||
return row;
|
||||
};
|
||||
table.parent().find("#add").button().click(function() {
|
||||
var obj = templateFunction();
|
||||
array.push( obj );
|
||||
addToList( obj ).click();
|
||||
});
|
||||
table.addClass("ui-corner-all");
|
||||
$( "thead", table ).addClass("ui-widget-header");
|
||||
array.forEach(addToList);
|
||||
}
|
||||
|
||||
function createQuestEditor(obj) {
|
||||
var div = $( "#templates #editQuest" ).clone(true);
|
||||
applyCommonEditorBindings(div, obj, model.quests);
|
||||
if (!obj.stages) obj.stages = [];
|
||||
var array = obj.stages;
|
||||
var createNewStage = function() {
|
||||
var nextProgress;
|
||||
if (array.length > 0) { nextProgress = parseInt(array[array.length - 1].progress) + 10; }
|
||||
if (!nextProgress) { nextProgress = 10; }
|
||||
return { progress: nextProgress };
|
||||
};
|
||||
applyTableEditor( $( "#stages", div ) , questlogDialog, array, createNewStage, function() {});
|
||||
return div;
|
||||
}
|
||||
|
||||
function createDroplistEditor(obj) {
|
||||
var div = $( "#templates #editDroplist" ).clone(true);
|
||||
applyCommonEditorBindings(div, obj, model.droplists);
|
||||
if (!obj.items) obj.items = [];
|
||||
var createNewDroplistItem = function() { return { quantity: 1, chance: 100 } };
|
||||
var setupEditor = function(div) {
|
||||
bindFieldToDataStore( $( "#itemID", div ), model.items , function(obj) { return obj.searchTag; } );
|
||||
}
|
||||
applyTableEditor( $( "#items", div ) , droplistItemDialog, obj.items, createNewDroplistItem, setupEditor);
|
||||
return div;
|
||||
}
|
||||
|
||||
|
||||
function openTabForObject(obj, dataStore) {
|
||||
tabs.openTabForObject(obj, dataStore.objectTypename, obj[dataStore.nameField]);
|
||||
}
|
||||
|
||||
function bindObjectsToItemList(itemListDiv, dataStore) {
|
||||
itemListDiv.children().remove();
|
||||
var addToList = function(obj) {
|
||||
var item = $("<li>" + obj[dataStore.nameField] + "</li>");
|
||||
item.click(function() { openTabForObject(obj, dataStore); });
|
||||
itemListDiv.append(item);
|
||||
item.hide().fadeIn('slow');
|
||||
};
|
||||
dataStore.items.forEach(addToList);
|
||||
dataStore.onAdded = addToList;
|
||||
dataStore.onDeserialized = function() {
|
||||
bindObjectsToItemList(itemListDiv, dataStore);
|
||||
// TODO: Should also close all tabs.
|
||||
};
|
||||
dataStore.onNameChanged = function(obj, name) {
|
||||
$("li:eq(" + dataStore.items.indexOf(obj) + ")", itemListDiv).html(name);
|
||||
//TODO: Should this really be in the same function?
|
||||
// (splitting the left part from the tab controls would reduce coupling, which would be a good thing.)
|
||||
tabs.renameTabForObject(obj, name);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function exportIfExists(dataStore, div) {
|
||||
var exists = false;
|
||||
if (dataStore && dataStore.items.length > 0) exists = true;
|
||||
div.toggle(exists);
|
||||
if (!exists) { return; }
|
||||
var exportData = dataStore.serialize();
|
||||
$( "#value" , div ).val(exportData);
|
||||
}
|
||||
|
||||
function prepareImport(dataStore, div) {
|
||||
var importButton = $( "#import", div );
|
||||
var textarea = $( "#value", div );
|
||||
importButton.button({ disabled: true }).click(function() {
|
||||
if (!textarea.val()) return;
|
||||
dataStore.deserialize(textarea.val());
|
||||
div.hide('slow');
|
||||
});
|
||||
textarea.val("").change(function() {
|
||||
var disabled = $(this).val() ? false : true;
|
||||
importButton.button( "option", "disabled", disabled );
|
||||
});
|
||||
}
|
||||
|
||||
function bindEditorType(dataStore, div, createObjectEditor, newObjectCreator) {
|
||||
tabs.registerEditorType(dataStore.objectTypename, createObjectEditor);
|
||||
|
||||
bindObjectsToItemList( $( "ul", div ), dataStore );
|
||||
|
||||
$( "#add", div )
|
||||
.button()
|
||||
.click(function() {
|
||||
var obj = newObjectCreator();
|
||||
dataStore.add(obj);
|
||||
openTabForObject( obj, dataStore );
|
||||
});
|
||||
}
|
||||
|
||||
function startEditor() {
|
||||
|
||||
model = {
|
||||
actorEffects: new DataStore('effect', new FieldList("[id|name|iconID|isStacking|"
|
||||
+ "hasRoundEffect|round_visualEffectID|round_boostHP_Min|round_boostHP_Max|round_boostAP_Min|round_boostAP_Max|"
|
||||
+ "hasFullRoundEffect|fullround_visualEffectID|fullround_boostHP_Min|fullround_boostHP_Max|fullround_boostAP_Min|fullround_boostAP_Max|"
|
||||
+ "hasAbilityEffect|boostMaxHP|boostMaxAP|moveCostPenalty|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|"
|
||||
+ "];"))
|
||||
,quests: new DataStore('quest', new FieldList("[id|name|showInLog|stages[progress|logText|rewardExperience|finishesQuest|]|];"))
|
||||
,items: new DataStore('item', new FieldList("[iconID|name|searchTag|category|baseMarketCost|"
|
||||
+ "hasEquipEffect|equip_boostMaxHP|equip_boostMaxAP|equip_moveCostPenalty|equip_attackCost|equip_attackChance|equip_criticalChance|equip_criticalMultiplier|equip_attackDamage_Min|equip_attackDamage_Max|equip_blockChance|equip_damageResistance|equip_conditions[condition|magnitude|]|"
|
||||
+ "hasUseEffect|use_boostHP_Min|use_boostHP_Max|use_boostAP_Min|use_boostAP_Max|use_conditionsSource[condition|magnitude|duration|chance|]|"
|
||||
+ "hasHitEffect|hit_boostHP_Min|use_boostHP_Max|hit_boostAP_Min|hit_boostAP_Max|hit_conditionsSource[condition|magnitude|duration|chance|]|hit_conditionsTarget[condition|magnitude|duration|chance|]|"
|
||||
+ "hasKillEffect|kill_boostHP_Min|kill_boostHP_Max|kill_boostAP_Min|kill_boostAP_Max|kill_conditionsSource[condition|magnitude|duration|chance|]|"
|
||||
+ "];"))
|
||||
,droplists: new DataStore('droplist', new FieldList("[id|items[itemID|quantity|chance|]|];"), 'id')
|
||||
,dialogue: new DataStore('dialogue', new FieldList("[id|name|];"))
|
||||
,monsters: new DataStore('monster', new FieldList("[iconID|name|tags|size|exp|maxHP|maxAP|moveCost|attackCost|attackChance|criticalChance|criticalMultiplier|attackDamage_Min|attackDamage_Max|blockChance|damageResistance|droplistID|phraseID|"
|
||||
+ "hasHitEffect|onHit_boostHP_Min|onHit_boostHP_Max|onHit_boostAP_Min|onHit_boostAP_Max|onHit_conditionsSource[condition|magnitude|duration|chance|]|onHit_conditionsTarget[condition|magnitude|duration|chance|]|"
|
||||
+ "];"))
|
||||
};
|
||||
|
||||
model.actorEffects.add({id: "bless", name: "Bless", iconID: "items_tiles:318", hasAbilityEffect: true, attackChance: 15, blockChance: 5});
|
||||
model.actorEffects.add({id: "poison_weak", name: "Weak Poison", iconID: "items_tiles:340", hasRoundEffect: true, round_visualEffectID: 2, round_boostHP_Min: -1, round_boostHP_Max: -1});
|
||||
|
||||
model.quests.add({id: "testQuest", name: "Test quest", stages: [ { progress: 10, logText: "Stage 10"} , { progress: 20, logText: "Stage 20", finishesQuest: 1 } ] });
|
||||
|
||||
model.items.add({iconID: "items_tiles:70", name: "Test item", searchTag: "item0", category: 0, baseMarketCost: 51, hasEquipEffect: 1, equip_attackChance: 10, equip_attackDamage_Min: 2, equip_attackDamage_Max: 4});
|
||||
model.items.add({iconID: "items_tiles:266", name: "Ring of damage +1", searchTag: "dmg_ring1", category: 7, baseMarketCost: 62, hasEquipEffect: 1, equip_attackDamage_Min: 1, equip_attackDamage_Max: 1});
|
||||
|
||||
model.droplists.add({id: "merchant1", items: [ { itemID: 'dmg_ring1', quantity: 5, chance: 100 } , { itemID: 'item0', quantity: 1, chance: 100 } ] } );
|
||||
|
||||
model.monsters.add({name: "Small ant", iconID: "monsters_insects:2", maxHP: 30, size: '1x1'});
|
||||
model.monsters.add({name: "Red ant", iconID: "monsters_insects:3", maxHP: 20, size: '1x1'});
|
||||
model.monsters.add({name: "Wasp", iconID: "monsters_insects:1", maxHP: 10, size: '1x1'});
|
||||
|
||||
|
||||
|
||||
|
||||
$( "#left #tools" ).accordion({ fillSpace: true });
|
||||
|
||||
tabs = new EditorTabs( $( "#center #tabs" ) );
|
||||
|
||||
bindEditorType(model.actorEffects, $( "#tools #effectlist" ), createStatusEffectEditor, function() {
|
||||
return {name: "New Effect", id: 'new_effect' };
|
||||
});
|
||||
bindEditorType(model.quests, $( "#tools #questlist" ), createQuestEditor, function() {
|
||||
return {name: "New Quest", id: 'new_quest' };
|
||||
});
|
||||
bindEditorType(model.items, $( "#tools #itemlist" ), createItemEditor, function() {
|
||||
return {name: "New Item", searchTag: "new_item", category: 31 };
|
||||
});
|
||||
bindEditorType(model.droplists, $( "#tools #droplist" ), createDroplistEditor, function() {
|
||||
return {id: "new_droplist" };
|
||||
});
|
||||
bindEditorType(model.monsters, $( "#tools #monsterlist" ), createMonsterEditor, function() {
|
||||
return {name: "New Monster", maxAP: 10, attackCost: 5, moveCost: 5, size: '1x1'};
|
||||
});
|
||||
|
||||
|
||||
var importExportDialog;
|
||||
|
||||
$( "#buttons #import" )
|
||||
.button()
|
||||
.click(function() {
|
||||
importExportDialog.dialog({ title: "Import data" });
|
||||
$( "div", importExportDialog ).show();
|
||||
prepareImport(model.actorEffects, $( "#statuseffects", importExportDialog ));
|
||||
prepareImport(model.quests, $( "#quests", importExportDialog ));
|
||||
prepareImport(model.items, $( "#items", importExportDialog ));
|
||||
prepareImport(model.droplists, $( "#droplists", importExportDialog ));
|
||||
prepareImport(model.dialogue, $( "#dialogue", importExportDialog ));
|
||||
prepareImport(model.monsters, $( "#monsters", importExportDialog ));
|
||||
importExportDialog.dialog( "open" );
|
||||
});
|
||||
$( "#buttons #export" )
|
||||
.button()
|
||||
.click(function() {
|
||||
importExportDialog.dialog({ title: "Export data" });
|
||||
exportIfExists(model.actorEffects, $( "#statuseffects", importExportDialog ));
|
||||
exportIfExists(model.quests, $( "#quests", importExportDialog ));
|
||||
exportIfExists(model.items, $( "#items", importExportDialog ));
|
||||
exportIfExists(model.droplists, $( "#droplists", importExportDialog ));
|
||||
exportIfExists(model.dialogue, $( "#dialogue", importExportDialog ));
|
||||
exportIfExists(model.monsters, $( "#monsters", importExportDialog ));
|
||||
$( "#import", importExportDialog ).hide();
|
||||
importExportDialog.dialog( "open" );
|
||||
});
|
||||
|
||||
var defaultButtons = {
|
||||
Close: function() { $( this ).dialog( "close" ); }
|
||||
};
|
||||
importExportDialog = $( "#templates #dialog-importexport" )
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 800,
|
||||
height: 500,
|
||||
buttons: defaultButtons
|
||||
});
|
||||
|
||||
questlogDialog = $( "#templates #dialog-questlog" )
|
||||
.dialog({
|
||||
title: "Quest log item",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 450,
|
||||
buttons: defaultButtons
|
||||
});
|
||||
|
||||
onHitConditionsDialog = $( "#templates #dialog-onHitConditions" )
|
||||
.dialog({
|
||||
title: "Actor status conditon",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 350,
|
||||
buttons: defaultButtons
|
||||
});
|
||||
|
||||
equipConditionsDialog = $( "#templates #dialog-equipConditions" )
|
||||
.dialog({
|
||||
title: "Actor status conditon",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 350,
|
||||
buttons: defaultButtons
|
||||
});
|
||||
|
||||
droplistItemDialog = $( "#templates #dialog-droplistItem" )
|
||||
.dialog({
|
||||
title: "Droplist item",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 350,
|
||||
buttons: defaultButtons
|
||||
});
|
||||
|
||||
|
||||
|
||||
imageSelector = new ImageSelector("http://andors-trail.googlecode.com/svn/trunk/AndorsTrail/res/drawable/", $( "#dialog-images" ) );
|
||||
imageSelector.add(new TilesetImage("items_tiles", {x: 14, y:30}, {x: 34, y:34}, [ 'items', 'effects' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_armor1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_demon1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_demon2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_dogs", {x: 7, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_dragons", {x: 7, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_eye1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_eye2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_eye3", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_eye4", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_ghost1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_ghost2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_hydra1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_insects", {x: 6, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_liches", {x: 4, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_mage", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_mage2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_mage3", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_mage4", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_man1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_men", {x: 9, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_men2", {x: 10, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_misc", {x: 12, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_rats", {x: 5, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_rogue1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_skeleton1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_skeleton2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_snakes", {x: 6, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_cyclops", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_warrior1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_wraiths", {x: 3, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_zombie1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_zombie2", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
imageSelector.add(new TilesetImage("monsters_dragon1", {x: 1, y:1}, undefined, [ 'monsters' ] ));
|
||||
|
||||
}
|
||||
|
||||
141
AndorsTrailEdit/DataStore.js
Normal file
141
AndorsTrailEdit/DataStore.js
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
var DataStore_Data_fieldValue = '[^\\{\\}\\|]*';
|
||||
var DataStore_Data_arrayObject = '\\{(' + DataStore_Data_fieldValue + '\\|)*\\}';
|
||||
var DataStore_Data_arrayObjectPattern = new RegExp(DataStore_Data_arrayObject, 'g');
|
||||
var DataStore_Data_arrayField = '\\{(' + DataStore_Data_arrayObject + ')*\\}';
|
||||
var DataStore_Data_field = '(' + DataStore_Data_fieldValue + '|' + DataStore_Data_arrayField + ')\\|';
|
||||
var DataStore_Data_pattern = new RegExp(DataStore_Data_field, 'g');
|
||||
var DataStore_Data_line = "^(\\{(" + DataStore_Data_field + ")*\\};)$";
|
||||
var DataStore_Data_linePattern = new RegExp(DataStore_Data_line, 'gm');
|
||||
|
||||
var showErrorMessages = true;
|
||||
|
||||
var specialEncodings = [
|
||||
{
|
||||
decoded: "'", decoded_Regex: /'/gm,
|
||||
encoded: "\\'", encoded_Regex: /\\'/gm
|
||||
},
|
||||
{
|
||||
decoded: "\n", decoded_Regex: /\n/gm,
|
||||
encoded: "\\n", encoded_Regex: /\\n/gm
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
function DataStore(objectTypename, fieldList, nameField) {
|
||||
this.objectTypename = objectTypename;
|
||||
this.fieldList = fieldList;
|
||||
this.nameField = nameField ? nameField : 'name';
|
||||
this.items = [];
|
||||
|
||||
this.add = function(obj) {
|
||||
if (this.items.indexOf(obj) < 0) {
|
||||
this.items.push(obj);
|
||||
this.onAdded(obj);
|
||||
}
|
||||
}
|
||||
this.get = function(index) { return this.items[index]; }
|
||||
this.clear = function() { this.items = {}; }
|
||||
|
||||
this.onAdded = function(obj) { }
|
||||
this.onNameChanged = function(obj, name) { }
|
||||
this.onDeserialized = function() { }
|
||||
|
||||
this.deserialize = function(str) {
|
||||
this.items = deserializeObjectList(this.fieldList, str);
|
||||
this.onDeserialized();
|
||||
}
|
||||
this.serialize = function() {
|
||||
return serializeObjectList(this.fieldList, this.items);
|
||||
}
|
||||
}
|
||||
|
||||
function deserializeObject(fieldList, data) {
|
||||
var match = data.match(DataStore_Data_pattern);
|
||||
if (!match) return;
|
||||
|
||||
if (match.length != fieldList._fields.length) {
|
||||
if (showErrorMessages) {
|
||||
alert("Error parsing data object. Expected " + fieldList._fields.length + " fields, but found " + match.length + " fields.\ndata = \"" + data + "\"");
|
||||
showErrorMessages = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var obj = {};
|
||||
for (var i = 0; i < fieldList._fields.length; ++i) {
|
||||
var s = match[i].match(DataStore_Data_field)[1]; // Strip trailing pipe
|
||||
|
||||
var f = fieldList._fields[i];
|
||||
var v = s;
|
||||
var fieldName = fieldList.getFieldName(i);
|
||||
if (f instanceof FieldList) {
|
||||
fieldName = f._name;
|
||||
v = [];
|
||||
var objects = s.match(DataStore_Data_arrayObjectPattern);
|
||||
if (objects) {
|
||||
for (var j = 0; j < objects.length; ++j) {
|
||||
v[j] = deserializeObject(f, objects[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var j = 0; j < specialEncodings.length; ++j) {
|
||||
var e = specialEncodings[j];
|
||||
v = v.replace(e.encoded_Regex, e.decoded);
|
||||
}
|
||||
}
|
||||
obj[fieldName] = v;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function deserializeObjectList(fieldList, data) {
|
||||
var result = [];
|
||||
if(!data) return result;
|
||||
var match = data.match(DataStore_Data_linePattern);
|
||||
if(!match) return result;
|
||||
for(var i = 0; i < match.length; ++i) {
|
||||
result[i] = deserializeObject(fieldList, match[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function serializeObject(fieldList, obj) {
|
||||
if (!obj) return "";
|
||||
var result = "{";
|
||||
|
||||
for(var i = 0; i < fieldList._fields.length; ++i) {
|
||||
var fieldName = fieldList.getFieldName(i);
|
||||
var f = fieldList._fields[i];
|
||||
var v = obj[fieldName];
|
||||
if (f instanceof FieldList) {
|
||||
if (v && v.length > 0) {
|
||||
result += "{";
|
||||
for(var j = 0; j < v.length; ++j) {
|
||||
result += serializeObject(f, v[j]);
|
||||
}
|
||||
result += "}";
|
||||
}
|
||||
} else if (v != undefined) {
|
||||
v = "" + v;
|
||||
for(var j = 0; j < specialEncodings.length; ++j) {
|
||||
var e = specialEncodings[j];
|
||||
v = v.replace(e.decoded_Regex, e.encoded);
|
||||
}
|
||||
result += v;
|
||||
}
|
||||
result += "|";
|
||||
}
|
||||
result += "}";
|
||||
return result;
|
||||
}
|
||||
|
||||
function serializeObjectList(fieldList, obj) {
|
||||
var result = fieldList.getHeader() + ";\n";
|
||||
if(!obj) return result;
|
||||
|
||||
for(var i = 0; i < obj.length; ++i) {
|
||||
result += serializeObject(fieldList, obj[i]) + ";\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
84
AndorsTrailEdit/EditorTabs.js
Normal file
84
AndorsTrailEdit/EditorTabs.js
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
function EditorTabs(div) {
|
||||
var mainTabs;
|
||||
var nextTabID;
|
||||
var tabInfos = [];
|
||||
var editorTypes = [];
|
||||
|
||||
// =====================================
|
||||
// Private methods
|
||||
|
||||
var makeTabClosable = function(tabID) {
|
||||
var tab = findTab(tabID);
|
||||
tab.find( ".ui-icon-close" ).click(function() {
|
||||
tabInfos[tabID] = null;
|
||||
var index = findTabIndex(tab);
|
||||
mainTabs.tabs( "remove", index );
|
||||
});
|
||||
}
|
||||
|
||||
var findTab = function(tabID) { return mainTabs.find('ul li a[href="#tabs-' + tabID + '"]').parent(); }
|
||||
var findTabIndex = function(tab) { return $( "li", mainTabs ).index(tab); }
|
||||
|
||||
var addTab = function(title, tabInfo) {
|
||||
var tabID = nextTabID;
|
||||
tabInfos[tabID] = tabInfo;
|
||||
mainTabs.tabs( "add", "#tabs-" + tabID, title );
|
||||
mainTabs.tabs( "select", -1 );
|
||||
makeTabClosable(tabID);
|
||||
nextTabID++;
|
||||
}
|
||||
|
||||
var findTabIDOfObject = function(obj) {
|
||||
for (var i = 1; i < tabInfos.length; ++i) {
|
||||
if (tabInfos[i] && tabInfos[i].obj == obj) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var createObjectEditor = function(tabInfo) {
|
||||
var creator = editorTypes[tabInfo.objectType];
|
||||
if (!creator) {
|
||||
alert("unknown objectType: " + tabInfo.objectType);
|
||||
return;
|
||||
}
|
||||
return creator(tabInfo.obj);
|
||||
}
|
||||
|
||||
|
||||
// =====================================
|
||||
// Public methods
|
||||
|
||||
this.registerEditorType = function(objectType, editorCreator) {
|
||||
editorTypes[objectType] = editorCreator;
|
||||
}
|
||||
|
||||
this.renameTabForObject = function(obj, name) {
|
||||
var tabID = findTabIDOfObject(obj);
|
||||
if (!tabID) return;
|
||||
findTab(tabID).find("a").text(name);
|
||||
}
|
||||
|
||||
this.openTabForObject = function(obj, objectType, title) {
|
||||
var tabID = findTabIDOfObject(obj);
|
||||
if (tabID > 0) {
|
||||
var index = findTabIndex(findTab(tabID));
|
||||
mainTabs.tabs( "select", index );
|
||||
return;
|
||||
}
|
||||
addTab(title, {obj: obj, objectType: objectType});
|
||||
}
|
||||
|
||||
mainTabs = div.tabs({
|
||||
tabTemplate: "<li><a href='#{href}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
|
||||
add: function( event, ui ) {
|
||||
var editor = createObjectEditor( tabInfos[nextTabID] );
|
||||
$( ui.panel ).append( editor );
|
||||
mainTabs.tabs('select', ui.index);
|
||||
}
|
||||
});
|
||||
mainTabs.find( ".ui-tabs-nav" ).sortable({ axis: "x" });
|
||||
nextTabID = mainTabs.size() + 1;
|
||||
}
|
||||
62
AndorsTrailEdit/FieldList.js
Normal file
62
AndorsTrailEdit/FieldList.js
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
var FieldList_Header_fieldName = '[^\\[\\]\\|]*';
|
||||
var FieldList_Header_arrayField = FieldList_Header_fieldName + '\\[(' + FieldList_Header_fieldName + '\\|)*\\]';
|
||||
var FieldList_Header_arrayFieldName = new RegExp(FieldList_Header_fieldName);
|
||||
var FieldList_Header_field = '(' + FieldList_Header_fieldName + '|' + FieldList_Header_arrayField + ')\\|';
|
||||
var FieldList_Header_pattern = new RegExp(FieldList_Header_field, 'g');
|
||||
var FieldList_Header_line = "^(\\[(" + FieldList_Header_field + ")*\\];)$";
|
||||
var FieldList_Header_linePattern = new RegExp(FieldList_Header_line, 'm');
|
||||
|
||||
function FieldList(header, name) {
|
||||
this._name = name ? name : "";
|
||||
this._fields = [];
|
||||
|
||||
var match = header.match(FieldList_Header_pattern);
|
||||
if (!match) return;
|
||||
|
||||
for (var i = 0; i < match.length; ++i) {
|
||||
var s = match[i].match(FieldList_Header_field)[1]; // Strip trailing pipe
|
||||
|
||||
var f = s;
|
||||
if (s.match(FieldList_Header_arrayField)) {
|
||||
var name = s.match(FieldList_Header_arrayFieldName)[0];
|
||||
f = new FieldList(s, name);
|
||||
}
|
||||
this._fields[i] = f;
|
||||
}
|
||||
|
||||
this.getFieldName = function(i) {
|
||||
var f = this._fields[i];
|
||||
if (f instanceof FieldList) {
|
||||
return f._name;
|
||||
} else {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
this.getHeader = function() {
|
||||
var result = this._name + "[";
|
||||
for(var i = 0; i < this._fields.length; ++i) {
|
||||
var f = this._fields[i];
|
||||
if (f instanceof FieldList) {
|
||||
result += f.getHeader();
|
||||
} else {
|
||||
result += f;
|
||||
}
|
||||
result += "|";
|
||||
}
|
||||
result += "]";
|
||||
return result;
|
||||
}
|
||||
|
||||
this.getHeaderLine = function() {
|
||||
return this.getHeader() + ";";
|
||||
}
|
||||
}
|
||||
|
||||
function findHeader(str, name) {
|
||||
var match = str.match(FieldList_Header_linePattern);
|
||||
if (!match) return;
|
||||
return new FieldList(match[0]);
|
||||
}
|
||||
|
||||
111
AndorsTrailEdit/ImageSelector.js
Normal file
111
AndorsTrailEdit/ImageSelector.js
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
function TilesetImage(name, numTiles, tileSize, tags) {
|
||||
this._name = name;
|
||||
this._numTiles = numTiles ? numTiles : { x: 1, y: 1 };
|
||||
this._tileSize = tileSize ? tileSize : { x: 32, y: 32 };
|
||||
this._tags = tags ? tags : [];
|
||||
|
||||
this.localIDToCoords = function(localID) {
|
||||
return {
|
||||
x: (localID % this._numTiles.x) * this._tileSize.x,
|
||||
y: Math.floor(localID / this._numTiles.x) * this._tileSize.y
|
||||
}
|
||||
}
|
||||
this.coordsToLocalID = function(x, y) {
|
||||
return Math.floor(x / this._tileSize.x)
|
||||
+ this._numTiles.x * Math.floor(y / this._tileSize.y)
|
||||
}
|
||||
}
|
||||
|
||||
var defaultimage = {
|
||||
name: 'defaultimage',
|
||||
localID: 0,
|
||||
path: ''
|
||||
};
|
||||
|
||||
function ImageSelector(imagePath, dialog) {
|
||||
var _tilesets = {};
|
||||
_tilesets[""] = new TilesetImage(defaultimage.name);
|
||||
_tilesets[defaultimage.name] = _tilesets[""];
|
||||
|
||||
var currentInput;
|
||||
|
||||
var get = function(name) { return _tilesets[name]; }
|
||||
|
||||
var parseImageID = function(str) {
|
||||
if (!str || str == "") return defaultimage;
|
||||
var v = str.split(":");
|
||||
if (v.length < 1) return defaultimage;
|
||||
return {
|
||||
name: v[0],
|
||||
localID: v[1],
|
||||
path: imagePath
|
||||
};
|
||||
}
|
||||
|
||||
var getImageID = function(name, localID) {
|
||||
if (!name) return "";
|
||||
return name + ":" + localID;
|
||||
}
|
||||
|
||||
var updateImageFromFormField = function(image, formField) {
|
||||
var img = parseImageID(formField.val());
|
||||
var tilesetImage = get(img.name);
|
||||
if (!tilesetImage) { tilesetImage = get(""); }
|
||||
var c = tilesetImage.localIDToCoords(img.localID);
|
||||
image.css({
|
||||
"background-image": "url(" +img.path + img.name + ".png)",
|
||||
"background-position": (-c.x)+"px " + (-c.y)+"px",
|
||||
"width": tilesetImage._tileSize.x + "px",
|
||||
"height": tilesetImage._tileSize.y + "px",
|
||||
"cursor": "pointer"
|
||||
});
|
||||
}
|
||||
|
||||
this.add = function(tileset) {
|
||||
var name = tileset._name;
|
||||
_tilesets[name] = tileset;
|
||||
dialog.append("<img src=\"" + imagePath + name + ".png\" id=\"" + name + "\" style=\"cursor: pointer;\" />");
|
||||
|
||||
dialog.find("#" + name).click(function(e) {
|
||||
var x = e.pageX - $(this).offset().left;
|
||||
var y = e.pageY - $(this).offset().top;
|
||||
var localID = tileset.coordsToLocalID(x, y);
|
||||
currentInput.val(getImageID(name, localID));
|
||||
currentInput.change(); // Causes the change handler to be run, thus updating the image.
|
||||
dialog.dialog("close");
|
||||
});
|
||||
}
|
||||
|
||||
var showImages = function(showTilesetTag) {
|
||||
jQuery.each(_tilesets, function(idx, t) {
|
||||
var visible = t._tags.indexOf(showTilesetTag) >= 0;
|
||||
$( "#" + idx, dialog ).toggle(visible);
|
||||
});
|
||||
}
|
||||
|
||||
this.imageify = function(img, val, showTilesetTag) {
|
||||
val.change(function() { updateImageFromFormField(img, val); });
|
||||
img.click(function() {
|
||||
currentInput = val;
|
||||
showImages(showTilesetTag);
|
||||
dialog.dialog("open");
|
||||
});
|
||||
val.change();
|
||||
}
|
||||
|
||||
|
||||
dialog.dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
width: 600,
|
||||
height: 800,
|
||||
position: [50,50],
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
767
AndorsTrailEdit/editor.html
Normal file
767
AndorsTrailEdit/editor.html
Normal file
@@ -0,0 +1,767 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<link rel="shortcut icon" href="http://andors.techby2guys.com/favicon.ico"/>
|
||||
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="styles2.css" />
|
||||
<script type="text/javascript" src="AndorsTrailEditor.js"></script>
|
||||
<title>Andor's Trail Content Editor</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="screen">
|
||||
<div id="top">
|
||||
<div class="andorsTrailLogo" id="title">
|
||||
Andor's Trail Content Editor
|
||||
<span id="version">
|
||||
v0.6.9dev1
|
||||
</span>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<div id="import">Import</div>
|
||||
<div id="export">Export</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="left" class="workarea">
|
||||
<div id="tools">
|
||||
<h3><a href="#">Actor status effects</a></h3>
|
||||
<div id="effectlist">
|
||||
<ul class="itemlist"> </ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
<h3><a href="#">Quests</a></h3>
|
||||
<div id="questlist">
|
||||
<ul class="itemlist"> </ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
<h3><a href="#">Items</a></h3>
|
||||
<div id="itemlist">
|
||||
<ul class="itemlist"> </ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
<h3><a href="#">Droplists</a></h3>
|
||||
<div id="droplist">
|
||||
<ul class="itemlist"> </ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
<h3><a href="#">Dialogue</a></h3>
|
||||
<div id="conversationlist">
|
||||
<ul class="itemlist">
|
||||
<li>TODO: Conversation 1</li>
|
||||
<li>TODO: Conversation 2</li>
|
||||
<li>TODO: Conversation 3</li>
|
||||
</ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
<h3><a href="#">Monsters & NPCs</a></h3>
|
||||
<div id="monsterlist">
|
||||
<ul class="itemlist"> </ul>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="center" class="workarea">
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">Start</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1">
|
||||
<h3>Welcome to the content editor for Andor's Trail.</h3>
|
||||
Start by importing some content from the game by pressing the top right "Import" button.<br />
|
||||
Or you could start by creating new content by selecting something from the left list.<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hidden" id="templates">
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Monster editor -->
|
||||
|
||||
<div id="editMonster">
|
||||
<h3>Monster / NPC</h3>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>General</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<div id="monsterimage" class="field"><input type="hidden" id="iconID" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="name" class="label">Display name:</label>
|
||||
<input class="field" type="text" size="30" id="name" class="fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="tags" class="label">Spawngroup:</label>
|
||||
<input class="field" type="text" size="30" id="tags" class="fieldInput" title="This is a grouping of monster types. Use these tags on spawn areas to spawn random monsters with the same tag. Several monsters may have the same tag." />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="size" class="label">Size:</label>
|
||||
<input class="field" type="text" size="5" id="size" class="fieldInput" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasConversation" />Has conversation</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel" id="hasConversationDisplay">
|
||||
<label for="phraseID" class="label">Conversation phrase ID:</label>
|
||||
<input class="field" type="text" size="30" id="phraseID" class="fieldInput" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="droplistID" class="label">Droplist ID (combat or shop):</label>
|
||||
<input class="field" type="text" size="30" id="droplistID" class="fieldInput"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>Stats</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="maxAP" class="label">Total AP:</label>
|
||||
<input class="field" type="text" size="5" id="maxAP" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="moveCost" class="label">Movement cost (AP):</label>
|
||||
<input class="field" type="text" size="5" id="moveCost" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="maxHP" class="label">Max HP:</label>
|
||||
<input class="field" type="text" size="5" id="maxHP" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasCombat" />Has combat traits</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasCombatDisplay">
|
||||
<legend>Combat</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackCost" class="label">Attack cost (AP):</label>
|
||||
<input class="field" type="text" size="5" id="attackCost" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackChance" class="label">Attack chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="attackChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackDamage" class="label">Attack damage (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="attackDamage_Min" class="fieldInput integer" /> - <input type="text" size="3" id="attackDamage_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasCritical" />Has critical chance</div>
|
||||
</div>
|
||||
<div id="hasCriticalDisplay">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="criticalChance" class="label">Critical chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="criticalChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="criticalMultiplier" class="label">Critical multiplier:</label>
|
||||
<input class="field" type="text" size="5" id="criticalMultiplier" class="fieldInput integer" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="blockChance" class="label">Block chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="blockChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="damageResistance" class="label">Damage resistance:</label>
|
||||
<input class="field" type="text" size="5" id="damageResistance" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasHitEffect" />Has effect on each hit</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasHitEffectDisplay">
|
||||
<legend>On every successful attack hit</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="onHit_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="onHit_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="onHit_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="onHit_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="onHit_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="onHit_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="onHit_conditionsSource" class="label">Conditions added to source</label>
|
||||
<table class="field" id="onHit_conditionsSource">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="onHit_conditionsTarget" class="label">Conditions added to target</label>
|
||||
<table class="field" id="onHit_conditionsTarget">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="endSets"> </div>
|
||||
</div>
|
||||
|
||||
<div id="dialog-onHitConditions">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="condition" class="label">Actor status effect id:</label>
|
||||
<input type="text" size="30" id="condition" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="magnitude" class="label">Magnitude:</label>
|
||||
<input type="text" size="5" id="magnitude" class="field fieldInput integer"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="duration" class="label">Duration (number of turns):</label>
|
||||
<input type="text" size="5" id="duration" class="field fieldInput integer"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="chance" class="label">Chance:</label>
|
||||
<select class="field" id="chance">
|
||||
<option value="100">Always</option>
|
||||
<option value="70">Frequent (70%)</option>
|
||||
<option value="50">Sometimes (50%)</option>
|
||||
<option value="20">Rare (20%)</option>
|
||||
<option value="10">Seldom (10%)</option>
|
||||
<option value="1">Very seldom (1%)</option>
|
||||
<option value="1/1000">Unique (0.1%)</option>
|
||||
<option value="1/10000">Legendary (0.01%)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Droplist editor -->
|
||||
|
||||
<div id="editDroplist">
|
||||
<h3>Droplist</h3>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>General</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="id" class="label">Droplist internal ID:</label>
|
||||
<input type="text" size="30" id="id" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="stages" class="label">Items dropped</label>
|
||||
<table class="field" id="items">
|
||||
<thead><tr>
|
||||
<th id="itemID">Item (id)</th>
|
||||
<th id="quantity">Quantity</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="endSets"> </div>
|
||||
</div>
|
||||
|
||||
<div id="dialog-droplistItem">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="itemID" class="label">Item (id):</label>
|
||||
<input type="text" size="30" id="itemID" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="quantity" class="label">Quantity:</label>
|
||||
<input type="text" size="7" id="quantity" class="field fieldInput integer"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="chance" class="label">Chance:</label>
|
||||
<select class="field" id="chance">
|
||||
<option value="100">Always</option>
|
||||
<option value="70">Frequent (70%)</option>
|
||||
<option value="50">Sometimes (50%)</option>
|
||||
<option value="20">Rare (20%)</option>
|
||||
<option value="10">Seldom (10%)</option>
|
||||
<option value="1">Very seldom (1%)</option>
|
||||
<option value="1/1000">Unique (0.1%)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Item editor -->
|
||||
|
||||
<div id="editItem">
|
||||
<h3>Item</h3>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>General</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<div id="itemimage" class="field"><input type="hidden" id="iconID" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="name" class="label">Display name:</label>
|
||||
<input class="field" type="text" size="30" id="name" class="fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="tags" class="label">Internal ID:</label>
|
||||
<input class="field" type="text" size="30" id="searchTag" class="fieldInput" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="category" class="label">Category:</label>
|
||||
<select class="field" id="category">
|
||||
<option value="0">Weapon</option>
|
||||
<option value="1">Shield</option>
|
||||
<option value="2">Wearable (head)</option>
|
||||
<option value="3">Wearable (body)</option>
|
||||
<option value="4">Wearable (hand)</option>
|
||||
<option value="5">Wearable (feet)</option>
|
||||
<option value="6">Wearable (neck)</option>
|
||||
<option value="7">Wearable (ring)</option>
|
||||
<option value="20">Potion</option>
|
||||
<option value="21">Food</option>
|
||||
<option value="30">Money</option>
|
||||
<option value="31">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="baseMarketCost" class="label">Base market cost (gold):</label>
|
||||
<input class="field" type="text" size="7" id="baseMarketCost" class="fieldInput integer" title="The actual price is adjusted 15% depending on if the item is being bought or sold. Set to 0 to prohibit selling this item type (for example, for a quest item)." />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasEquipEffect" />Has equip effect</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasUseEffect" />Has use effect</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasHitEffect" />Has effect on every hit</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasKillEffect" />Has effect on every kill</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasEquipEffectDisplay">
|
||||
<legend>When equipped</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_boostMaxHP" class="label">Boost max HP:</label>
|
||||
<input type="text" size="5" id="equip_boostMaxHP" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_boostMaxAP" class="label">Boost max AP:</label>
|
||||
<input type="text" size="5" id="equip_boostMaxAP" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_moveCostPenalty" class="label">Move cost penalty (AP):</label>
|
||||
<input type="text" size="5" id="equip_moveCostPenalty" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_attackCost" class="label">Attack cost (AP):</label>
|
||||
<input class="field" type="text" size="5" id="equip_attackCost" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_attackChance" class="label">Attack chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="equip_attackChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_attackDamage" class="label">Attack damage (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="equip_attackDamage_Min" class="fieldInput integer" /> - <input type="text" size="3" id="equip_attackDamage_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="equip_hasCritical" />Has critical chance</div>
|
||||
</div>
|
||||
<div id="equip_hasCriticalDisplay">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_criticalChance" class="label">Critical chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="equip_criticalChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_criticalMultiplier" class="label">Critical multiplier:</label>
|
||||
<input class="field" type="text" size="5" id="equip_criticalMultiplier" class="fieldInput integer" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_blockChance" class="label">Block chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="equip_blockChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_damageResistance" class="label">Damage resistance:</label>
|
||||
<input class="field" type="text" size="5" id="equip_damageResistance" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="equip_conditions" class="label">Apply status effects</label>
|
||||
<table class="field" id="equip_conditions">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasUseEffectDisplay">
|
||||
<legend>When used</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="use_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="use_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="use_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="use_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="use_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="use_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="use_conditionsSource" class="label">Apply status effects</label>
|
||||
<table class="field" id="use_conditionsSource">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasHitEffectDisplay">
|
||||
<legend>When making a successful hit</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="hit_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="hit_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="hit_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="hit_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="hit_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="hit_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="hit_conditionsSource" class="label">Apply status effects to source</label>
|
||||
<table class="field" id="hit_conditionsSource">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="hit_conditionsTarget" class="label">Apply status effects to target</label>
|
||||
<table class="field" id="hit_conditionsTarget">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasKillEffectDisplay">
|
||||
<legend>On every kill</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="kill_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="kill_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="kill_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="kill_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="kill_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="kill_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="kill_conditionsSource" class="label">Apply status effects</label>
|
||||
<table class="field" id="kill_conditionsSource">
|
||||
<thead><tr>
|
||||
<th id="condition">Condition</th>
|
||||
<th id="magnitude">Magnitude</th>
|
||||
<th id="duration">Duration</th>
|
||||
<th id="chance">Chance</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="endSets"> </div>
|
||||
</div>
|
||||
|
||||
<div id="dialog-equipConditions">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="condition" class="label">Actor status effect id:</label>
|
||||
<input type="text" size="30" id="condition" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="magnitude" class="label">Magnitude:</label>
|
||||
<input type="text" size="5" id="magnitude" class="field fieldInput integer"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Quest editor -->
|
||||
|
||||
<div id="editQuest">
|
||||
<h3>Quest</h3>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>General</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="id" class="label">Internal ID:</label>
|
||||
<input type="text" size="30" id="id" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="name" class="label">Display name:</label>
|
||||
<input type="text" size="30" id="name" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="showInLog" />Show in quest log</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>Stages</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="stages" class="label">Stages shown in quest log</label>
|
||||
<table class="field" id="stages">
|
||||
<thead><tr>
|
||||
<th id="progress">Progress</th>
|
||||
<th id="logText">Logtext</th>
|
||||
<th id="rewardExperience">Experience</th>
|
||||
<th id="finishesQuest">Finishes quest</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add">Add</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="endSets"> </div>
|
||||
</div>
|
||||
|
||||
<div id="dialog-questlog">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="progress" class="label">Progress stage (#):</label>
|
||||
<input type="text" size="4" id="progress" class="field fieldInput integer"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="logText" class="label">Logtext displayed in quest log:</label>
|
||||
<textarea rows="4" cols="40" id="logText"></textarea>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="rewardExperience" class="label">Experience reward for reaching this stage:</label>
|
||||
<input type="text" size="7" id="rewardExperience" class="field fieldInput integer"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="finishesQuest" />Finishes quest</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Actor effects editor -->
|
||||
|
||||
<div id="editActorEffect">
|
||||
<h3>Actor status effect</h3>
|
||||
<fieldset class="fieldSet">
|
||||
<legend>General</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<div id="statuseffectimage" class="field"><input type="hidden" id="iconID" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="id" class="label">Internal ID:</label>
|
||||
<input type="text" size="30" id="id" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="name" class="label">Display name:</label>
|
||||
<input type="text" size="30" id="name" class="field fieldInput"/>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="isStacking" />Allow stacked effects</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasRoundEffect" />Has effect every round</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasFullRoundEffect" />Has effect every full round</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasAbilityEffect" />Has constant ability effect</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasRoundEffectDisplay">
|
||||
<legend>Every round</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="round_visualEffectID" class="label">Visual effect:</label>
|
||||
<select class="field fieldInput" id="round_visualEffectID">
|
||||
<option value="-1">None</option>
|
||||
<option value="0">Red splatter</option>
|
||||
<option value="1">Blue swirl</option>
|
||||
<option value="2">Green splatter</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="round_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="round_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="round_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="round_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="round_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="round_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasFullRoundEffectDisplay">
|
||||
<legend>Every full round</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="fullround_visualEffectID" class="label">Visual effect:</label>
|
||||
<select class="field fieldInput" id="fullround_visualEffectID">
|
||||
<option value="-1">None</option>
|
||||
<option value="0">Red splatter</option>
|
||||
<option value="1">Blue swirl</option>
|
||||
<option value="2">Green splatter</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="fullround_boostHP" class="label">Boost current HP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="fullround_boostHP_Min" class="fieldInput" /> - <input type="text" size="3" id="fullround_boostHP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="fullround_boostAP" class="label">Boost current AP (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="fullround_boostAP_Min" class="fieldInput" /> - <input type="text" size="3" id="fullround_boostAP_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="fieldSet" id="hasAbilityEffectDisplay">
|
||||
<legend>Constant ability effect</legend>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="boostMaxHP" class="label">Boost max HP:</label>
|
||||
<input type="text" size="5" id="boostMaxHP" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="boostMaxAP" class="label">Boost max AP:</label>
|
||||
<input type="text" size="5" id="boostMaxAP" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="moveCostPenalty" class="label">Move cost penalty (AP):</label>
|
||||
<input type="text" size="5" id="moveCostPenalty" class="field fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackCost" class="label">Attack cost (AP):</label>
|
||||
<input class="field" type="text" size="5" id="attackCost" class="fieldInput integer" />
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackChance" class="label">Attack chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="attackChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="attackDamage" class="label">Attack damage (range):</label>
|
||||
<div class="field"><input type="text" size="3" id="attackDamage_Min" class="fieldInput integer" /> - <input type="text" size="3" id="attackDamage_Max" class="fieldInput integer" /></div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<div class="label"><input type="checkbox" id="hasCritical" />Has critical chance</div>
|
||||
</div>
|
||||
<div id="hasCriticalDisplay">
|
||||
<div class="fieldWithLabel">
|
||||
<label for="criticalChance" class="label">Critical chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="criticalChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="criticalMultiplier" class="label">Critical multiplier:</label>
|
||||
<input class="field" type="text" size="5" id="criticalMultiplier" class="fieldInput integer" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="blockChance" class="label">Block chance:</label>
|
||||
<div class="field"><input type="text" size="5" id="blockChance" class="fieldInput integer" />%</div>
|
||||
</div>
|
||||
<div class="fieldWithLabel">
|
||||
<label for="damageResistance" class="label">Damage resistance:</label>
|
||||
<input class="field" type="text" size="5" id="damageResistance" class="fieldInput integer" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="endSets"> </div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Import / Export dialog form -->
|
||||
|
||||
<div id="dialog-importexport">
|
||||
<div id="statuseffects">
|
||||
<div class="importexport-header">Actor status effects</div>
|
||||
<div id="description">This data corresponds to res/values/actorstatuseffects.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
|
||||
<div id="quests">
|
||||
<div class="importexport-header">Quests</div>
|
||||
<div id="description">This data corresponds to res/values/questlist.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
|
||||
<div id="items">
|
||||
<div class="importexport-header">Items</div>
|
||||
<div id="description">This data corresponds to res/values/itemlist.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
|
||||
<div id="droplists">
|
||||
<div class="importexport-header">Droplists</div>
|
||||
<div id="description">This data corresponds to res/values/droplist.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
|
||||
<div id="dialogue">
|
||||
<div class="importexport-header">Dialogue</div>
|
||||
<div id="description">This data corresponds to res/values/conversationlist.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
|
||||
<div id="monsters">
|
||||
<div class="importexport-header">Monsters</div>
|
||||
<div id="description">This data corresponds to res/values/monsters.xml in the source code.</div>
|
||||
<textarea id="value" rows="6" cols="80"></textarea>
|
||||
<div id="import">Import</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Select images dialog -->
|
||||
|
||||
<div id="dialog-images"> </div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(startEditor);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
7
AndorsTrailEdit/inc/jquery.shorten.min.js
vendored
Normal file
7
AndorsTrailEdit/inc/jquery.shorten.min.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Shorten, a jQuery plugin to automatically shorten text to fit in a block or a pre-set width and configure how the text ends.
|
||||
* Copyright (C) 2009-2011 Marc Diethelm
|
||||
* License: (GPL 3, http://www.gnu.org/licenses/gpl-3.0.txt) see license.txt
|
||||
*/
|
||||
|
||||
(function(a){function s(g,c){return c.measureText(g).width}function t(g,c){c.text(g);return c.width()}var q=false,o,j,k;a.fn.shorten=function(){var g={},c=arguments,r=c.callee;if(c.length)if(c[0].constructor==Object)g=c[0];else if(c[0]=="options")return a(this).eq(0).data("shorten-options");else g={width:parseInt(c[0]),tail:c[1]};this.css("visibility","hidden");var h=a.extend({},r.defaults,g);return this.each(function(){var e=a(this),d=e.text(),p=d.length,i,f=a("<span/>").html(h.tail).text(),l={shortened:false, textOverflow:false};i=e.css("float")!="none"?h.width||e.width():h.width||e.parent().width();if(i<0)return true;e.data("shorten-options",h);this.style.display="block";this.style.whiteSpace="nowrap";if(o){var b=a(this),n=document.createElement("canvas");ctx=n.getContext("2d");b.html(n);ctx.font=b.css("font-style")+" "+b.css("font-variant")+" "+b.css("font-weight")+" "+Math.ceil(parseFloat(b.css("font-size")))+"px "+b.css("font-family");j=ctx;k=s}else{b=a('<table style="padding:0; margin:0; border:none; font:inherit;width:auto;zoom:1;position:absolute;"><tr style="padding:0; margin:0; border:none; font:inherit;"><td style="padding:0; margin:0; border:none; font:inherit;white-space:nowrap;"></td></tr></table>'); $td=a("td",b);a(this).html(b);j=$td;k=t}b=k.call(this,d,j);if(b<i){e.text(d);this.style.visibility="visible";e.data("shorten-info",l);return true}h.tooltip&&this.setAttribute("title",d);if(r._native&&!g.width){n=a("<span>"+h.tail+"</span>").text();if(n.length==1&&n.charCodeAt(0)==8230){e.text(d);this.style.overflow="hidden";this.style[r._native]="ellipsis";this.style.visibility="visible";l.shortened=true;l.textOverflow="ellipsis";e.data("shorten-info",l);return true}}f=k.call(this,f,j);i-=f;f=i*1.15; if(b-f>0){f=d.substring(0,Math.ceil(p*(f/b)));if(k.call(this,f,j)>i){d=f;p=d.length}}do{p--;d=d.substring(0,p)}while(k.call(this,d,j)>=i);e.html(a.trim(a("<span/>").text(d).html())+h.tail);this.style.visibility="visible";l.shortened=true;e.data("shorten-info",l);return true})};var m=document.documentElement.style;if("textOverflow"in m)q="textOverflow";else if("OTextOverflow"in m)q="OTextOverflow";if(typeof Modernizr!="undefined"&&Modernizr.canvastext)o=Modernizr.canvastext;else{m=document.createElement("canvas"); o=!!(m.getContext&&m.getContext("2d")&&typeof m.getContext("2d").fillText==="function")}a.fn.shorten._is_canvasTextSupported=o;a.fn.shorten._native=q;a.fn.shorten.defaults={tail:"…",tooltip:true}})(jQuery);
|
||||
1442
AndorsTrailEdit/inc/qunit.js
Normal file
1442
AndorsTrailEdit/inc/qunit.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
AndorsTrailEdit/logo.png
Normal file
BIN
AndorsTrailEdit/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
35
AndorsTrailEdit/styles2.css
Normal file
35
AndorsTrailEdit/styles2.css
Normal file
@@ -0,0 +1,35 @@
|
||||
html,body { margin:0; padding:0; height: 100%; font-size: 0.9em; }
|
||||
#screen { height: 100%; background-color: #a0c0ff; color: black; position: relative; font-family: sans-serif; }
|
||||
#screen #top { height: 50px; color: #eeeeee; }
|
||||
#screen #top #title { font-size: 2em; float: left; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; }
|
||||
#screen #top #version { font-size: 10pt; margin-left: 3ex; }
|
||||
.andorsTrailLogo { background-image: url(logo.png); background-repeat: no-repeat; background-position: 3px 3px; padding:10px 0px 15px 50px; }
|
||||
#screen #top #buttons { float: right; font-size: 1.2em; margin: 10px; }
|
||||
#screen .workarea { margin: 5px; border: 1px #c0d0ff solid; background-color: #eeeeee; position: absolute; top: 50px; bottom: 0; }
|
||||
#screen #left { width: 250px; padding: 0px; font-size: 1em; }
|
||||
#screen #center { left: 256px; right: 0; }
|
||||
#screen #center #tabs { border: 0px; padding: 0px; }
|
||||
|
||||
.ui-icon-close { float: left; margin: 0.4em 0.2em 0 0; cursor: pointer; }
|
||||
.hidden { display: none; }
|
||||
|
||||
.fieldWithLabel { font-size: 0.9em; }
|
||||
.fieldWithLabel .label { display: block; margin-top: 1ex; }
|
||||
.fieldWithLabel .field { display: block; }
|
||||
.fieldWithLabel .hint { }
|
||||
.fieldWithLabel .fieldInput { }
|
||||
.fieldSet { float: left; margin: 1ex; background-color: #f7f7ff; }
|
||||
.endSets { clear: both; }
|
||||
|
||||
#screen #left div { }
|
||||
.itemlist { border: 1px #bbb solid; list-style: none; padding: 0px; margin: 0px; line-height: 1.3; margin-bottom: 2ex; }
|
||||
.itemlist li { cursor: pointer; padding: 0.5ex 1ex 0.5ex 2ex; }
|
||||
.itemlist li:nth-child(odd) { background-color: #eee; border-top: 1px #ddd solid; border-bottom: 1px #ddd solid; }
|
||||
.itemlist li:hover { background-color: #d7d7ff; }
|
||||
.importexport-header { font-weight: bold; padding-top: 1ex; }
|
||||
#dialog-importexport #value { font-family: monospace; font-size: 7pt; }
|
||||
.fieldWithLabel table { border-collapse: collapse; margin-bottom: 1ex; }
|
||||
.fieldWithLabel table th { border: 1px #ccc solid; padding: 0.5ex; }
|
||||
.fieldWithLabel table td { border: 1px #ccc solid; padding: 0.5ex; cursor: pointer; }
|
||||
.fieldWithLabel table tbody tr:nth-child(even) { background-color: #eee }
|
||||
.fieldWithLabel table tbody tr:hover { background-color: #d7d7ff; }
|
||||
464
AndorsTrailEdit/unittest.html
Normal file
464
AndorsTrailEdit/unittest.html
Normal file
@@ -0,0 +1,464 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Andor's Trail editor unit tests</title>
|
||||
<link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css" type="text/css" media="screen" />
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js"></script>
|
||||
<script type="text/javascript" src="AndorsTrailEditor.js"></script>
|
||||
<script type="text/javascript" src="inc/qunit.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("FieldList");
|
||||
|
||||
test("Deserialization of header", function() {
|
||||
var header = "[Id|Name|Spells[SpellID|SpellName|]|Size|]";
|
||||
var ds = new FieldList(header);
|
||||
equals(ds._fields.length, 4);
|
||||
equals(ds._fields[0], "Id");
|
||||
equals(ds._fields[1], "Name");
|
||||
equals(ds._fields[2]._name, "Spells");
|
||||
equals(ds._fields[2]._fields.length, 2);
|
||||
equals(ds._fields[2]._fields[0], "SpellID");
|
||||
equals(ds._fields[2]._fields[1], "SpellName");
|
||||
equals(ds._fields[3], "Size");
|
||||
});
|
||||
|
||||
test("Deserialization of header with multiple lists", function() {
|
||||
var header = "[Id|Name|Spells[SpellID|SpellName|]|Size|Drops[ItemID|]|]";
|
||||
var ds = new FieldList(header);
|
||||
equals(ds._fields.length, 5);
|
||||
equals(ds._fields[0], "Id");
|
||||
equals(ds._fields[1], "Name");
|
||||
equals(ds._fields[2]._name, "Spells");
|
||||
equals(ds._fields[2]._fields.length, 2);
|
||||
equals(ds._fields[2]._fields[0], "SpellID");
|
||||
equals(ds._fields[2]._fields[1], "SpellName");
|
||||
equals(ds._fields[3], "Size");
|
||||
equals(ds._fields[4]._name, "Drops");
|
||||
equals(ds._fields[4]._fields.length, 1);
|
||||
equals(ds._fields[4]._fields[0], "ItemID");
|
||||
});
|
||||
|
||||
test("Serialization of header", function() {
|
||||
var header = "[Id|Name|Spells[SpellID|SpellName|]|Size|]";
|
||||
var ds = new FieldList(header);
|
||||
equals(ds.getHeader(), header);
|
||||
equals(ds.getHeaderLine(), header + ";");
|
||||
});
|
||||
|
||||
test("Finding header in text", function() {
|
||||
var ds = findHeader("[Id|Name|[SpellID|SpellName|]|Size|];");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
|
||||
ds = findHeader("[Id|Name|[SpellID|SpellName|]|Size|]");
|
||||
ok(!ds);
|
||||
|
||||
ds = findHeader("\n[Id|Name|[SpellID|SpellName|]|Size|];");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
|
||||
ds = findHeader("\ntest\n[Id|Name|[SpellID|SpellName|]|Size|];");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
|
||||
ds = findHeader("\ntest\n[Id|Name|[SpellID|SpellName|]|Size|];\ntest2\n");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
|
||||
ds = findHeader("\ntest\n{Test|Foo|};\n[Id|Name|[SpellID|SpellName|]|Size|];\ntest2\n");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
|
||||
ds = findHeader("\ntest\n[Id|Name|[SpellID|SpellName|]|Size|];\n{Test|Foo|};\n");
|
||||
ok(ds);
|
||||
equals(ds._fields.length, 4);
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("deserializeObject");
|
||||
|
||||
test("Simple object deserialization", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = deserializeObject(ds, "{23|TestObject|50|}");
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
equals(obj.SpellID, 50);
|
||||
});
|
||||
|
||||
test("Hierarcical object deserialization", function() {
|
||||
var ds = new FieldList("[Id|Name|Spells[SpellID|SpellName|]|Size|]");
|
||||
var obj = deserializeObject(ds, "{23|TestObject|{{50|Heal|}{51|Curse|}}|Large|}");
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
ok(obj.Spells);
|
||||
equals(obj.Spells.length, 2);
|
||||
equals(obj.Spells[0].SpellID, 50);
|
||||
equals(obj.Spells[0].SpellName, "Heal");
|
||||
equals(obj.Spells[1].SpellID, 51);
|
||||
equals(obj.Spells[1].SpellName, "Curse");
|
||||
equals(obj.Size, "Large");
|
||||
});
|
||||
|
||||
test("Multiple hierarcical object deserialization", function() {
|
||||
var ds = new FieldList("[Id|Name|Spells[SpellID|SpellName|]|Size|Drops[ItemID|]|Effects[EffectID|]|]");
|
||||
var obj = deserializeObject(ds, "{23|TestObject|{{50|Heal|}{51|Curse|}}|Large|{{4|}}||}");
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
ok(obj.Spells);
|
||||
equals(obj.Spells.length, 2);
|
||||
equals(obj.Spells[0].SpellID, 50);
|
||||
equals(obj.Spells[0].SpellName, "Heal");
|
||||
equals(obj.Spells[1].SpellID, 51);
|
||||
equals(obj.Spells[1].SpellName, "Curse");
|
||||
equals(obj.Size, "Large");
|
||||
ok(obj.Drops);
|
||||
equals(obj.Drops.length, 1);
|
||||
equals(obj.Drops[0].ItemID, 4);
|
||||
ok(obj.Effects);
|
||||
equals(obj.Effects.length, 0);
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("serializeObject");
|
||||
|
||||
test("Simple object serialization", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 23;
|
||||
obj.Name = "TestObject";
|
||||
obj.SpellID = 50;
|
||||
|
||||
equals(serializeObject(ds, obj), "{23|TestObject|50|}");
|
||||
});
|
||||
|
||||
test("Hierarcical object serialization", function() {
|
||||
var ds = new FieldList("[Id|Name|Spells[SpellID|SpellName|]|Size|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 23;
|
||||
obj.Name = "TestObject";
|
||||
obj.Spells = [];
|
||||
obj.Spells[0] = new Object();
|
||||
obj.Spells[0].SpellID = 50;
|
||||
obj.Spells[0].SpellName = "Heal";
|
||||
obj.Spells[1] = new Object();
|
||||
obj.Spells[1].SpellID = 51;
|
||||
obj.Spells[1].SpellName = "Curse";
|
||||
obj.Size = "Large";
|
||||
|
||||
equals(serializeObject(ds, obj), "{23|TestObject|{{50|Heal|}{51|Curse|}}|Large|}");
|
||||
});
|
||||
|
||||
test("Multiple hierarcical object serialization", function() {
|
||||
var ds = new FieldList("[Id|Name|Spells[SpellID|SpellName|]|Size|Drops[ItemID|]|Effects[EffectID|]|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 23;
|
||||
obj.Name = "TestObject";
|
||||
obj.Spells = [];
|
||||
obj.Spells[0] = new Object();
|
||||
obj.Spells[0].SpellID = 50;
|
||||
obj.Spells[0].SpellName = "Heal";
|
||||
obj.Spells[1] = new Object();
|
||||
obj.Spells[1].SpellID = 51;
|
||||
obj.Spells[1].SpellName = "Curse";
|
||||
obj.Size = "Large";
|
||||
obj.Drops = [];
|
||||
obj.Drops[0] = new Object();
|
||||
obj.Drops[0].ItemID = 4;
|
||||
obj.Effects = [];
|
||||
|
||||
equals(serializeObject(ds, obj), "{23|TestObject|{{50|Heal|}{51|Curse|}}|Large|{{4|}}||}");
|
||||
});
|
||||
|
||||
test("Object serialization with undefined fields", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = new Object();
|
||||
obj.Name = "TestObject";
|
||||
obj.SpellID = 50;
|
||||
|
||||
equals(serializeObject(ds, obj), "{|TestObject|50|}");
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("serializeObjectList");
|
||||
|
||||
test("Simple object serialization", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var list = [];
|
||||
var obj = new Object();
|
||||
obj.Id = 23;
|
||||
obj.Name = "TestObject";
|
||||
obj.SpellID = 50;
|
||||
list[0] = obj;
|
||||
obj = new Object();
|
||||
obj.Id = 25;
|
||||
obj.Name = "Foo";
|
||||
obj.SpellID = 9;
|
||||
list[1] = obj;
|
||||
|
||||
equals(serializeObjectList(ds, list), "[Id|Name|SpellID|];\n{23|TestObject|50|};\n{25|Foo|9|};\n");
|
||||
});
|
||||
|
||||
test("Serialize empty list", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var list = [];
|
||||
equals(serializeObjectList(ds, list), "[Id|Name|SpellID|];\n");
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("deserializeObjectList");
|
||||
|
||||
test("Simple object deserialization", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var list = deserializeObjectList(ds, "{23|TestObject|50|};\n{25|Foo|9|};");
|
||||
ok(list);
|
||||
equals(list.length, 2);
|
||||
|
||||
var obj = list[0];
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
equals(obj.SpellID, 50);
|
||||
|
||||
var obj = list[1];
|
||||
ok(obj);
|
||||
equals(obj.Id, 25);
|
||||
equals(obj.Name, "Foo");
|
||||
equals(obj.SpellID, 9);
|
||||
});
|
||||
|
||||
test("Deserialize empty list", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var list = deserializeObjectList(ds, "");
|
||||
ok(list);
|
||||
equals(list.length, 0);
|
||||
|
||||
list = deserializeObjectList(ds, "test value};");
|
||||
ok(list);
|
||||
equals(list.length, 0);
|
||||
});
|
||||
|
||||
test("Deserialize garbage values", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var list = deserializeObjectList(ds, "test value};");
|
||||
ok(list);
|
||||
equals(list.length, 0);
|
||||
|
||||
list = deserializeObjectList(ds, "{23|TestObject|50|};\nfoo\n\n{25|Foo|9|};\nbaz\n");
|
||||
ok(list);
|
||||
equals(list.length, 2);
|
||||
|
||||
var obj = list[0];
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
equals(obj.SpellID, 50);
|
||||
|
||||
var obj = list[1];
|
||||
ok(obj);
|
||||
equals(obj.Id, 25);
|
||||
equals(obj.Name, "Foo");
|
||||
equals(obj.SpellID, 9);
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("Combined serialization");
|
||||
|
||||
test("Serialization -> deserialization", function() {
|
||||
var ds = new FieldList("[Id|Name|Spells[SpellID|SpellName|]|Size|Drops[ItemID|]|Effects[EffectID|]|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 23;
|
||||
obj.Name = "TestObject";
|
||||
obj.Spells = [];
|
||||
obj.Spells[0] = new Object();
|
||||
obj.Spells[0].SpellID = 50;
|
||||
obj.Spells[0].SpellName = "Heal";
|
||||
obj.Spells[1] = new Object();
|
||||
obj.Spells[1].SpellID = 51;
|
||||
obj.Spells[1].SpellName = "Curse";
|
||||
obj.Size = "Large";
|
||||
obj.Drops = [];
|
||||
obj.Drops[0] = new Object();
|
||||
obj.Drops[0].ItemID = 4;
|
||||
obj.Effects = [];
|
||||
|
||||
|
||||
var obj = deserializeObject(ds, serializeObject(ds, obj));
|
||||
ok(obj);
|
||||
equals(obj.Id, 23);
|
||||
equals(obj.Name, "TestObject");
|
||||
ok(obj.Spells);
|
||||
equals(obj.Spells.length, 2);
|
||||
equals(obj.Spells[0].SpellID, 50);
|
||||
equals(obj.Spells[0].SpellName, "Heal");
|
||||
equals(obj.Spells[1].SpellID, 51);
|
||||
equals(obj.Spells[1].SpellName, "Curse");
|
||||
equals(obj.Size, "Large");
|
||||
ok(obj.Drops);
|
||||
equals(obj.Drops.length, 1);
|
||||
equals(obj.Drops[0].ItemID, 4);
|
||||
ok(obj.Effects);
|
||||
equals(obj.Effects.length, 0);
|
||||
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("Regex test");
|
||||
|
||||
test("test1", function() {
|
||||
var expression = /'/gm;
|
||||
var str = "Test'value'";
|
||||
|
||||
var str2 = str.replace(expression, '_');
|
||||
equals(str2, "Test_value_");
|
||||
|
||||
str2 = str.replace(expression, "\\'");
|
||||
equals(str2, "Test\\'value\\'");
|
||||
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("Serialization and deserialization with special characters");
|
||||
|
||||
test("Serialization of newline", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 3;
|
||||
obj.Name = "Test\nObject\nValue";
|
||||
obj.SpellID = 50;
|
||||
|
||||
equals(serializeObject(ds, obj), "{3|Test\\nObject\\nValue|50|}");
|
||||
});
|
||||
|
||||
test("Deserialization of newline", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = deserializeObject(ds, "{3|Test\\nObject\\nValue|50|}");
|
||||
ok(obj);
|
||||
equals(obj.Id, 3);
|
||||
equals(obj.Name, "Test\nObject\nValue");
|
||||
equals(obj.SpellID, 50);
|
||||
});
|
||||
|
||||
test("Serialization of single quote", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = new Object();
|
||||
obj.Id = 3;
|
||||
obj.Name = "Test 'Object'";
|
||||
obj.SpellID = 50;
|
||||
|
||||
equals(serializeObject(ds, obj), "{3|Test \\'Object\\'|50|}");
|
||||
});
|
||||
|
||||
test("Deserialization of single quote", function() {
|
||||
var ds = new FieldList("[Id|Name|SpellID|]");
|
||||
var obj = deserializeObject(ds, "{3|Test \\'Object\\'|50|}");
|
||||
ok(obj);
|
||||
equals(obj.Id, 3);
|
||||
equals(obj.Name, "Test 'Object'");
|
||||
equals(obj.SpellID, 50);
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("DataStore");
|
||||
|
||||
test("Getting and putting items", function() {
|
||||
var ds = new DataStore('item', new FieldList("[iconID|name|];"));
|
||||
equals(ds.items.length, 0);
|
||||
ds.add({iconID: 3, name: 'Test'});
|
||||
equals(ds.items.length, 1);
|
||||
equals(ds.items[0].iconID, 3);
|
||||
equals(ds.items[0].name, 'Test');
|
||||
});
|
||||
|
||||
test("Serialization of datastore", function() {
|
||||
var ds = new DataStore('item', new FieldList("[iconID|name|];"));
|
||||
ds.add({iconID: 3, name: 'Test'});
|
||||
equals(ds.serialize(), '[iconID|name|];\n{3|Test|};\n');
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
|
||||
module("TilesetImage");
|
||||
|
||||
test("Image bounds", function() {
|
||||
var img = new TilesetImage("defaultimage.png", { x:1, y:1 });
|
||||
equals(img._tileSize.x, 32);
|
||||
equals(img._tileSize.y, 32);
|
||||
|
||||
var c = img.localIDToCoords(0);
|
||||
equals(img.coordsToLocalID(c.x, c.y), 0);
|
||||
equals(c.x, 0);
|
||||
equals(c.y, 0);
|
||||
equals(img.coordsToLocalID(0, 0), 0);
|
||||
equals(img.coordsToLocalID(1, 1), 0);
|
||||
equals(img.coordsToLocalID(10, 10), 0);
|
||||
equals(img.coordsToLocalID(31, 31), 0);
|
||||
|
||||
img = new TilesetImage("defaultimage.png", { x:10, y:2 });
|
||||
c = img.localIDToCoords(0);
|
||||
equals(img.coordsToLocalID(c.x, c.y), 0);
|
||||
equals(c.x, 0);
|
||||
equals(c.y, 0);
|
||||
equals(img.coordsToLocalID(0, 0), 0);
|
||||
equals(img.coordsToLocalID(1, 1), 0);
|
||||
equals(img.coordsToLocalID(10, 10), 0);
|
||||
equals(img.coordsToLocalID(31, 31), 0);
|
||||
|
||||
c = img.localIDToCoords(1);
|
||||
equals(img.coordsToLocalID(c.x, c.y), 1);
|
||||
equals(c.x, 32);
|
||||
equals(c.y, 0);
|
||||
equals(img.coordsToLocalID(32, 0), 1);
|
||||
equals(img.coordsToLocalID(33, 1), 1);
|
||||
equals(img.coordsToLocalID(40, 10), 1);
|
||||
equals(img.coordsToLocalID(63, 31), 1);
|
||||
|
||||
c = img.localIDToCoords(10);
|
||||
equals(img.coordsToLocalID(c.x, c.y), 10);
|
||||
equals(c.x, 0);
|
||||
equals(c.y, 32);
|
||||
equals(img.coordsToLocalID(0, 32), 10);
|
||||
equals(img.coordsToLocalID(1, 33), 10);
|
||||
equals(img.coordsToLocalID(10, 40), 10);
|
||||
equals(img.coordsToLocalID(31, 63), 10);
|
||||
|
||||
c = img.localIDToCoords(12);
|
||||
equals(img.coordsToLocalID(c.x, c.y), 12);
|
||||
equals(c.x, 64);
|
||||
equals(c.y, 32);
|
||||
equals(img.coordsToLocalID(64, 32), 12);
|
||||
equals(img.coordsToLocalID(65, 33), 12);
|
||||
equals(img.coordsToLocalID(70, 40), 12);
|
||||
equals(img.coordsToLocalID(95, 63), 12);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="qunit-header">Andor's Trail editor unit tests</h1>
|
||||
<h2 id="qunit-banner"></h2>
|
||||
<div id="qunit-testrunner-toolbar"></div>
|
||||
<h2 id="qunit-userAgent"></h2>
|
||||
<ol id="qunit-tests"></ol>
|
||||
<div id="qunit-fixture">test markup, will be hidden</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user