This repository has been archived on 2024-07-18. You can view files and clone it, but cannot push or open issues or pull requests.
lwb5/admin/map_edit.js

703 lines
18 KiB
JavaScript

// Generic text input component
TextInput = function (id, title, multi, min, max, init) {
this.id = id;
this.title = title;
this.multi = multi;
var value = init;
this.min = min;
this.max = max;
this.valid = false;
this.onChange = null;
var readValue = function () {
var e = document.getElementById(id + '-input');
if (e) {
value = e.value;
}
};
this.getText = function () {
readValue();
return value;
};
this.draw = function () {
readValue();
var str = '<table style="width: 100%; border-style: none; padding: 2px; margin: 0px">'
+ '<tr><th style="text-align:right; padding: 0px 5px; width: 120px;vertical-align:top">'
+ this.title + ':</th><td style="text-align: center">';
var jsStr = '="TextInput.byId[\'' + this.id + '\'].__change(); return true"';
if (this.multi) {
str += '<textarea id="' + this.id + '-input" rows="5" style="width: 100%" onChange'
+ jsStr + ' onKeyUp' + jsStr + ' onClick' + jsStr + '></textarea>';
} else {
str += '<input type="text" id="' + this.id + '-input" style="width: 100%" onChange'
+ jsStr + ' onKeyUp' + jsStr + ' onClick' + jsStr + '/>';
}
str += '</td></tr></table>';
document.getElementById(this.id).innerHTML = str;
document.getElementById(this.id + '-input').value = value;
this.checkValidity();
};
this.checkValidity = function () {
this.valid = (this.min == 0 && this.max == 0);
if (!this.valid) {
this.valid = (this.min > 0 && value.length >= this.min || this.min == 0)
&& (this.max > 0 && value.length <= this.max || this.max == 0);
}
with (document.getElementById(this.id + '-input').style) {
color = this.valid ? 'black' : 'white';
backgroundColor = this.valid ? 'white' : 'red';
}
};
this.__change = function () {
readValue();
this.checkValidity();
if (this.onChange) {
this.onChange(value);
}
};
TextInput.byId[this.id] = this;
};
TextInput.byId = {};
// Generic numeric control for size and amount of alliances
NumericControl = function (id, title, min, max, init) {
this.id = id;
this.title = title;
this.min = min;
this.max = max;
this.value = init;
this.onChange = null;
this.draw = function () {
var str = '<table style="width: 100%; border-style: none; padding: 2px; margin: 0px">'
+ '<tr><th style="text-align:right; padding: 0px 5px">' + this.title
+ ':</th><td style="text-align: center; width: 20px">' + this.value
+ '</td><td style="width: 70px; text-align:center">'
+ '<input type="button" onClick="NumericControl.byId[\''
+ this.id + '\'].decrease()" value="-" style="width: 24px" /> '
+ '<input type="button" onClick="NumericControl.byId[\''
+ this.id + '\'].increase()" value="+" style="width: 24px" /></td>'
+ '</tr></table>';
document.getElementById(this.id).innerHTML = str;
};
this.increase = function () {
if (this.value == this.max) {
return;
}
this.value ++;
this.draw();
if (this.onChange) {
this.onChange(this.value);
}
};
this.decrease = function () {
if (this.value == this.min) {
return;
}
this.value --;
this.draw();
if (this.onChange) {
this.onChange(this.value);
}
};
NumericControl.byId[this.id] = this;
};
NumericControl.byId = {};
// A system on the map
MapLocation = function (type, alloc, spawn) {
this.type = type;
this.alloc = alloc;
this.spawn = spawn;
};
// The map itself
Map = function (initFrom) {
// Copy basic map information
this.name = initFrom.name;
this.description = initFrom.description;
this.alliances = initFrom.alliances;
this.width = initFrom.width;
this.height = initFrom.height;
// Copy the map
this.map = { };
for (var i in initFrom.map) {
var ma = initFrom.map[i];
this.map['x' + ma[0] + 'y' + ma[1]] = new MapLocation(ma[2], ma[3], ma[4]);
}
// Function that computes min/max x/y
this.updateCoordinates = function () {
this.minX = - Math.floor(this.width / 2);
this.maxX = this.minX + this.width - 1;
this.minY = - Math.floor(this.height / 2);
this.maxY = this.minY + this.height - 1;
};
// Creates a nebula area
this.setNebula = function (x, y, opacity) {
if (this.map['x' + x + 'y' + y]) {
this.map['x' + x + 'y' + y].type = opacity;
} else {
this.map['x' + x + 'y' + y] = new MapLocation(opacity, null, null);
}
};
// Creates a target system
this.setTarget = function (x, y) {
if (this.map['x' + x + 'y' + y]) {
this.map['x' + x + 'y' + y].type = 'S';
this.map['x' + x + 'y' + y].alloc = 0;
} else {
this.map['x' + x + 'y' + y] = new MapLocation('S', 0, null);
}
};
// Creates an alliance-controlled system
this.setAlliance = function (x, y, alliance) {
var sys = this.map['x' + x + 'y' + y];
if (sys) {
sys.type = 'S';
sys.alloc = alliance;
sys.spawn = false;
} else {
this.map['x' + x + 'y' + y] = new MapLocation('S', alliance, false);
}
};
// Switches spawning status for an alliance-controlled system
this.switchSpawnPoint = function (x, y) {
var sys = this.map['x' + x + 'y' + y];
if (sys && sys.type == 'S' && sys.alloc > 0) {
sys.spawn = !sys.spawn;
}
};
// Removes systems allocated to alliances which no longer exist
this.removeExtraAlliances = function () {
for (var i in this.map) {
var sys = this.map[i];
if (sys.type == 'S' && sys.alloc > this.alliances) {
this.map[i] = null;
}
}
};
this.updateCoordinates();
};
// The editor's grid
Grid = function (id, map) {
this.id = id;
this.map = map;
this.cX = 0;
this.cY = 0;
this.onClick = null;
// This function draws the grid in which the map is displayed
this.draw = function () {
var i, j;
var str = '<table style="border: 1px solid black; border-collapse: collapse; padding: 0px; margin: 0px">'
+ '<tr><td style="border: 1px solid black;width: 32px;height:32px">&nbsp;</td>'
+ '<td style="border: 1px solid black; width:416px" colspan="13" id="map-up">&nbsp;</td>'
+ '<td style="border: 1px solid black;width: 32px">&nbsp;</td></tr>'
+ '<tr><td style="border: 1px solid black; height:416px" rowspan="13" id="map-left">&nbsp;</td>'
+ '<td style="border: 1px solid black;width: 32px;height:32px">&nbsp;</td>';
for (i = 0; i < 11; i ++) {
str += '<td style="border: 1px solid black;width: 32px; height: 32px; text-align: center" '
+ 'id="top-x-' + i + '">&nbsp;</td>';
}
str += '<td style="border: 1px solid black;width: 32px;height:32px">&nbsp;</td>'
+ '<td style="border: 1px solid black; height:416px" rowspan="13" id="map-right">&nbsp;</td>'
+ '</tr>';
for (i = 0; i < 11; i ++) {
str += '<tr><td style="border: 1px solid black;width: 32px; height: 32px; text-align: center" '
+ 'id="left-y-' + i + '">&nbsp;</td>';
for (j = 0; j < 11; j ++) {
str += '<td style="border: 1px solid #7f7f7f; width: 32px; height: '
+ '32px; text-align:center" id="map-' + j + '-' + i
+ '" onclick="Editor.editor.mapClick(' + j + ',' + i +')">&nbsp;</td>';
}
str += '<td style="border: 1px solid black;width: 32px; height: 32px; text-align:center" '
+ 'id="right-y-' + i + '">&nbsp;</td></tr>';
}
str += '<tr><td style="border: 1px solid black;width: 32px;height:32px">&nbsp;</td>';
for (i = 0; i < 11; i ++) {
str += '<td style="border: 1px solid black;width: 32px; height: 32px;text-align:center" '
+ 'id="bottom-x-' + i + '">&nbsp;</td>';
}
str += '<td style="border: 1px solid black;width: 32px;height:32px">&nbsp;</td></tr>'
+ '<tr><td style="border: 1px solid black;width: 32px; height:32px">&nbsp;</td>'
+ '<td style="border: 1px solid black; width:416px" colspan="13" id="map-down">&nbsp;</td>'
+ '<td style="border: 1px solid black;width: 32px">&nbsp;</td></tr>'
+ '</table>';
document.getElementById(this.id).innerHTML = str;
this.drawMap();
};
// This function draws the map on the grid
this.drawMap = function () {
var i, j, str, x, y;
// Scroll buttons
if (this.cX - 5 > this.map.minX) {
str = '<input type="button" value="<" style="width:100%;height:100%;margin:0"'
+ ' onclick="Editor.editor.scroll(-1, 0)" />';
} else {
str = '&nbsp;';
}
document.getElementById('map-left').innerHTML = str;
if (this.cX + 5 < this.map.maxX) {
str = '<input type="button" value=">" style="width:100%;height:100%;margin:0"'
+ ' onclick="Editor.editor.scroll(1, 0)" />';
} else {
str = '&nbsp;';
}
document.getElementById('map-right').innerHTML = str;
if (this.cY - 5 > this.map.minY) {
str = '<input type="button" value="\\/" style="width:100%;height:100%;margin:0"'
+ ' onclick="Editor.editor.scroll(0, -1)" />';
} else {
str = '&nbsp;';
}
document.getElementById('map-down').innerHTML = str;
if (this.cY + 5 < this.map.maxY) {
str = '<input type="button" value="/\\" style="width:100%;height:100%;margin:0"'
+ ' onclick="Editor.editor.scroll(0, 1)" />';
} else {
str = '&nbsp;';
}
document.getElementById('map-up').innerHTML = str;
// Draw X coordinates
for (i = 0; i < 11; i ++) {
x = this.cX + i - 5;
if (x < this.map.minX || x > this.map.maxX) {
str = '&nbsp;';
} else {
str = '<b>' + x + '</b>';
}
document.getElementById('top-x-' + i).innerHTML =
document.getElementById('bottom-x-' + i).innerHTML = str;
}
// Draw Y coordinates
for (i = 0; i < 11; i ++) {
y = this.cY - i + 5;
if (y < this.map.minY || y > this.map.maxY) {
str = '&nbsp;';
} else {
str = '<b>' + y + '</b>';
}
document.getElementById('left-y-' + i).innerHTML =
document.getElementById('right-y-' + i).innerHTML = str;
}
// Draw contents
for (i = 0; i < 11; i ++) {
x = this.cX + i - 5;
for (j = 0; j < 11; j ++) {
y = this.cY - j + 5;
var cell = document.getElementById('map-' + i + '-' + j);
if (y < this.map.minY || y > this.map.maxY || x < this.map.minX || x > this.map.maxX) {
cell.innerHTML = '&nbsp;';
cell.style.backgroundColor = 'black';
continue;
}
var sys = this.map.map['x' + x + 'y' + y];
if (!sys) {
cell.innerHTML = '&nbsp;';
cell.style.backgroundColor = '#3f3f3f';
} else if (sys.type != 'S') {
cell.innerHTML = '<b>' + sys.type + '</b>';
cell.style.backgroundColor = '#afafaf';
cell.style.color = Grid.nebulaColours[parseInt(sys.type, 10) - 1];
} else if (sys.alloc == 0) {
cell.innerHTML = 'T';
cell.style.backgroundColor = 'black';
cell.style.color = 'white';
} else {
cell.style.color = 'black';
cell.style.backgroundColor = Grid.allianceColours[sys.alloc - 1];
cell.innerHTML = sys.spawn ? 'X' : '&nbsp;';
}
}
}
};
// This function handle clicks on the map
this.handleClick = function(i, j) {
x = this.cX + i - 5;
y = this.cY - j + 5;
if (x >= this.map.minX && x <= this.map.maxX
&& y >= this.map.minY && y <= this.map.maxY && this.onClick) {
this.onClick(x, y);
}
};
};
Grid.allianceColours = [
'ff0000', '00ff00', '0000ff', '007f7f', '7f007f', 'afaf00', 'ffaf3f', '003f7f'
];
Grid.nebulaColours = [
'#ff0000', '#ff2f00', '#ff5f00', '#ff7f00'
];
// Toolbox
Toolbox = function (id, map) {
this.id = id;
this.map = map;
this.currentTool = null;
var drawToolCell = function (id, title, ctxt, cbg, cfg) {
return str = '<tr><td><table style="width:100%;padding:6px;margin:0px" id="tool-' + id
+ '" onClick="Editor.editor.selectTool(\'' + id + '\');"><tr>'
+ '<td style="color:' + cfg + '; background-color: ' + cbg
+ '; text-align:center;width:32px;height:32px">' + ctxt + '</td>'
+ '<td style="text-align:center;font-weight:bold">' + title + '</td>'
+ '</tr></table></td></tr>';
};
this.draw = function () {
var i, str = '<table style="width: 200px; border-style: none">'
+ '<tr><th style="font-size: 120%">Map drawing tools</th></tr>'
+ '<tr><th>Nebulae</th></tr>';
for (i = 1; i < 5; i ++) {
str += drawToolCell('n' + i, 'Class ' + i + ' nebula', '<b>' + i + '</b>',
'#afafaf', Grid.nebulaColours[i - 1]);
}
str += '<tr><th>Misc</th></tr>'
+ drawToolCell('tg', 'Target system', 'T', 'black', 'white')
+ drawToolCell('sp', 'Spawing point', 'X', 'white', 'black')
+ '<tr><th>Alliances</th></tr>';
for (i = 1; i <= this.map.alliances; i ++) {
str += drawToolCell('a' + i, 'Alliance ' + i, '&nbsp;', Grid.allianceColours[i - 1], 'black');
}
str += '</table>';
document.getElementById(this.id).innerHTML = str;
if (this.currentTool) {
this.selectTool(this.currentTool);
}
};
this.selectTool = function(tool) {
if (this.currentTool && document.getElementById('tool-' + this.currentTool)) {
document.getElementById('tool-' + this.currentTool).style.borderStyle = 'none';
}
if (! document.getElementById('tool-' + tool)) {
this.currentTool = null;
return;
}
with (document.getElementById('tool-' + tool).style) {
borderColor = 'red';
borderWidth = '2px';
borderStyle = 'solid';
}
this.currentTool = tool;
};
};
// Validity check / submit button
CheckAndSubmit = function (id, map) {
this.id = id;
this.map = map;
this.error = "---";
var mapCheck = function (map) {
var ax = [];
var tgc = 0;
for (var i = 0; i < map.alliances; i ++) {
ax[i] = [0, 0];
}
for (var i = map.minX; i <= map.maxX; i ++) {
for (var j = map.minY; j <= map.maxY; j ++) {
var m = map.map['x' + i + 'y' + j];
if (! m) {
return 1;
}
if (m.type != 'S') {
continue;
}
if (m.alloc == 0) {
tgc ++;
continue;
}
ax[m.alloc - 1][0] ++;
if (m.spawn) {
ax[m.alloc - 1][1] ++;
}
}
}
if (tgc == 0) {
return 2;
}
var min = -1;
for (var i = 0; i < map.alliances; i ++) {
if (ax[i][0] == 0) {
return 3 + i;
}
if (min == -1) {
min = ax[i][0];
} else if (ax[i][0] < min) {
return 19;
} else if (ax[i][0] > min) {
return 19 + i;
}
}
min = -1;
for (var i = 0; i < map.alliances; i ++) {
if (ax[i][1] == 0) {
return 11 + i;
}
if (min == -1) {
min = ax[i][1];
} else if (ax[i][1] < min) {
return 27;
} else if (ax[i][1] > min) {
return 27 + i;
}
}
return 0;
};
this.draw = function () {
if (this.error == '---') {
this.check();
}
var str;
if (this.error == "") {
str = '<input type="button" value="Save map" onclick="Editor.editor.submit()" />';
} else {
str = '<span style="color:red;font-weight:bold">' + this.error + '</span>';
}
document.getElementById(this.id).innerHTML = str;
};
this.check = function () {
var error = "";
if (map.name.length < 4) {
error = "Name too short";
} else if (map.name.length > 32) {
error = "Name too long";
} else {
var i = mapCheck(this.map);
switch (i) {
case 0: break;
case 1: error = "Map has undefined areas"; break;
case 2: error = "Map has no target areas"; break;
default:
if (i < 11) {
i -= 2;
error = "Alliance " + i + " has no systems.";
} else if (i < 19) {
i -= 10;
error = "Alliance " + i + " has no spawning points.";
} else if (i < 27) {
i -= 18;
error = "Alliance " + i + " has too many systems.";
} else if (i < 35) {
i -= 26;
error = "Alliance " + i + " has too many spawning points.";
}
break;
}
}
if (this.error != error) {
this.error = error;
this.draw();
}
};
};
// Main editor object
Editor = function (initFrom) {
var map = new Map(initFrom);
var components = [];
var cSub = new CheckAndSubmit('check-and-send', map);
var tools = new Toolbox('tools', map);
components.push(tools);
var grid = new Grid('grid', map);
grid.onClick = function(x, y) {
var tool = tools.currentTool;
if (!tool) {
return;
}
if (tool.match(/^n[1-4]$/)) {
map.setNebula(x, y, tool.charAt(1));
} else if (tool == 'tg') {
map.setTarget(x, y);
} else if (tool.match(/^a[1-8]$/)) {
map.setAlliance(x, y, parseInt(tool.charAt(1), 10));
} else if (tool == 'sp') {
map.switchSpawnPoint(x, y);
}
grid.drawMap();
cSub.check();
};
components.push(grid);
var c;
c = new TextInput('name', 'Map name', false, 4, 32, map.name);
c.onChange = function (value) {
map.name = value;
cSub.check();
};
components.push(c);
c = new TextInput('desc', 'Description', true, 0, 0, map.description);
c.onChange = function (value) {
map.description = value;
};
components.push(c);
c = new NumericControl('n-alliances', 'Alliances', 2, 8, map.alliances);
c.onChange = function (value) {
var old = map.alliances;
map.alliances = value;
tools.draw();
if (value < old) {
map.removeExtraAlliances();
grid.drawMap();
}
cSub.check();
};
components.push(c);
c = new NumericControl('m-width', 'Map width', 3, 41, map.width);
c.onChange = function (value) {
map.width = value;
map.updateCoordinates();
grid.drawMap();
cSub.check();
};
components.push(c);
c = new NumericControl('m-height', 'Map height', 3, 41, map.height);
c.onChange = function (value) {
map.height = value;
map.updateCoordinates();
grid.drawMap();
cSub.check();
};
components.push(c);
components.push(cSub);
this.draw = function () {
var str = '<h3>Parameters</h3>'
+ '<table style="width: 100%; border-style: none; margin: 0px; padding: 0px">'
+ '<tr><td colspan="3" id="name">&nbsp;</td></tr>'
+ '<tr><td colspan="3" id="desc">&nbsp;</td></tr>'
+ '<tr><td style="width: 33%" id="n-alliances">&nbsp;</td>'
+ '<td style="width: 34%" id="m-width">&nbsp;</td>'
+ '<td style="width: 33%" id="m-height">&nbsp;</td></tr>'
+ '<tr><td colspan="3" id="check-and-send" style="text-align:center">&nbsp;</td></tr>'
+ '</table>'
+ '<h3>Map</h3>'
+ '<div><div id="tools" style="float:right">&nbsp;</div><div id="grid">&nbsp;</div></div>';
document.getElementById('mapedit').innerHTML = str;
for (var i in components) {
components[i].draw();
}
};
this.scroll = function (dx, dy) {
grid.cX += dx; grid.cY += dy;
grid.drawMap();
};
this.mapClick = function(x,y) {
grid.handleClick(x, y);
};
this.selectTool = function(tool) {
tools.selectTool(tool);
};
this.submit = function () {
document.getElementById('sf-name').value = map.name;
document.getElementById('sf-desc').value = map.description;
document.getElementById('sf-width').value = map.width;
document.getElementById('sf-height').value = map.height;
document.getElementById('sf-alliances').value = map.alliances;
var ma = [];
for (var j = map.minY; j <= map.maxY; j ++) {
var str = '';
for (var i = map.minX; i <= map.maxX; i ++) {
var m = map.map['x' + i + 'y' + j];
str += m.type;
if (m.type != 'S') {
continue;
}
str += m.alloc;
if (m.alloc == 0) {
continue;
}
str += m.spawn ? '1' : '0';
}
ma.push(str);
}
document.getElementById('sf-map').value = ma.join('#');
document.getElementById('sendform').submit();
};
};
Editor.editor = new Editor(initMap);
Editor.editor.draw();