JSFiddle - React, Tailwind, and code Playground
by Gwyn Milcote
HTML
<div id='df-container'>
<!-- Title screen. Choose an option. -->
<div id='df-screen-title' style='display: none;'>
// generate a world
// load a world (input json)
// start a new fortress
// continue a fortress (input json)
</div>
<!-- Canvas in here. -->
<div id='df-map'></div>
<!-- Visible in TR corner, if menu hidden. -->
<button id='show-menu-btn' style='display: none;'>Menu</button>
<div id='df-menu'>
<div id='df-menu-top'>
<!-- Toggle menu visibility. -->
<button id='hide-menu-btn'>Hide</button>
<!-- Show index tab. -->
<button class='df-menu-tab-btn' data-tab='index'>Index</button>
<!-- Pause/resume game. -->
<button id='pause-btn'>Pause</button>
<!-- Bring up the settings menu. -->
<button id='settings-btn'>Settings</button>
</div>
<!-- These inner menus are switched in/out, updated. -->
<div id='df-menu-inner'>
Hovering pixels: <span id='map-hover-coords'></span>
<br>Hovered tile: <span id='map-hover-tile'></span>
<br>Selected tiles: <span id='map-selected-tiles'></span>
<hr>
<!-- Switch between tabs. -->
<div class='df-menu-tab' id='df-menu-tab-index'>
<button class='df-menu-tab-btn' data-tab='lists'>Lists</button>
<button class='df-menu-tab-btn' data-tab='mining'>Mining</button>
<button class='df-menu-tab-btn' data-tab='farming'>Farming</button>
</div>
<!-- Bring up lists of things, in separate boxes. -->
<div class='df-menu-tab' id='df-menu-tab-lists' style='display: none;'>
<p>Lists: a record of everything in the database. Some details will be vague if you don't have a proper bookkeeper.</p>
<button class='show-list-btn' data-list='dwarves'>Dwarves</button>
<button class='show-list-btn'...
CSS
div, img, p, button, table, tr, td, canvas {
box-sizing: border-box;
}
#df-container {
width: 800px;
height: 500px;
background-color: #222;
color: #ccc;
position: relative;
font-family: monospace;
font-size: 13px;
}
#df-container button {
font-family: monospace;
font-size: 13px;
padding: 2px 5px;
background-color: #aaa;
border: 1px solid #000;
border-radius: 0;
transition: 0.1s;
}
#df-container button:hover {
cursor: pointer;
background-color: #eee;
transition: 0.1s;
}
#df-map {
position: absolute;
top: 0;
left: 0;
width: 500px; /* 780 if expanded */
height: 100%;
border: 10px solid #aaa;
}/*
#df-map canvas {
position: absolute;
top: 0;
left: 0;
background-color: orange;
}*/
#show-menu-btn {
position: absolute;
top: 10px;
right: 10px;
}
#df-menu {
position: absolute;
top: 0;
right: 0;
width: 300px;
height: 100%;
border: 10px solid #aaa;
padding: 10px;
}
#df-menu-top {
display: flex;
flex-flow: row;
justify-content: space-between;
border-bottom: 1px solid #aaa;
padding-bottom: 5px;
margin-bottom: 5px;
}
#df-menu-inner {
height: 430px;
overflow: auto;
}
JavaScript
/*
* Return index of object, or -1 if not found.
* Needed for un-selecting tiles.
*/
function arrayIncludesObject(array, object){
let o = JSON.stringify(object);
return array.findIndex(row => JSON.stringify(row) == o);
}
class GameHandler {
constructor(){
/*
* Visual size of the map canvas.
* Taking 10px CSS borders into account.
*/
this.canvasWidth = 480; // 780 at max
this.canvasHeight = 480;
/*
* Camera position. Starts in top left corner.
* Add these numbers to a tile's indices when
* looking up the actual tile clicked on screen.
*/
this.cameraX = 0;
this.cameraY = 0;
/*
* Which tab is currently open.
*/
this.showMenuTab("index");
/*
* Whether to detect cursor/click on canvas.
* Set to true when designating, etc.
*/
this.detectHover = true;
this.detectClick = true;
}
showMenu(){
$("#show-menu-btn").hide();
$("#df-menu").show();
$("#df-map").width(480);
this.canvasWidth = 480;
Canvas.setSize(480, 480);
Canvas.update();
}
hideMenu(){
$("#df-menu").hide();
$("#show-menu-btn").show();
$("#df-map").width(780);
this.canvasWidth = 780;
Canvas.setSize(780, 480);
Canvas.update();
}
setCanvasSize(w, h){
this.canvasWidth = w;
this.canvasHeight = h;
}
showMenuTab(tab){
this.menuTab = tab;
$(".df-menu-tab").hide();
$("#df-menu-tab-" + tab).show();
}
}
/*
* One canvas is displayed on page. Made of 2 layers.
* The map, then a layer for highlighting/selecting tiles.
*/
class CanvasHandler {
constructor(){
this.tileSize = 16;
/*
* Store XY coords of relevant tiles.
* For selecting stuff, before confirming.
...