JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<h3>NQ Editor</h3>
<div id='editor-tab-buttons'>
<div class='tab-button' data-tab='maps'>Maps</div>
<div class='tab-button' data-tab='doors'>Doors</div>
<div class='tab-button' data-tab='npcs'>NPCs</div>
<div class='tab-button' data-tab='enemies'>Enemies</div>
<div class='tab-button' data-tab='items'>Items</div>
</div>
<p>Click on a tile to 'paint' it with the currently-selected terrain.</p>
Width: <input type='number' id='select-width' min='10' max='100' value='100'>
Height: <input type='number' id='select-height' min='10' max='100' value='100'>
<button id='new-map'>Generate</button>
Tile size: <input type='number' id='select-tileSize' min='10' max='40' value='20'>
<div id='terrain-container'></div>
<div id='map-container'></div>
<p><button id='import-button'>Import</button> - paste your JSON map object here</p>
<textarea id='import-map'></textarea>
<p><button id='export-button'>Export</button> - copy this</p>
<textarea id='export-map'></textarea>
CSS
div, img, p, a, button, input {
box-sizing: border-box;
}
input {
width: 60px;
}
textarea {
width: 90%;
height: 100px;
}
#map-container {
width: 100%;
padding: 20px;
min-height: 150px;
max-height: 400px;
overflow: auto;
background-color: silver;
text-align: center;
}
#map-border {
border: 2px solid black;
line-height: 0px;
margin: auto;
}
#map-border .tile {
display: inline-block;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
background-size: cover;
}
#map-border .tile:hover {
cursor: pointer;
box-shadow: inset 0 0 0 1px #000;
}
#map-border .selected-tile {
box-shadow: inset 0 0 0 1px #fff;
}
#terrain-container {
width: 100%;
height: 100px;
overflow: auto;
background-color: silver;
margin: 20px auto;
}
#terrain-container .terrain {
width: 40px;
height: 40px;
display: inline-block;
margin: 5px;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
}
#terrain-container .terrain:hover {
cursor: pointer;
box-shadow: inset 0 0 0 1px #000;
}
#terrain-container .selected-terrain {
box-shadow: inset 0 0 0 1px #fff;
}
JavaScript
const path = "http://images.neopets.com/nq/tp/",
urls = [
"grassland",
"water_iso",
"forest",
"mountain",
"hills",
"jungle",
"desert",
"dirt",
"city",
"castle",
"ruins",
"cave",
"stone",
"swamp",
"snow",
"cave_ent",
"cave_exit",
"cave_up",
"cave_down",
"dungeon",
"dungeon_barrel",
"dungeon_crate",
"dungeon_chair",
"dungeon_table",
"dungeon_pillar",
"dungeon_crate",
"dungeon_bed",
"dungeon_portal",
"dungeon_door",
"dungeon_up",
"dungeon_down"
];
class MapEditor {
constructor(){
this.rows = 10;
this.cols = 10;
this.tileSize = 20;
this.terrain = 1;
this.selectedTile = {x: 0, y: 0};
this.terrainOptions();
this.blankMap();
}
setSize(w,h){
this.rows = h;
this.cols = w;
}
setTileSize(px){
this.tileSize = px;
let c = this.getContainerSize();
$("#map-border").css("height", c.h + "px").css("width", c.w + "px");
$("#map-border .tile").css("height", px + "px").css("width", px + "px");
}
// Add 4px to account for the 2px-thick borders
getContainerSize(){
return {
w: (this.cols * this.tileSize) + 4,
h: (this.rows * this.tileSize) + 4
};
}
blankMap(terrain = this.terrain){
this.map = [];
for(let y = 0; y < this.rows; y++){
let row = [];
for(let x = 0; x < this.cols; x++){
row.push(terrain);
}
this.map.push(row);
}
this.renderMap();
}
renderMap(){
let c = this.getContainerSize();
let html = "<div id='map-border' style='width:" + c.w...