JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

<div id='nq-container'>

    <!-- Top bar -->
    <div id='nq-status'>
    
        <!-- Name and level -->
        <div class='section'>
            Level: <span class='current-level'>1</span>
            <br>
            Skill points: <span class='skill-points-count'>0</span>
        </div>
        
        <!-- HP bar -->
        <div class='section'>
            HP: <span class='hp-current'>20</span>/<span class='hp-max'>20</span>
            <br>
            <div class='bar-outer hp-outer'>
                <div class='bar-inner hp-inner' style='width:60%;'></div>
            </div>
        </div>
        
        <!-- EXP bar -->
        <div class='section'>
            EXP: <span id='exp-current'>0</span>/<span id='exp-needed'>10</span>
            <br>
            <div class='bar-outer exp-outer'>
                <div class='bar-inner exp-inner' style='width:60%;'></div>
            </div>
        </div>
        
        <!-- Open Skill or Item screen -->
        <!-- Remove Talk/Battle later -->
        <div class='section'>
            <button id='nq-skills-open'>Skills</button>
            <button id='nq-items-open'>Items</button>
            <!--
                <button id='nq-npc-open'>Talk</button>
                <button id='nq-battle-open'>Battle</button>
            -->
            <button id='nq-options-open'>Options</button>
        </div>
        
    </div>
    
    <!-- OVERWORLD -->
    
    <!-- Map tiles -->
    <div id='nq-map-container'>[tiles here]</div>
    
    <!--  -->
    <div id='nq-compass'>
        <div class='nq-arrow nq-n' data-dir='n'>N</div>
        <div class='nq-arrow nq-s' data-dir='s'>S</div>
        <div class='nq-arrow nq-e' data-dir='e'>E</div>
        <div class='nq-arrow nq-w' data-dir='w'>W</div>
        <div class='nq-arrow nq-ne' data-dir='ne'>NE</div>
        <div class='nq-arrow nq-nw' data-dir='nw'>NW</div>
        <div class='nq-arrow nq-se' data-dir='se'>SE</div>
        <div class='nq-arrow nq-sw'...

CSS

/* Stop padding, borders etc changing sizes. */
div, span, img, button, p, a {
    box-sizing: border-box;
}

/* The game 'window'. Everything happens inside here. */
#nq-container {
    width: 700px;
    height: 500px;
    margin: 20px auto;
    position: relative;
    background-color: ivory;
    border: 2px solid rgba(0, 0, 0, 0.4);
    border-radius: 5px;
    box-shadow: 0 5px 10px 3px rgba(0, 0, 0, 0.25);
    font-size: 16px;
}

/* Give all buttons a consistent style. */
#nq-container button {
    padding: 5px 10px;
    margin: 0 2px;
    background-color: gold;
    border: 1px solid brown;
    color: brown;
    transition: 0.3s;
    border-radius: 3px;
}
#nq-container button:hover {
    cursor: pointer;
    background-color: ivory;
    transition: 0.3s;
}

/* Popup screens for npc, battle, skills, inventory. */
.nq-popup-screen {
    display: none;
    position: absolute;
    top: 18px;
    left: 18px;
    width: 660px;
    height: 460px;
    background-color: ivory;
    border: 1px solid coral;
    border-radius: 5px;
    box-shadow: 0 0 20px 10px rgba(0, 0, 0, 0.4);
}

/* Top bar with HP, EXP, buttons. */
#nq-status {
    position: absolute;
    top: 10px;
    left: 20px;
    width: 660px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    line-height: 24px;
    border: 1px dotted grey;

}
#nq-status .section:not(:nth-child(1)) {
    text-align: center;
}


/* HP and EXP, both yours and enemy's. */
.bar-outer {
    display: inline-block;
    width: 120px;
    height: 20px;
    position: relative;
    border-radius: 3px;
}
.bar-inner {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
}

/* Containers. */
.hp-outer {
    border: 1px solid darkgreen;
}
.exp-outer {
    border: 1px solid brown;
}
/* Inner sections. Width will vary. */
.hp-inner {
    background-color: green;
}
.exp-inner {
    background-color: gold;
}

#nq-map-container {
    position: absolute;
    top: 70px;
    left: 20px;
    width:...

JavaScript

// This object acts like a database.
const db = {

    // Paths for loading images
    tilePath: "http://images.neopets.com/nq/tp/",
    enemyPath: "http://images.neopets.com/nq/m/",
    
    // Map landmarks. Can teleport between them, for testing.
    cities: [
        {id: 1, name: "Neopia City", map: 0, x: 7, y: 0},
        {id: 2, name: "Jungle Ruins", map: 0, x: 8, y: 8},
        {id: 3, name: "Swamp Edge City", map: 0, x: 12, y: 4},
        {id: 4, name: "Temple of Roo", map: 0, x: 15, y: 11},
        {id: 5, name: "Mountain Fortress", map: 0, x: 16, y: 13},
        {id: 6, name: "Kal Panning", map: 0, x: 13, y: 13},
        {id: 7, name: "Two Rings Palace", map: 0, x: 11, y: 9}
    ],
    
    // Table of enemies to look up/choose from.
    enemies: [
        {
            id: 1, name: "a fire imp", 
            url: "400000_afireimp", hp: [9,16],
            exp: [5,14], battleMoves: [2],
            drops: [1,2], dropChance: 20,
            victoryQuotes: [3], defeatQuotes: [1,2]
        },{
            id: 2, name: "a snow imp", 
            url: "400001_asnowimp", hp: [9,16],
            exp: [5,14], battleMoves: [2],
            drops: [1,2], dropChance: 20,
            victoryQuotes: [3], defeatQuotes: [1,2]
        },{
            id: 3, name: "a plains lupe",
            url: "400002_aplainswolf", hp: [12,15],
            exp: [5,14], battleMoves: [2],
            drops: [1,6], dropChance: 20,
            victoryQuotes: [4], defeatQuotes: [1]
        },{
            id: 4, name: "a plains aisha", 
            url: "400003_aplainscougar", hp: [15,19],
            exp: [5,14], battleMoves: [2,3],
            drops: [2,6], dropChance: 10,
            victoryQuotes: [4], defeatQuotes: [1,2]
        },{
            id: 5, name: "a grey lupe",
            url: "400004_agraywolf", hp: [18,24],
            exp: [5,14], battleMoves: [2,3],
            drops: [6], dropChance: 10,
            victoryQuotes: [3,4], defeatQuotes: [2]
        },{
           ...