var hazardData = null;
var hazardID = null;
var expandedCards = {};
var addedSections = {};
var cardWidths = {};
var HAZARD_SECTIONS = [
{
id: 'core', label: 'Core', always: true,
fields: [
['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', 'search', 'e.g. gas_mask', '', null, 'items'],
]
},
{
id: 'combat', label: 'Combat', path: 'combat',
detect: function(d) { return d.combat; },
fields: [
['stats.attack', 'Attack', 'number', '5', 'narrow'],
['stats.strength', 'Strength', '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.ranged_bonus', 'Rng Bonus', 'number', '0', 'narrow'],
['stats.bonuses.ranged_strength_bonus', 'Rng Str Bonus', 'number', '0', 'narrow'],
['stats.bonuses.science_bonus', 'Sci Bonus', 'number', '0', 'narrow'],
['stats.bonuses.science_percent_bonus', 'Sci % Bonus', 'number', '0', 'narrow'],
]
}
];
function initHazardEditor() {
editorType = 'hazards';
editorFields = [];
loadList();
if (window.location.hash) loadHazardEditor(window.location.hash.substring(1));
}
function cardPath(sec) { return sec.path || ''; }
function getVal(data, sec, key) {
return ced_getVal(data, sec, key);
}
function fieldID(sec, key, idx) {
return ced_fieldID(sec, key, idx);
}
function setupSearchFields() { ced_setupSearchFields(); }
function collectFieldValue(el, type) { return ced_collectFieldValue(el, type); }
function loadHazardEditor(id) {
hazardID = id;
currentID = id;
renderList('');
window.location.hash = id;
API.get('/api/hazards/' + encodeURIComponent(id)).then(function(data) {
hazardData = data;
renderHazardEditor(id, data);
}).catch(function(e) {
$('#editorMain').innerHTML = '
Failed to load: ' + esc(e.message) + '
';
});
}
function loadItem(id) { loadHazardEditor(id); }
function renderHazardEditor(id, data) {
var html = renderRenameableHeader('Hazard', id);
html += 'Fields ';
html += 'Raw YAML
';
html += '';
html += '
';
HAZARD_SECTIONS.forEach(function(sec) {
if (!sec.always && sec.detect && !sec.detect(data) && !addedSections[sec.id]) return;
html += renderCard(sec, data);
});
html += '
';
html += '
';
html += '';
html += '+ Add Section... ';
HAZARD_SECTIONS.forEach(function(sec) {
if (sec.always) return;
if (sec.detect && sec.detect(data)) return;
if (addedSections[sec.id]) return;
html += '' + esc(sec.label) + ' ';
});
html += ' ';
html += 'Add ';
html += renderMoveBar();
html += '
';
html += '
';
html += '';
html += '
' + esc(data._raw || JSON.stringify(data, null, 2)) + ' ';
html += '
';
html += '';
html += 'Save ';
html += 'Duplicate ';
html += 'Remove ';
html += 'New Directory ';
html += '
';
$('#editorMain').innerHTML = html;
setupSearchFields();
}
function renderCurrent() {
ced_syncDOMToData(hazardData);
if (!hazardData || !hazardID) return;
renderHazardEditor(hazardID, hazardData);
}
function renderCard(sec, data) {
var collapsed = expandedCards[sec.id] === false;
var isFull = cardWidths[sec.id] !== undefined ? cardWidths[sec.id] : (sec.fullWidth || false);
var fullClass = isFull ? ' obj-card-full' : '';
var h = '';
h += '';
if (!collapsed) {
h += '
';
if (sec.id === 'combat') {
h += renderHazardCombatCard(data);
} else {
h += '
';
sec.fields.forEach(function(f) {
h += renderField(sec, f, data, -1);
});
h += '
';
}
h += '
';
}
h += '
';
return h;
}
function renderField(sec, f, data, idx) {
return ced_renderField(sec, f, data, idx, null, null, cardPath, fieldID, getVal);
}
function toggleCard(id) {
expandedCards[id] = expandedCards[id] === false ? true : false;
renderCurrent();
}
function toggleCardWidth(id) {
cardWidths[id] = !cardWidths[id];
renderCurrent();
}
function removeSection(id) {
var sec = HAZARD_SECTIONS.find(function(s) { return s.id === id; });
if (!sec) return;
ced_syncDOMToData(hazardData);
if (sec.path) {
delete hazardData[sec.path];
}
delete addedSections[id];
renderHazardEditor(hazardID, hazardData);
}
function addSection() {
var sel = $('#addSectionSelect');
var id = sel.value;
if (!id) return;
var sec = HAZARD_SECTIONS.find(function(s) { return s.id === id; });
if (!sec) return;
if (id === 'combat') {
hazardData.combat = {
attack_types: ['crush'],
stats: {
attack: 1, strength: 0, ranged: 0, science: 0,
speed: 5,
max_melee_hit: 0, max_ranged_hit: 0, max_science_hit: 0,
bonuses: {attack_bonus:0, strength_bonus:0, ranged_bonus:0, ranged_strength_bonus:0, science_bonus:0, science_percent_bonus:0}
}
};
}
addedSections[id] = true;
expandedCards[id] = true;
renderCurrent();
}
function saveHazard() {
if (!ced_validateFields(HAZARD_SECTIONS, fieldID, null)) return;
var out = {};
HAZARD_SECTIONS.forEach(function(sec) {
var sectionObj = {};
var hasValues = sec.always;
if (sec.fields) {
sec.fields.forEach(function(f) {
var fid = fieldID(sec, f[0]);
var el = document.getElementById(fid);
if (!el) return;
var val = collectFieldValue(el, f[2]);
if (val !== '' && val !== false && val !== 0 && val !== null) hasValues = true;
sectionObj[f[0]] = val;
});
}
if (sec.id === 'combat' && document.getElementById('hz_atktype')) {
sectionObj['attack_types'] = ced_collectAtkType('hz_atktype');
hasValues = true;
}
if (sec.always) {
Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
} else if (hasValues && sec.path) {
out[sec.path] = ced_unflattenObj(sectionObj);
} else if (hasValues && !sec.path) {
Object.keys(sectionObj).forEach(function(k) { out[k] = sectionObj[k]; });
}
});
ced_cleanOutput(out);
out.id = hazardID;
API.put('/api/hazards/' + encodeURIComponent(hazardID), out).then(function(resp) {
notify('Hazard ' + hazardID + ' saved', 'success');
updateUndoBar();
if (resp && resp._raw) hazardData._raw = resp._raw;
}).catch(function(e) { notify('Save failed: ' + e.message, 'error'); });
}
function deleteHazard() {
if (!confirm('Delete hazard "' + hazardID + '"?')) return;
API.del('/api/hazards/' + encodeURIComponent(hazardID)).then(function() {
notify('Hazard ' + hazardID + ' deleted', 'success');
updateUndoBar();
hazardID = null;
currentID = null;
hazardData = null;
$('#editorMain').innerHTML = 'Deleted
';
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'); });
}
function renderHazardCombatCard(data) {
var sec = HAZARD_SECTIONS.find(function(s) { return s.id === 'combat'; });
if (!sec) return '';
var byKey = {};
sec.fields.forEach(function(f) { byKey[f[0]] = f; });
var h = ced_renderAtkTypeWidget(data, 'hz_atktype', 'ced_atkPillSelect');
h += '';
h += '
Stats ';
h += '
';
var statKeys = ['stats.attack','stats.strength','stats.ranged','stats.science'];
statKeys.forEach(function(k) { h += renderField(sec, byKey[k], data, -1); });
h += '
';
h += '
';
h += renderField(sec, byKey['stats.speed'], data, -1);
h += '
';
h += '
';
var maxKeys = ['stats.max_melee_hit','stats.max_ranged_hit','stats.max_science_hit'];
maxKeys.forEach(function(k) { h += renderField(sec, byKey[k], data, -1); });
h += '
';
h += '
';
h += '';
h += '
Bonuses ';
h += '
';
var bonusKeys = ['stats.bonuses.attack_bonus','stats.bonuses.strength_bonus','stats.bonuses.ranged_bonus',
'stats.bonuses.ranged_strength_bonus','stats.bonuses.science_bonus','stats.bonuses.science_percent_bonus'];
bonusKeys.forEach(function(k) { h += renderField(sec, byKey[k], data, -1); });
h += '
';
h += '
';
return h;
}