Simple Map Editor - Knockout Version
A simple map editor for grid-based systems. The assumes that you're doing it yourself and isn't using anything like ROT for instance.
by Sam Fereday
HTML
<div id="preview" data-bind="css: { foundationEnabled: !propMode(), foundationDisabled: propMode() }">
<span data-bind="foreach: grid">
<div class="sq" data-bind="style: positionCSS, css: { occupied: occupied() }, click: !$parent.propMode() ? clicked : null, text: decorType()"></div>
</span>
</div>
<div id="prop-preview" data-bind="css: { foundationEnabled: propMode(), foundationDisabled: !propMode() }">
<span data-bind="foreach: propGrid">
<div class="sq" data-bind="style: positionCSS, css: { occupied: occupied() }, click: $parent.propMode() ? clicked : null, text: decorType()"></div>
</span>
</div>
<div id="ui">
<form id="entities" data-bind="css: { enabled: !propMode() }">
<p><strong>Foundation Tile Types</strong></p>
<span>
<input type="radio" name="entity" value="block" data-bind="checked: tileGraphic, enable: !propMode()"> Block
</span>
</form>
<p><strong>Placement mode:</strong>
<input type="checkbox" data-bind="checked: propMode, click: changeDefault" />
</p>
<form id="entities" data-bind="css: { enabled: propMode() }">
<p><strong>Entity Tile Types</strong></p>
<span>
<input type="radio" name="entity" value="enemy" data-bind="checked: tileGraphic, enable: propMode"> Enemy
</span>
<span>
<input type="radio" name="entity" value="chest" data-bind="checked: tileGraphic, enable: propMode"> Chest
</span>
</form>
<p>
<strong>Export data:</strong>
</p>
<button data-bind="click: exportData">Export</button>
<p>
<strong>Exported data output:</strong>
</p>
<div id="output" contenteditable="true" data-bind="text: exportedData"></div>
</div>
SCSS
$tileWidth: 32;
$tileHeight: 32;
$tileNum: 6;
body {
font: 75%/1.4em arial;
}
.sq {
width: $tileWidth + 'px';
height: $tileHeight + 'px';
position: absolute;
background: #333;
cursor: pointer;
text-align: center;
}
.sq:hover {
background: #555;
}
.sq.occupied {
background: green;
}
#ui {
width: 100%;
clear: both;
padding-top: $tileHeight * $tileNum + 32 + 'px';
}
form {
opacity: 0.5;
}
.enabled {
opacity: 1;
}
form span {
display: block;
clear: both;
width: 100%;
}
#preview,
#prop-preview {
// width: 100%;
height: $tileHeight * $tileNum + 'px';
position: absolute;
top: 2em;
left: 2em;
}
#prop-preview {
.sq {
background: #EEE;
}
}
// Needs sorting a bit
.foundationDisabled {
opacity: 0.5;
z-index: 0;
}
.foundationEnabled {
opacity: 0.5;
z-index: 999;
}
#output {
max-height: 200px;
border: 1px solid #ccc;
padding: 1em;
overflow-y: auto;
}
JavaScript
// Tile graphical types
var tileTypes = {
unset: 0,
block: 1,
enemy: 2,
chest: 3
};
// Tile type data (contains all graphical info, etc) - Expects each eType to always differ!
var tileStaticData = [{
name: "block",
eType: tileTypes.block, // Corresponds to enums below.
asset: "../blockimg.jpg"
},
{
name: "enemy",
eType: tileTypes.enemy, // Corresponds to enums below.
asset: "../enemyimg.jpg"
}];
// Helpers, for when there's nowhere else to put these functions
var helpers = {
guid: function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
},
getDataTileName: function(eType) {
var found = tileStaticData.find(function(data) {
return data.eType === eType;
});
return found ? found.name : null;
}
}
// Components / models for mapper to use
var TileModel = function(x, y, occupied, d, parent) {
this.w = 32;
this.h = 32;
this.x = ko.observable(x);
this.y = ko.observable(y);
this.occupied = ko.observable(occupied);
this.decorType = ko.observable(d);
this.guid = ko.observable(helpers.guid());
this.parent = parent;
this.layer = null;
// No idea how this works.
// http://stackoverflow.com/questions/18560815/can-you-add-knockoutjs-style-binding-specifying-propertyvalue-together
this.positionCSS = ko.computed(function() {
return {
"left": this.x() * this.w + "px",
"top": this.y() * this.h + "px"
};
}, this);
};
TileModel.prototype.clicked = function(data, event) {
// Not entirely sure if this is allowed or if it buggers the memory...
this.occupied(!this.occupied());
// This should be pre-determined by what you select from the menu. If you're not in placement mode, it should revert to using the default block graphic (or set of)
this.decorType(tileTypes[data.parent.tileGraphic()]);
}
//...