diff options
Diffstat (limited to 'internal/admin/static')
| -rw-r--r-- | internal/admin/static/dropeditor.js | 55 | ||||
| -rw-r--r-- | internal/admin/static/editor.js | 45 | ||||
| -rw-r--r-- | internal/admin/static/hazardeditor.js | 63 | ||||
| -rw-r--r-- | internal/admin/static/itemeditor.js | 90 | ||||
| -rw-r--r-- | internal/admin/static/mobeditor.js | 81 | ||||
| -rw-r--r-- | internal/admin/static/moduleeditor.js | 55 | ||||
| -rw-r--r-- | internal/admin/static/objecteditor.js | 64 | ||||
| -rw-r--r-- | internal/admin/static/techeditor.js | 71 |
8 files changed, 409 insertions, 115 deletions
diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js index c5da314..a6537e9 100644 --- a/internal/admin/static/dropeditor.js +++ b/internal/admin/static/dropeditor.js @@ -7,14 +7,14 @@ var DROP_SECTIONS = [ { id: 'drops', label: 'Drops', always: true, path: 'drops', isArray: true, fields: [ - ['item_id', 'Item ID', 'search', '', '', null, 'items'], - ['table', 'Table', 'search', '', '', null, 'drops'], - ['weight', 'Weight', 'number', '', 'narrow'], + ['item_id', 'Item ID', 'search', 'e.g. copper_ore', '', null, 'items'], + ['table', 'Table', 'search', 'e.g. gem_table', '', null, 'drops'], + ['weight', 'Weight', 'number', '10', 'narrow'], ['quantity', 'Quantity', 'number', '1', 'narrow'], - ['level', 'Level', 'number', '', 'narrow'], - ['xp', 'XP', 'number', '', 'narrow'], + ['level', 'Level', 'number', '1', 'narrow'], + ['xp', 'XP', 'number', '5', 'narrow'], ['depletes', 'Depletes', 'checkbox'], - ['success_message', 'Success Message', 'text', '', 'wide'], + ['success_message', 'Success Message', 'text', 'You get some %n.', 'wide'], ] } ]; @@ -75,6 +75,7 @@ function renderDropEditor(id, data) { html += '<div class="btn-row">'; html += '<button class="btn btn-primary" onclick="saveDrop()">Save</button>'; + html += '<button class="btn" onclick="duplicateDrop()">Duplicate</button>'; html += '<button class="btn btn-danger" onclick="deleteDrop()">Delete</button>'; html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>'; html += '</div>'; @@ -226,3 +227,45 @@ function deleteDrop() { loadList(); }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); } + +function duplicateDrop() { + var baseID = dropID + '_copy'; + var newID = baseID; + var counter = 2; + while (true) { + var exists = false; + for (var i = 0; i < allIDs.length; i++) { + if (allIDs[i] === newID) { exists = true; break; } + } + if (!exists) break; + newID = baseID + '_' + counter; + counter++; + } + + var copy = JSON.parse(JSON.stringify(dropData)); + delete copy._raw; + delete copy._path; + copy.id = newID; + + var dir = ''; + if (dropData._path) { + var parts = dropData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'drops') dir = parentDir; + } + + API.post('/api/drops', {id: newID}).then(function() { + var putOp = API.put('/api/drops/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/drops/move', {id: newID, dir: dir}); + }); + } + return putOp; + }).then(function() { + notify('Duplicated as ' + newID, 'success'); + updateUndoBar(); + loadList(); + loadDropEditor(newID); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); +} diff --git a/internal/admin/static/editor.js b/internal/admin/static/editor.js index a9c1e1c..50d86f1 100644 --- a/internal/admin/static/editor.js +++ b/internal/admin/static/editor.js @@ -340,12 +340,57 @@ function showItemContextMenu(e, id) { } addBtn('Rename', '', function() { startInlineItemRename(id); }); + addBtn('Duplicate', '', function() { duplicateFromMenu(id); }); addBtn('Delete', 'danger', function() { deleteItem(id); }); document.body.appendChild(overlay); document.body.appendChild(menu); } +function duplicateFromMenu(id) { + API.get('/api/' + editorType + '/' + encodeURIComponent(id)).then(function(data) { + var dir = ''; + if (data._path) { + var parts = data._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== editorType) dir = parentDir; + } + var clean = JSON.parse(JSON.stringify(data)); + delete clean._raw; + delete clean._path; + clean.id = ''; + + var baseID = id + '_copy'; + var newID = baseID; + var counter = 2; + while (true) { + var exists = false; + for (var i = 0; i < allIDs.length; i++) { + if (allIDs[i] === newID) { exists = true; break; } + } + if (!exists) break; + newID = baseID + '_' + counter; + counter++; + } + + clean.id = newID; + API.post('/api/' + editorType, {id: newID}).then(function() { + var putOp = API.put('/api/' + editorType + '/' + encodeURIComponent(newID), clean); + if (dir) { + return putOp.then(function() { + return API.post('/api/' + editorType + '/move', {id: newID, dir: dir}); + }); + } + return putOp; + }).then(function() { + notify('Duplicated as ' + newID, 'success'); + updateUndoBar(); + loadList(); + loadItem(newID); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); +} + function closeContextMenu() { var o = document.getElementById('_ctxMenuOverlay'); var m = document.getElementById('_ctxMenu'); diff --git a/internal/admin/static/hazardeditor.js b/internal/admin/static/hazardeditor.js index ff2857d..c4eb0b5 100644 --- a/internal/admin/static/hazardeditor.js +++ b/internal/admin/static/hazardeditor.js @@ -8,13 +8,13 @@ var HAZARD_SECTIONS = [ { id: 'core', label: 'Core', always: true, fields: [ - ['name', 'Name', 'text'], - ['description', 'Description', 'textarea', '', 'wide'], - ['warn_message', 'Warn Message', 'text', '', 'wide'], - ['hit_message', 'Hit Message', 'text'], - ['miss_message', 'Miss Message', 'text'], - ['required_item', 'Required Item', 'text'], - ['speed', 'Speed', 'number', '', 'narrow'], + ['name', 'Name', 'text', 'toxic gas'], + ['description', 'Description', 'textarea', 'A cloud of toxic gas fills the room.', 'wide'], + ['warn_message', 'Warn Message', 'text', 'The air grows thick with toxic fumes...', 'wide'], + ['hit_message', 'Hit Message', 'text', 'You cough and choke on the toxic gas!'], + ['miss_message', 'Miss Message', 'text', 'You hold your breath and avoid the fumes.'], + ['required_item', 'Required Item', 'text', 'e.g. gas_mask'], + ['speed', 'Speed', 'number', '10', 'narrow'], ] }, { @@ -22,9 +22,9 @@ var HAZARD_SECTIONS = [ detect: function(d) { return d.attack_type || d.attack || d.attack_bonus || d.max_hit; }, fields: [ ['attack_type', 'Attack Type', 'select', 'stab|slash|crush|ranged|science'], - ['attack', 'Attack', 'number', '', 'narrow'], - ['attack_bonus', 'Attack Bonus', 'number', '', 'narrow'], - ['max_hit', 'Max Hit', 'number', '', 'narrow'], + ['attack', 'Attack', 'number', '5', 'narrow'], + ['attack_bonus', 'Attack Bonus', 'number', '0', 'narrow'], + ['max_hit', 'Max Hit', 'number', '3', 'narrow'], ] } ]; @@ -99,6 +99,7 @@ function renderHazardEditor(id, data) { html += '<div class="btn-row">'; html += '<button class="btn btn-primary" onclick="saveHazard()">Save</button>'; + html += '<button class="btn" onclick="duplicateHazard()">Duplicate</button>'; html += '<button class="btn btn-danger" onclick="deleteHazard()">Delete</button>'; html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>'; html += '</div>'; @@ -228,3 +229,45 @@ function deleteHazard() { loadList(); }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); } + +function duplicateHazard() { + var baseID = hazardID + '_copy'; + var newID = baseID; + var counter = 2; + while (true) { + var exists = false; + for (var i = 0; i < allIDs.length; i++) { + if (allIDs[i] === newID) { exists = true; break; } + } + if (!exists) break; + newID = baseID + '_' + counter; + counter++; + } + + var copy = JSON.parse(JSON.stringify(hazardData)); + delete copy._raw; + delete copy._path; + copy.id = newID; + + var dir = ''; + if (hazardData._path) { + var parts = hazardData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'hazards') dir = parentDir; + } + + API.post('/api/hazards', {id: newID}).then(function() { + var putOp = API.put('/api/hazards/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/hazards/move', {id: newID, dir: dir}); + }); + } + return putOp; + }).then(function() { + notify('Duplicated as ' + newID, 'success'); + updateUndoBar(); + loadList(); + loadHazardEditor(newID); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); +} diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js index ac76f94..ed32267 100644 --- a/internal/admin/static/itemeditor.js +++ b/internal/admin/static/itemeditor.js @@ -10,9 +10,9 @@ var SECTIONS = [ fields: [ ['name', 'Name', 'text', 'Copper Ore'], ['color', 'Color', 'color', 'B2'], - ['value', 'Value', 'number', '0'], + ['value', 'Value', 'number', '100'], ['stackable', 'Stackable', 'checkbox'], - ['max_quality', 'Quality', 'number', ''], + ['max_quality', 'Quality', 'number', '100', 'narrow'], ['aliases', 'Aliases', 'text', 'comma separated'], ['description', 'Description', 'textarea', 'A chunk of copper ore.'], ] @@ -55,7 +55,7 @@ var SECTIONS = [ ['output_qty', 'Output Qty', 'number', '', 'narrow'], ['station', 'Station', 'select', '|anvil|cooking_range|fire|furnace|pottery_oven|pottery_wheel|spinning_wheel|workbench'], ['tool', 'Tool', 'select', '|pickaxe|axe|fire|chisel|hammer|knife|needle|rake|saw|shears|spade|watering_can|fishing_rod|fly_rod|harpoon|small_net|big_net|lobster_pot'], - ['fail', 'Fail Item', 'search', '', '', null, 'items'], + ['fail', 'Fail Item', 'search', 'e.g. burnt_fish', '', null, 'items'], ['success_base', 'Base', 'number', '0.5', 'narrow'], ['success_per_level', 'Per Lvl', 'number', '0.01', 'narrow'], ['success_cap', 'Cap', 'number', '0.95', 'narrow'], @@ -76,8 +76,8 @@ var SECTIONS = [ ['quantity', 'Qty', 'number', '1', 'narrow'], ]}, {label:'Steps', key:'steps', fields:[ - ['delay', 'Delay (ticks)', 'number', '', 'narrow'], - ['message', 'Message', 'text', '', 'wide'], + ['delay', 'Delay (ticks)', 'number', '3', 'narrow'], + ['message', 'Message', 'text', 'You turn the handle...', 'wide'], ]}, ], } @@ -87,8 +87,8 @@ var SECTIONS = [ id: 'search', label: 'Search', detect: function(d) { return d.search_table || d.search_ticks; }, fields: [ - ['search_table', 'Search Table', 'search', '', '', null, 'drops'], - ['search_ticks', 'Search Ticks', 'number', '', 'narrow'], + ['search_table', 'Search Table', 'search', 'e.g. gem_table', '', null, 'drops'], + ['search_ticks', 'Search Ticks', 'number', '8', 'narrow'], ['search_message', 'Search Message', 'text', 'You search...', 'wide'], ] }, @@ -96,7 +96,7 @@ var SECTIONS = [ id: 'food', label: 'Food', detect: function(d) { return d.heal_value || d.eat_message; }, fields: [ - ['heal_value', '+HP', 'number', '', 'narrow'], + ['heal_value', '+HP', 'number', '10', 'narrow'], ['eat_message', 'Eat Message', 'text', 'You eat the...', 'wide'], ] }, @@ -105,22 +105,22 @@ var SECTIONS = [ detect: function(d) { return d.farm_patch_type; }, fields: [ ['farm_patch_type', 'Patch Type', 'select', 'herb|allotment|flower|bush|tree'], - ['farm_level', 'Farm Level', 'number', '', 'narrow'], - ['farm_plant_xp', 'Plant XP', 'number', '', 'narrow'], - ['farm_harvest_xp', 'Harvest XP', 'number', '', 'narrow'], - ['farm_stages', 'Stages', 'number', '', 'narrow'], - ['farm_product', 'Product', 'search', '', '', null, 'items'], - ['farm_min_yield', 'Min Yield', 'number', '', 'narrow'], - ['farm_max_yield', 'Max Yield', 'number', '', 'narrow'], + ['farm_product', 'Product', 'search', 'e.g. tomato', '', null, 'items'], + ['farm_plant_xp', 'Plant XP', 'number', '5', 'narrow'], + ['farm_harvest_xp', 'Harvest XP', 'number', '10', 'narrow'], + ['farm_level', 'Farm Level', 'number', '1', 'narrow'], + ['farm_stages', 'Stages', 'number', '4', 'narrow'], + ['farm_min_yield', 'Min Yield', 'number', '1', 'narrow'], + ['farm_max_yield', 'Max Yield', 'number', '5', 'narrow'], ] }, { id: 'potions', label: 'Potions', detect: function(d) { return d.potion_effect; }, fields: [ - ['potion_effect', 'Effect', 'text', 'strength, accuracy, heal'], - ['potion_bonus', 'Bonus', 'number', '', 'narrow'], - ['potion_duration', 'Duration', 'number', '', 'narrow'], + ['potion_effect', 'Effect', 'select', 'heal|battery|all_combat|accuracy|strength|defense|ranged|science'], + ['potion_bonus', 'Bonus', 'number', '10', 'narrow'], + ['potion_duration', 'Duration', 'number', '50', 'narrow'], ] }, ]; @@ -255,6 +255,10 @@ function renderCard(sec, data) { h += renderCoreCard(data); } else if (sec.id === 'equipment') { h += renderEquipmentCard(data); + } else if (sec.id === 'farming') { + h += renderFarmingCard(data); + } else if (sec.id === 'potions') { + h += renderPotionsCard(data); } else { h += '<div class="obj-card-grid">'; if (sec.fields) sec.fields.forEach(function(f) { @@ -316,6 +320,41 @@ function renderCoreCard(data) { return h; } +function renderFarmingCard(data) { + var sec = SECTIONS.find(function(s) { return s.id === 'farming'; }); + var byKey = {}; + sec.fields.forEach(function(f) { byKey[f[0]] = f; }); + var h = '<div class="obj-core-row">'; + h += renderField(sec, byKey['farm_patch_type'], data, -1); + h += renderField(sec, byKey['farm_product'], data, -1, null, 'flex:1'); + h += '</div>'; + h += '<div class="obj-core-row">'; + h += renderField(sec, byKey['farm_plant_xp'], data, -1); + h += renderField(sec, byKey['farm_harvest_xp'], data, -1); + h += renderField(sec, byKey['farm_level'], data, -1); + h += '</div>'; + h += '<div class="obj-core-row">'; + h += renderField(sec, byKey['farm_stages'], data, -1); + h += renderField(sec, byKey['farm_min_yield'], data, -1); + h += renderField(sec, byKey['farm_max_yield'], data, -1); + h += '</div>'; + return h; +} + +function renderPotionsCard(data) { + var sec = SECTIONS.find(function(s) { return s.id === 'potions'; }); + var byKey = {}; + sec.fields.forEach(function(f) { byKey[f[0]] = f; }); + var h = '<div class="obj-core-row">'; + h += renderField(sec, byKey['potion_effect'], data, -1, null, 'flex:1'); + h += '</div>'; + h += '<div class="obj-core-row">'; + h += renderField(sec, byKey['potion_bonus'], data, -1); + h += renderField(sec, byKey['potion_duration'], data, -1); + h += '</div>'; + return h; +} + // ========================================================================= // Equipment card (custom render with 3 stat blocks) // ========================================================================= @@ -1551,8 +1590,21 @@ function duplicateItem() { delete copy._path; copy.id = newID; + var dir = ''; + if (itemData._path) { + var parts = itemData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'items') dir = parentDir; + } + API.post('/api/items', {id: newID}).then(function() { - return API.put('/api/items/' + encodeURIComponent(newID), copy); + var putOp = API.put('/api/items/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/items/move', {id: newID, dir: dir}); + }); + } + return putOp; }).then(function() { notify('Duplicated as ' + newID, 'success'); updateUndoBar(); diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js index debe7e0..c142fb4 100644 --- a/internal/admin/static/mobeditor.js +++ b/internal/admin/static/mobeditor.js @@ -24,34 +24,34 @@ var MOB_SECTIONS = [ fields: [ ['kind', 'Kind', 'select', 'combat|task'], ['aggressive', 'Aggressive', 'checkbox'], - ['respawn_ticks', 'Respawn Ticks', 'number', '', 'narrow'], - ['stats.hp', 'HP', 'number', '', 'narrow'], - ['stats.attack', 'Attack', 'number', '', 'narrow'], - ['stats.strength', 'Strength', 'number', '', 'narrow'], - ['stats.defense', 'Defense', 'number', '', 'narrow'], - ['stats.ranged', 'Ranged', 'number', '', 'narrow'], - ['stats.science', 'Science', 'number', '', 'narrow'], - ['stats.speed', 'Speed', 'number', '', 'narrow'], - ['stats.max_melee_hit', 'Max Melee Hit', 'number', '', 'narrow'], - ['stats.max_ranged_hit', 'Max Ranged Hit', 'number', '', 'narrow'], - ['stats.max_science_hit', 'Max Science Hit', 'number', '', 'narrow'], - ['stats.bonuses.attack_bonus', 'Atk Bonus', 'number', '', 'narrow'], - ['stats.bonuses.strength_bonus', 'Str Bonus', 'number', '', 'narrow'], - ['stats.bonuses.science_bonus', 'Sci Bonus', 'number', '', 'narrow'], - ['stats.bonuses.science_percent_bonus', 'Sci % Bonus', 'number', '', 'narrow'], - ['stats.bonuses.ranged_bonus', 'Rng Bonus', 'number', '', 'narrow'], - ['stats.bonuses.ranged_strength_bonus', 'Rng Str Bonus', 'number', '', 'narrow'], - ['stats.defenses.stab_defense', 'Stab Def', 'number', '', 'narrow'], - ['stats.defenses.slash_defense', 'Slash Def', 'number', '', 'narrow'], - ['stats.defenses.crush_defense', 'Crush Def', 'number', '', 'narrow'], - ['stats.defenses.science_defense', 'Sci Def', 'number', '', 'narrow'], - ['stats.defenses.ranged_defense', 'Rng Def', 'number', '', 'narrow'], - ['stats.defenses.weakness', 'Weakness', 'text'], - ['stats.defenses.weakness_percent', 'Weakness %', 'number', '', 'narrow'], - ['assassin_level', 'Assassin Lvl', 'number', '', 'narrow'], - ['finishing_blow', 'Finishing Blow', 'search', '', '', null, 'items'], - ['damage_without', 'Damage Without', 'search', '', '', null, 'items'], - ['size', 'Size', 'text'], + ['respawn_ticks', 'Respawn Ticks', 'number', '30', 'narrow'], + ['stats.hp', 'HP', 'number', '10', 'narrow'], + ['stats.attack', 'Attack', 'number', '5', 'narrow'], + ['stats.strength', 'Strength', 'number', '5', 'narrow'], + ['stats.defense', 'Defense', 'number', '5', 'narrow'], + ['stats.ranged', 'Ranged', 'number', '1', 'narrow'], + ['stats.science', 'Science', 'number', '1', 'narrow'], + ['stats.speed', 'Speed', 'number', '5', 'narrow'], + ['stats.max_melee_hit', 'Max Melee Hit', 'number', '2', 'narrow'], + ['stats.max_ranged_hit', 'Max Ranged Hit', 'number', '0', 'narrow'], + ['stats.max_science_hit', 'Max Science Hit', 'number', '0', 'narrow'], + ['stats.bonuses.attack_bonus', 'Atk Bonus', 'number', '0', 'narrow'], + ['stats.bonuses.strength_bonus', 'Str Bonus', 'number', '0', 'narrow'], + ['stats.bonuses.science_bonus', 'Sci Bonus', 'number', '0', 'narrow'], + ['stats.bonuses.science_percent_bonus', 'Sci % Bonus', 'number', '0', 'narrow'], + ['stats.bonuses.ranged_bonus', 'Rng Bonus', 'number', '0', 'narrow'], + ['stats.bonuses.ranged_strength_bonus', 'Rng Str Bonus', 'number', '0', 'narrow'], + ['stats.defenses.stab_defense', 'Stab Def', 'number', '0', 'narrow'], + ['stats.defenses.slash_defense', 'Slash Def', 'number', '0', 'narrow'], + ['stats.defenses.crush_defense', 'Crush Def', 'number', '0', 'narrow'], + ['stats.defenses.science_defense', 'Sci Def', 'number', '0', 'narrow'], + ['stats.defenses.ranged_defense', 'Rng Def', 'number', '0', 'narrow'], + ['stats.defenses.weakness', 'Weakness', 'text', 'e.g. crush'], + ['stats.defenses.weakness_percent', 'Weakness %', 'number', '50', 'narrow'], + ['assassin_level', 'Assassin Lvl', 'number', '1', 'narrow'], + ['finishing_blow', 'Finishing Blow', 'search', 'e.g. stake', '', null, 'items'], + ['damage_without', 'Damage Without', 'search', 'e.g. holy_symbol', '', null, 'items'], + ['size', 'Size', 'text', 'small'], ], inline: [ {label:'Combat Descriptions', key:'combat_descriptions', type:'tags'}, @@ -61,10 +61,10 @@ var MOB_SECTIONS = [ id: 'steal', label: 'Steal', path: 'steal', detect: function(d) { return d.steal; }, fields: [ - ['table', 'Table ID', 'search', '', '', null, 'drops'], - ['level', 'Level', 'number', '', 'narrow'], - ['xp', 'XP', 'number', '', 'narrow'], - ['speed', 'Speed', 'number', '', 'narrow'], + ['table', 'Table ID', 'search', 'e.g. gem_table', '', null, 'drops'], + ['level', 'Level', 'number', '1', 'narrow'], + ['xp', 'XP', 'number', '5', 'narrow'], + ['speed', 'Speed', 'number', '4', 'narrow'], ] }, { @@ -73,7 +73,7 @@ var MOB_SECTIONS = [ fields: [ ['verb', 'Verb', 'text', 'work'], ['progress_noun', 'Progress Noun', 'text', 'construction'], - ['complete_message', 'Complete Message', 'text'], + ['complete_message', 'Complete Message', 'text', 'The work is finished.'], ] }, { @@ -756,8 +756,21 @@ function duplicateMob() { delete copy._path; copy.id = newID; + var dir = ''; + if (mobData._path) { + var parts = mobData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'mobs') dir = parentDir; + } + API.post('/api/mobs', {id: newID}).then(function() { - return API.put('/api/mobs/' + encodeURIComponent(newID), copy); + var putOp = API.put('/api/mobs/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/mobs/move', {id: newID, dir: dir}); + }); + } + return putOp; }).then(function() { notify('Duplicated as ' + newID, 'success'); updateUndoBar(); diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js index 5bcb7bc..ac632e8 100644 --- a/internal/admin/static/moduleeditor.js +++ b/internal/admin/static/moduleeditor.js @@ -8,13 +8,13 @@ var MODULE_SECTIONS = [ { id: 'core', label: 'Core', always: true, fields: [ - ['name', 'Name', 'text'], - ['level', 'Level', 'number', '', 'narrow'], - ['max_hit', 'Max Hit', 'number', '', 'narrow'], - ['base_xp', 'Base XP', 'number', '', 'narrow'], + ['name', 'Name', 'text', 'Fire Blast'], + ['level', 'Level', 'number', '20', 'narrow'], + ['max_hit', 'Max Hit', 'number', '10', 'narrow'], + ['base_xp', 'Base XP', 'number', '50', 'narrow'], ['category', 'Category', 'select', 'combat|utility|enchant|processing|transport'], - ['element', 'Element', 'text'], - ['destination', 'Destination', 'number', '', 'narrow'], + ['element', 'Element', 'text', 'e.g. fire'], + ['destination', 'Destination', 'number', '5', 'narrow'], ], inline: [ {label:'Junk Cost', key:'junk_cost', type:'kv', valType:'number'}, @@ -100,6 +100,7 @@ function renderModuleEditor(id, data) { html += '<div class="btn-row">'; html += '<button class="btn btn-primary" onclick="saveModule()">Save</button>'; + html += '<button class="btn" onclick="duplicateModule()">Duplicate</button>'; html += '<button class="btn btn-danger" onclick="deleteModule()">Delete</button>'; html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>'; html += '</div>'; @@ -321,3 +322,45 @@ function deleteModule() { loadList(); }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); } + +function duplicateModule() { + var baseID = moduleID + '_copy'; + var newID = baseID; + var counter = 2; + while (true) { + var exists = false; + for (var i = 0; i < allIDs.length; i++) { + if (allIDs[i] === newID) { exists = true; break; } + } + if (!exists) break; + newID = baseID + '_' + counter; + counter++; + } + + var copy = JSON.parse(JSON.stringify(moduleData)); + delete copy._raw; + delete copy._path; + copy.id = newID; + + var dir = ''; + if (moduleData._path) { + var parts = moduleData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'modules') dir = parentDir; + } + + API.post('/api/modules', {id: newID}).then(function() { + var putOp = API.put('/api/modules/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/modules/move', {id: newID, dir: dir}); + }); + } + return putOp; + }).then(function() { + notify('Duplicated as ' + newID, 'success'); + updateUndoBar(); + loadList(); + loadModuleEditor(newID); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); +} diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js index 5444985..7e2d04b 100644 --- a/internal/admin/static/objecteditor.js +++ b/internal/admin/static/objecteditor.js @@ -21,7 +21,7 @@ var SECTIONS = [ id: 'steal', label: 'Steal', path: 'steal', detect: function(d) { return d.steal !== undefined; }, fields: [ - ['table', 'Table ID', 'search', '', '', null, 'drops'], + ['table', 'Table ID', 'search', 'e.g. gem_table', '', null, 'drops'], ['level', 'Level', 'number', '1', 'narrow'], ['xp', 'XP', 'number', '5', 'narrow'], ['speed', 'Speed', 'number', '4', 'narrow'], @@ -64,16 +64,16 @@ var SECTIONS = [ id: 'use', label: 'Use (Station)', path: 'use', detect: function(d) { return d.use; }, fields: [ - ['message', 'Message', 'text'], - ['wait', 'Wait', 'number', '', 'narrow'], - ['skill', 'Skill', 'text'], - ['level', 'Level', 'number', '', 'narrow'], - ['xp', 'XP', 'number', '', 'narrow'], - ['fail_message', 'Fail Message', 'text'], + ['message', 'Message', 'text', 'You use the station...'], + ['wait', 'Wait', 'number', '4', 'narrow'], + ['skill', 'Skill', 'text', 'e.g. smithing'], + ['level', 'Level', 'number', '1', 'narrow'], + ['xp', 'XP', 'number', '25', 'narrow'], + ['fail_message', 'Fail Message', 'text', 'You fail to use the station.'], ], inline: [ {label:'Success', path:'success', fields:[ - ['base', 'Base', 'number', '', 'narrow'], ['per_level', 'Per Lvl', 'number', '', 'narrow'], ['cap', 'Cap', 'number', '', 'narrow'] + ['base', 'Base', 'number', '0.5', 'narrow'], ['per_level', 'Per Lvl', 'number', '0.01', 'narrow'], ['cap', 'Cap', 'number', '0.95', 'narrow'] ]}, {label:'Consume', key:'consume', type:'kv', valType:'number'}, ], @@ -88,14 +88,13 @@ var SECTIONS = [ id: 'safespot', label: 'Safespot', path: 'safespot', detect: function(d) { return d.safespot; }, fields: [ - ['tier', 'Tier', 'number', '', 'narrow'], + ['tier', 'Tier', 'number', '1', 'narrow'], ['max_block_size', 'Max Block Size', 'select', '|small|medium|large|massive'], - ['max_occupants', 'Max Occupants', 'number', '', 'narrow'], - ['unsafe_chance', 'Unsafe Chance', 'number', '', 'narrow'], - ['decay_ticks', 'Decay Ticks', 'number', '', 'narrow'], - ['decay_chance', 'Decay Chance', 'number', '', 'narrow'], - ['respawn_on_hide', 'Respawn on Hide', 'checkbox'], - ['respawn_ticks', 'Respawn Ticks', 'number', '', 'narrow'], + ['max_occupants', 'Max Occupants', 'number', '1', 'narrow'], + ['unsafe_chance', 'Unsafe Chance', 'number', '0.05', 'narrow'], + ['decay_ticks', 'Decay Ticks', 'number', '100', 'narrow'], + ['decay_chance', 'Decay Chance', 'number', '0.1', 'narrow'], + ['respawn_ticks', 'Respawn Ticks', 'number', '50', 'narrow'], ], subtables: [ {label:'Levels', key:'levels', fields:[ @@ -107,24 +106,24 @@ var SECTIONS = [ id: 'use_interactions', label: 'Use Interactions', labelHint: '(what happens when you use items on this)', path: 'use_interactions', isArray: true, fullWidth: true, detect: function(d) { return d.use_interactions && Array.isArray(d.use_interactions); }, fields: [ - ['item_id', 'Item ID', 'search', '', '', null, 'items'], + ['item_id', 'Item ID', 'search', 'e.g. keycard', '', null, 'items'], ['condition', 'Condition', 'json', 'e.g. {"flag":"my_flag"} or {"player_flag":"x","value":1} or {"all_of":[{"flag":"a"},{"has_item":"key"}]}'], ['action', 'Action', 'json', 'e.g. {"set_flags":{"my_flag":true},"message":"You activate it.","give_item":"gem"}'], - ['message', 'Message', 'text'], + ['message', 'Message', 'text', 'You use the object...'], ] }, { id: 'on_look', label: 'On Look', path: 'on_look', detect: function(d) { return d.on_look; }, fields: [ - ['set_flags', 'Set Flags', 'json'], - ['set_player_flags', 'Set Player Flags', 'json'], - ['give_item', 'Give Item', 'text'], - ['take_item', 'Take Item', 'text'], - ['teleport', 'Teleport', 'number', '', 'narrow'], - ['heal', 'Heal', 'number', '', 'narrow'], - ['credits', 'Credits', 'number', '', 'narrow'], - ['reputation_cost', 'Reputation Cost', 'number', '', 'narrow'], + ['set_flags', 'Set Flags', 'json', '{"flag":"value"}'], + ['set_player_flags', 'Set Player Flags', 'json', '{"pflag":"value"}'], + ['give_item', 'Give Item', 'text', 'e.g. keycard'], + ['take_item', 'Take Item', 'text', 'e.g. bomb'], + ['teleport', 'Teleport', 'number', '5', 'narrow'], + ['heal', 'Heal', 'number', '10', 'narrow'], + ['credits', 'Credits', 'number', '100', 'narrow'], + ['reputation_cost', 'Reputation Cost', 'number', '1', 'narrow'], ['aps_node', 'APS Node', 'checkbox'], ] }, @@ -1106,8 +1105,21 @@ function duplicateObject() { delete copy._path; copy.id = newID; + var dir = ''; + if (objectData._path) { + var parts = objectData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'objects') dir = parentDir; + } + API.post('/api/objects', {id: newID}).then(function() { - return API.put('/api/objects/' + encodeURIComponent(newID), copy); + var putOp = API.put('/api/objects/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/objects/move', {id: newID, dir: dir}); + }); + } + return putOp; }).then(function() { notify('Duplicated as ' + newID, 'success'); updateUndoBar(); diff --git a/internal/admin/static/techeditor.js b/internal/admin/static/techeditor.js index ffb2b7b..8e18a7a 100644 --- a/internal/admin/static/techeditor.js +++ b/internal/admin/static/techeditor.js @@ -7,25 +7,25 @@ var TECH_SECTIONS = [ { id: 'core', label: 'Core', always: true, fields: [ - ['name', 'Name', 'text'], - ['level', 'Level', 'number', '', 'narrow'], - ['drain_rate', 'Drain Rate', 'number', '', 'narrow'], - ['category', 'Category', 'text'], - ['group', 'Group', 'text'], + ['name', 'Name', 'text', 'Protect from Melee'], + ['level', 'Level', 'number', '37', 'narrow'], + ['drain_rate', 'Drain Rate', 'number', '0.5', 'narrow'], + ['category', 'Category', 'text', 'protection'], + ['group', 'Group', 'text', 'melee'], ] }, { id: 'effects', label: 'Effects', always: true, path: 'effects', fields: [ - ['accuracy_percent', 'Accuracy %', 'number', '', 'narrow'], - ['strength_percent', 'Strength %', 'number', '', 'narrow'], - ['defense_percent', 'Defense %', 'number', '', 'narrow'], - ['ranged_percent', 'Ranged %', 'number', '', 'narrow'], - ['science_percent', 'Science %', 'number', '', 'narrow'], - ['damage_reduction', 'Damage Reduction', 'number', '', 'narrow'], - ['hp_regen_multi', 'HP Regen Multi', 'number', '', 'narrow'], - ['preserve_drain', 'Preserve Drain', 'number', '', 'narrow'], - ['retribution_pct', 'Retribution %', 'number', '', 'narrow'], + ['accuracy_percent', 'Accuracy %', 'number', '5', 'narrow'], + ['strength_percent', 'Strength %', 'number', '5', 'narrow'], + ['defense_percent', 'Defense %', 'number', '5', 'narrow'], + ['ranged_percent', 'Ranged %', 'number', '5', 'narrow'], + ['science_percent', 'Science %', 'number', '5', 'narrow'], + ['damage_reduction', 'Damage Reduction', 'number', '40', 'narrow'], + ['hp_regen_multi', 'HP Regen Multi', 'number', '2', 'narrow'], + ['preserve_drain', 'Preserve Drain', 'number', '1', 'narrow'], + ['retribution_pct', 'Retribution %', 'number', '25', 'narrow'], ['protect_melee', 'Protect Melee', 'checkbox'], ['protect_ranged', 'Protect Ranged', 'checkbox'], ['protect_science', 'Protect Science', 'checkbox'], @@ -89,6 +89,7 @@ function renderTechEditor(id, data) { html += '<div class="btn-row">'; html += '<button class="btn btn-primary" onclick="saveTech()">Save</button>'; + html += '<button class="btn" onclick="duplicateTech()">Duplicate</button>'; html += '<button class="btn btn-danger" onclick="deleteTech()">Delete</button>'; html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>'; html += '</div>'; @@ -192,3 +193,45 @@ function deleteTech() { loadList(); }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); }); } + +function duplicateTech() { + var baseID = techID + '_copy'; + var newID = baseID; + var counter = 2; + while (true) { + var exists = false; + for (var i = 0; i < allIDs.length; i++) { + if (allIDs[i] === newID) { exists = true; break; } + } + if (!exists) break; + newID = baseID + '_' + counter; + counter++; + } + + var copy = JSON.parse(JSON.stringify(techData)); + delete copy._raw; + delete copy._path; + copy.id = newID; + + var dir = ''; + if (techData._path) { + var parts = techData._path.split('/'); + var parentDir = parts[parts.length - 2]; + if (parentDir !== 'techs') dir = parentDir; + } + + API.post('/api/techs', {id: newID}).then(function() { + var putOp = API.put('/api/techs/' + encodeURIComponent(newID), copy); + if (dir) { + return putOp.then(function() { + return API.post('/api/techs/move', {id: newID, dir: dir}); + }); + } + return putOp; + }).then(function() { + notify('Duplicated as ' + newID, 'success'); + updateUndoBar(); + loadList(); + loadTechEditor(newID); + }).catch(function(e) { notify('Duplicate failed: ' + e.message, 'error'); }); +} |
