Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                         
<!-- PANEL -->

<div class="Panel">
  
  <section class="Panel-section Panel-section--title">
    <h1 class="Panel-title">Hero<wbr/>Quest <small class="Panel-tagline">Map creator</small></h1>
    <ul class="Panel-basicInstructions">
      <li>
        <div class="MouseIcon MouseIcon--leftBtn"></div>
        Left-click &amp; drag to draw.
      </li>
      <li>
        <div class="MouseIcon MouseIcon--rightBtn"></div>
        Right-click &amp; drag to delete.
      </li>
      <li>
        <div class="KeyIcon KeyIcon--spacebar"></div>
        Hold 'Space' &amp; drag to pan.
      </li>
      <li>
        Ctrl &plusmn; to zoom ;)
      </li>
    </ul>
  </section>

  <section class="Panel-section  Panel-section--controls">
    <div class="Panel-controls">
      <div class="Panel-control-col">
        <div class="Panel-btnGrp Panel-btnGrp--rotation">
          <h2 class="Panel-btnGrpTitle">Rotation</h2>
          <label class="btn btn-default btn-top" data-toggle="tooltip">
            <input type="radio" name="rotation" id="radio-rotation270"/>270&deg;
          </label>
          <label class="btn btn-default btn-left" data-toggle="tooltip">
            <input type="radio" name="rotation" id="radio-rotation180"/>180&deg;
          </label>
          <label class="btn btn-default btn-mid" data-toggle="tooltip">
            <input type="radio" name="rotation" id="radio-rotationRand"/>Rand
          </label>
          <label class="btn btn-default btn-right" data-toggle="tooltip">
            <input type="radio" name="rotation" id="radio-rotation0"/>0&deg;
          </label>
          <label class="btn btn-default btn-bottom" data-toggle="tooltip">
            <input type="radio" name="rotation" id="radio-rotation90"/>90&deg;
          </label>
        </div>
      </div>
      <div class="Panel-control-col">
        <div class="Panel-btnGrp Panel-btnGrp--mode">
          <h2 class="Panel-btnGrpTitle">Paint mode</h2>
          <label class="btn btn-default btn-top" data-toggle="tooltip">
            <input type="radio" name="mode" id="radio-modeRand"/>Rand
          </label>
          <label class="btn btn-default btn-mid" data-toggle="tooltip">
            <input type="radio" name="mode" id="radio-modeSingle"/>Single tile
          </label>
          <label class="btn btn-default btn-bottom" data-toggle="tooltip">
            <input type="radio" name="mode" id="radio-modeWhole"/>Whole object
          </label>
        </div>
      </div>
    </div>
  </section>

  <section class="Panel-section  Panel-section--palette">
    <ul class="Panel-paletteTabs nav nav-tabs" role="tablist">
      <li><a id="tab-floors" role="tab" data-toggle="tooltip" title="Floor tiles"></a></li>
      <li><a id="tab-walls" role="tab" data-toggle="tooltip" title="Walls"></a></li>
      <li><a id="tab-triggers" role="tab" data-toggle="tooltip" title="Triggers"></a></li>
      <li><a id="tab-furniture" role="tab" data-toggle="tooltip" title="Furniture"></a></li>
      <li><a id="tab-creatures" role="tab" data-toggle="tooltip" title="Creatures"></a></li>
    </ul>
    <div class="Panel-paletteWrapper">
      <div class="Panel-palette" id="palette-floors"></div>
      <div class="Panel-palette" id="palette-walls"></div>
      <div class="Panel-palette" id="palette-triggers"></div>
      <div class="Panel-palette" id="palette-furniture"></div>
      <div class="Panel-palette" id="palette-creatures"></div>
    </div>
  </section>

  <section class="Panel-section  Panel-section--actions">
    <div class="Panel-actions">
      <a id="btn-clear" class="btn btn-primary" data-toggle="modal" data-target="#modal-clearAll">Clear</a>
      <a id="btn-save" class="btn btn-primary" data-toggle="modal" data-target="#modal-save">Save</a>
      <a id="btn-load" class="btn btn-primary" data-toggle="modal" data-target="#modal-load">Load</a>
    </div>
  </section>

</div> <!-- /.Panel -->


<!-- GRID -->

<div class="Grid">
  <div class="Grid-selector addMode"></div>
  <div class="Tiles Tiles-creatures"></div>
  <div class="Tiles Tiles-furniture"></div>
  <div class="Tiles Tiles-triggers"></div>
  <div class="Tiles Tiles-walls"></div>
  <div class="Tiles Tiles-floors"></div>
</div>



<!-- MODALS -->

<div class="modal fade" id="modal-clearAll">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h4 class="modal-title">Clear all tiles?</h4>
        <p>This will erase all tiles on the current map.<br>All unsaved changes will be lost.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button id="modalBtn-clearAll" type="button" class="btn btn-primary" data-dismiss="modal">Clear All</button>
      </div>
    </div>
  </div>
</div><!-- /.modal -->

<div class="modal fade" id="modal-save">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h4 class="modal-title">Save Map</h4>
        <p>Saves to a <code>.json</code> map file for later use.</p>
        <div class="form-group">
          <label for="modalInput-fileName">File name</label>
          <input type="text" class="form-control" id="modalInput-fileName" value="trial" placeholder="Enter file name">
        </div>
        <div class="form-group">
          <label for="modalTextarea-save">Map data</label>
          <textarea id="modalTextarea-save" class="form-control" rows="6" readonly>No map yet!</textarea>
        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button id="modalBtn-save" type="button" class="btn btn-primary" data-dismiss="modal">Save to file</button>
      </div>
    </div>
  </div>
</div><!-- /.modal -->

<div class="modal fade" id="modal-load">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-body">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h4 class="modal-title">Load map</h4>
        <p>Select a <code>.json</code> map file to load map data from.<br><strong>Warning:</strong> this will clear the current map.</p>
        <input type="file" id="modalInput-loadFile">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button id="modalBtn-load" type="button" class="btn btn-primary" data-dismiss="modal">Load</button>
      </div>
    </div>
  </div>
</div><!-- /.modal -->



              
            
!

CSS

              
                
// DEPENDENCIES: 
//  Bootstrap CSS
//  Font: heroQuestRegular (base64 encoded CSS file)
//  Panel UI (Pen)


/* Variables
*********************************/
@cellSize: 70px;
@gridColour: black;
@lineColour: white;
@tileColour: darken(@gridColour,20%);

// Depth
@creatureDepth:    5;
@furnitureDepth:   4;
@triggerDepth:     3;
@wallDepth:        2;
@floorDepth:       1;


/* Mixins
*********************************/
.grid( @lineColour; @cellSize; @cellCount ) {
  background-image: 
    linear-gradient(fade(@lineColour,30%) 2px, transparent 0),
    linear-gradient(90deg, fade(@lineColour,30%) 2px, transparent 0),
    linear-gradient(fade(@lineColour,10%) 1px, transparent 0),
    linear-gradient(90deg, fade(@lineColour,10%) 1px, transparent 0),
    url('http://www.davidelrizzo.com/heroquestonline/leveleditor/images/woodtexture_bg_dark.jpg');
  background-size:
    @cellSize*@cellCount @cellSize*@cellCount, 
    @cellSize*@cellCount @cellSize*@cellCount, 
    @cellSize @cellSize, 
    @cellSize @cellSize,
    240px 182px;
}


/* Global styles
*********************************/
*, :before, :after { box-sizing: border-box; }
body {
  position: relative;
  display: flex;
  height: 100vh;
  width: 100vw;
}


/* Control Panel
*********************************/
.Panel {
  position: fixed;
  width: 25%;
  max-width: 346px;
  margin-left: 0.5rem;
  opacity: 0.95;
}
.Panel-btnGrp input[type='radio'] {
  display: none;
}

.Panel-paletteSwatch {
  @swatch-margin: 5px;
  position: relative;
  flex: 0 1 auto;
  width: @cellSize;
  height: @cellSize;
  margin: @swatch-margin;
  transition: all 0.4s;
  cursor: pointer;
  border-radius: 3px;
  box-shadow:  inset 0 0 0 (@cellSize/2) transparent;
  &.is-loading:before {
    @size:       25px;
    position: absolute;
    content: "";
    top: 50%;
    left: 50%;
    width: @size;
    height: @size;
    margin: -(@size/2) 0 0 -(@size/2);
    border: 3px solid rgba(255,255,255,0.3);
    border-right-color: transparent;
    border-radius: 50%;
    animation: rotating 0.75s linear infinite;
  }
  @-webkit-keyframes rotating {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
  }
  &:hover, &:focus {
    box-shadow: inset 0 0 0 (@cellSize/2) fade(black,40%);
  }
  &:active {
    box-shadow: inset 0 0 0 (@cellSize/2) fade(limegreen,10%);
  }
  #palette-creatures & {
    border-radius: 50%;
  }
  #palette-furniture & {
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
  }
  #palette-walls & {
    background-position: -20px 0;
  }
  &.is-active {
    box-shadow: inset 0 0 0 2px fade(limegreen,80%);
  }
}



/* Grid
*********************************/
.Grid {
  flex: 1;
  position: relative;
  overflow: hidden;
  user-select: none;
  background-color: @gridColour;
  .grid( @lineColour; @cellSize; 5 );
  z-index: 0;
}
.Grid-selector {
  position: absolute;
  width: @cellSize;
  height: @cellSize;
  border-radius: 2px;
  animation: pulse 2s infinite ease-in-out;
  z-index: 100;
  box-shadow: 0 0 30px currentColor;
  transform-origin: @cellSize/2 @cellSize/2;
  
  &.addMode { color: limegreen; }
  &.deleteMode { color: crimson; }
  
  &:after {
    content: "\25B6";
    position: absolute;
    top: 0;
    right: 0;
    width: @cellSize;
    margin-right: -4px;
    text-align: right;
    font-size: 10px;
    line-height: @cellSize;
    transform-origin: center;
  }
  &[data-rotationMode='random']:after { 
    content: "";
    text-align: center;
  }
}  
@keyframes pulse {
  0% { box-shadow: 0 0 30px currentColor }
  50% { box-shadow: 0 0 10px currentColor }
  100% { box-shadow: 0 0 30px currentColor }
}


/* Grid tiles
*********************************/

.Tiles {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  transform: translate3d(0,0,0);  // force GPU rendering
}
.Tiles-creatures {
  z-index: @creatureDepth;
  -webkit-filter: drop-shadow(-@cellSize/10 @cellSize/10 10px black);
}
.Tiles-furniture {
  z-index: @furnitureDepth;
  -webkit-filter: drop-shadow(-@cellSize/20 @cellSize/20 3px black);
}
.Tiles-triggers {
  z-index: @triggerDepth;
}
.Tiles-walls {
  z-index: @wallDepth;
  -webkit-filter: 
    drop-shadow( -@cellSize/20 @cellSize/20 @cellSize/8 black )
    drop-shadow( -@cellSize/20 @cellSize/20 @cellSize/8 black )
    drop-shadow( -@cellSize/20 @cellSize/20 @cellSize/8 rgba(0,0,0,0.5) );
}
.Tiles-floors {
  -webkit-filter: drop-shadow(-@cellSize/10 @cellSize/10 10px black);
  z-index: @floorDepth;
}


.Tile {
  position: absolute;
  width: @cellSize; 
  height: @cellSize;
  animation: block-in 0.15s 0 ease-out;
  //box-shadow: 0 0 5px fade(pink,50%);
}
@keyframes block-in {
  0% { 
    width: 0; 
    height: 0;
    margin: @cellSize/2 @cellSize/2;
    border-radius: 50%;
  }
  100% { 
    width: @cellSize; 
    height: @cellSize;
    margin: 0 0;
    border-radius: 0;
  }
}

.Tile-creature {}
.Tile-trigger {}
.Tile-furniture {}
.Tile-wall {
  margin: -3px 0 0 -3px;
  width: @cellSize*2 + 6px;
  height: @cellSize + 6px;
  background-repeat: no-repeat;
  transform-origin: @cellSize/2+3px @cellSize/2+3px;
  //box-shadow: 0 0 5px pink; 
}
.Tile-floor {}



              
            
!

JS

              
                /*

  Allows you to create custom maps for the classic board game HeroQuest.  Part of a full HTML5 game that has been 'coming soon' for several years. It is mostly an exercise in UI design and teaching myself JS web apps.
  (See: http://www.rockpapershotgun.com/2011/08/27/cardboard-children-heroquest)
  "My whole gaming life has been one long attempt to recapture the magic of Hero Quest." - Daiv (sums it up)

*/

/*****************************
    
    POSSIBLE ENHANCEMENTS
    - auto save current map to cookie/local storage?
    - drag to re-size control panel
    - select specific single (floor) tile offset
    - review creature images
    - convert campaignData to JSON (probably not, data might change completely anyway in actual game)
    - zoom in and out with mouse wheel (just use Browser zoom, better, easier!)
    - BUG: control btns/swatches should not be selectable before tile swatches load
    
******************************/

// DEPENDENCIES: 
//  jQuery (latest)
//  Bootstrap (3.1.0, all components)
//  Pt.js (Pen)
//  campaignData.js (Pen)
//  FileSaver.js (to save map files, https://github.com/eligrey/FileSaver.js)
//  Panel UI (Pen)

// Much of the JS below is written in a way that would make baby jesus cry, I acknowledge that and endeavour to improve on my next project :)



(function () {
  "use strict";
  
  var UNIT = campaignData.campaignSettings.gridSize;
  var Main = { 
    
    curCell: new Pt(0,0),
    offset: new Pt(0,0),
    curTileClass: null,
    curTileData: null,
    tileType: null,      // [ "floors", "walls", "triggers", "furniture", "creatures" ]
    paintMode: null,     // [ "random", x:y", "whole" ]
    rotationMode: null,  // [ "random", 0, 90, 180, 270 ]
    groupCounter: 0,

    cursor: {
      enable: function() { 
        $("body").on( "mousemove", ".Grid", Main.cursor.run );
        $('.Grid').css( 'cursor', 'cell' );
        $(".Grid-selector").css({
          "left": (Main.curCell.x * UNIT) + Main.offset.x,
          "top":  (Main.curCell.y * UNIT) + Main.offset.y
        }).show();
      },
      disable: function() {
        $("body").off( "mousemove", ".Grid", Main.cursor.run );
        $(".Grid-selector").hide();
      },
      run: function(e) {
        var hoverCell = new Pt( 
          Math.floor( (e.pageX - Main.offset.x) / UNIT ), 
          Math.floor( (e.pageY - Main.offset.y) / UNIT ) 
        );
        var pos = new Pt(
          (e.pageX-Main.offset.x) - hoverCell.x*UNIT,
          (e.pageY-Main.offset.y) - hoverCell.y*UNIT
        );
        $(".Grid-selector").css({
          "left": (hoverCell.x * UNIT) + Main.offset.x,
          "top":  (hoverCell.y * UNIT) + Main.offset.y
        });
      },
      redraw: function() {
        //console.log( "cursor.redraw()" );
        if( Main.paintMode == "whole" ) {
          $(".Grid-selector").css({ "width":Main.curTileData.width, "height":Main.curTileData.height });
        } else {
          $(".Grid-selector").css({ "width":UNIT, "height":UNIT });
        }
        if( Main.rotationMode == "random" ) {
          $(".Grid-selector").css({ "transform":"rotate(0deg)" });
        } else {
          $(".Grid-selector").css({ "transform":"rotate("+parseInt(Main.rotationMode)+"deg)" });
        }
      }
    },

    modify: {
      enable: function() {
        $(".Grid").on( "mousedown", Main.modify.start );
        $(".Grid").on( "mouseup", Main.modify.end );
        $(".Grid").on("contextmenu", function () { return false; });
      },
      disable: function() {
        $(".Grid").off( "mousedown", Main.modify.start );
        $(".Grid").off( "mousemove", Main.modify.run );
      },
      start: function(e) {
        var hoverCell = new Pt( 
          Math.floor( (e.pageX - Main.offset.x) / UNIT ), 
          Math.floor( (e.pageY - Main.offset.y) / UNIT ) 
        );
        Main.curCell = hoverCell;
        if( e.which == 1 ) {
          Main.addTiles(Main.curTileClass, Main.getSet(), Main.paintMode, Main.rotationMode);
        } else if ( e.which == 3 ) {
          $(".Grid-selector").removeClass("addMode").addClass("deleteMode").hide().show(1);
          Main.deleteTiles(Main.curTileClass, Main.getSet());
        }
        $(".Grid").on( "mousemove", Main.modify.run );
      },
      run: function(e) {
        var hoverCell = new Pt( 
          Math.floor( (e.pageX - Main.offset.x) / UNIT ), 
          Math.floor( (e.pageY - Main.offset.y) / UNIT ) 
        );
        if( !Main.curCell.equalTo(hoverCell) ) {
          Main.curCell = hoverCell;
          if( e.which == 1 ) {
            Main.addTiles(Main.curTileClass, Main.getSet(), Main.paintMode, Main.rotationMode);
          } else if ( e.which == 3 ) {
            Main.deleteTiles(Main.curTileClass, Main.getSet());
          }
        }
      },
      end: function(e) {
        $(".Grid-selector").removeClass("deleteMode").addClass("addMode").hide().show(1);
      }
    },

    pan: {
      lastPt: new Pt(0,0),
      enable: function() {
        $(document).on( "keydown", Main.pan.start );
        $(document).on( "keyup", Main.pan.disable );
      },          
      disable: function(e) {
        if( e.which == 32 ) {
          e.preventDefault();
          $(".Grid").off( "mousedown", Main.pan.begin );
          $(".Grid").off( "mouseup", Main.pan.stop );
          Main.cursor.enable();
          Main.modify.enable();
        }
      },
      start: function(e) {
        if( e.which == 32 ) {
          e.preventDefault();
          Main.cursor.disable();
          Main.modify.disable();
          $('.Grid').css( 'cursor','all-scroll' );
          $(".Grid").on( "mousedown", Main.pan.begin );
          $(".Grid").on( "mouseup", Main.pan.stop );
        }
      },
      begin: function(e) {
         Main.pan.lastPt = new Pt(e.pageX,e.pageY);
        $(".Grid").on( "mousemove", Main.pan.run );
      },
      run: function(e) {
        Main.offset.x += ( e.pageX - Main.pan.lastPt.x );
        Main.offset.y += ( e.pageY - Main.pan.lastPt.y );
        $(".Tiles").css({
          "left": Main.offset.x,
          "top":  Main.offset.y,
        });
        $(".Grid").css( "background-position", Main.offset.x+"px "+Main.offset.y+"px" );
        Main.pan.lastPt = new Pt(e.pageX,e.pageY);
      },
      stop: function(e) {
        $(".Grid").off( "mousemove", Main.pan.run );
      },
      setOffset: function(x,y) {
        Main.offset.x = x;
        Main.offset.y = y;
        $(".Tiles").css({
          "left": Main.offset.x,
          "top":  Main.offset.y,
        });
        $(".Grid").css( "background-position", Main.offset.x+"px "+Main.offset.y+"px" );
        console.log("pan.setOffset("+x+","+y+")");
      }
    },
    
    keylisten: {
      enable: function() {
        $(document).on( "keyup", Main.keylisten.run );
      },
      disable: function() {
        $(document).off( "keyup", Main.keylisten.run );
      },
      run: function(e) {
        switch(e.which) {
          case 68: case 39: Main.setRotationMode("0"); break;
          case 83: case 40: Main.setRotationMode("90"); break;
          case 65: case 37: Main.setRotationMode("180"); break;
          case 87: case 38: Main.setRotationMode("270"); break;
          case 82: Main.setRotationMode("random"); break;
          case 90: Main.setPaintMode("random"); break;
          case 88: Main.setPaintMode("0:0"); break;
          case 67: Main.setPaintMode("whole"); break;
          case 49: Main.selectTab("tab-floors"); break;
          case 50: Main.selectTab("tab-walls"); break;
          case 51: Main.selectTab("tab-triggers"); break;
          case 52: Main.selectTab("tab-furniture"); break;
          case 53: Main.selectTab("tab-creatures"); break;
        }
      }
    },
    
    zoom: {
      enable: function() {
        // set intitial grid size
        /*var count = 5;
        var val = 
            UNIT*count +"px "+ UNIT*count +"px, "+
            UNIT*count +"px "+ UNIT*count +"px, "+
            UNIT +"px "+ UNIT +"px, "+
            UNIT +"px "+ UNIT +"px";*/
        //console.log( $(".Grid").css("backgroundSize") );
        //$(".Grid").css({"backgroundSize": val});
        //console.log( val );
      }
    },
    
    getTilesAt: function(pt) {
      var list = [];
      $(".Tile[data-x='"+pt.x+"'][data-y='"+pt.y+"']").each( function() {
        var classNames = this.className.split(" ");
        for( var i=0 ; i<classNames.length ; i++ ) {
          if( classNames[i].match(/Tile-\w+/) ) {
            list.push( { "type":classNames[i], "rot":$(this).attr("data-rotation") } );
          }
        }
      });
      console.log( "tilesAt("+pt.x+","+pt.y+") = "+JSON.stringify(list) );
      return list;
    },
    
    getSet: function() {
      if( Main.paintMode != "whole" ) {
        return [new Pt(Main.curCell.x,Main.curCell.y)];
      } else {
        var pts = [];
        if( Main.rotationMode == "90" ) {
          for( var x=0 ; x < Main.curTileData.unitHeight ; x++ ) {
            for( var y=0 ; y < Main.curTileData.unitWidth ; y++ ) {
              pts.push( new Pt( Main.curCell.x+(-x), Main.curCell.y+y ) );
            }
          }
        } else if( Main.rotationMode == "180" ) {
          for( var x=0 ; x < Main.curTileData.unitWidth ; x++ ) {
            for( var y=0 ; y < Main.curTileData.unitHeight ; y++ ) {
              pts.push( new Pt( Main.curCell.x+(-x), Main.curCell.y+(-y) ) );
            }
          }
        } else if( Main.rotationMode == "270" ) {
          for( var x=0 ; x < Main.curTileData.unitHeight ; x++ ) {
            for( var y=0 ; y < Main.curTileData.unitWidth ; y++ ) {
              pts.push( new Pt( Main.curCell.x+x, Main.curCell.y+(-y) ) );
            }
          }
        } else {
          for( var x=0 ; x < Main.curTileData.unitWidth ; x++ ) {
            for( var y=0 ; y < Main.curTileData.unitHeight ; y++ ) {
              pts.push( new Pt( Main.curCell.x+x, Main.curCell.y+y ) );
            }
          }
        }
        return pts;
      }
    },
    
    //lastTile: null,
    addTiles: function(tileType,pts,paintMode,rotMode) {
      Main.groupCounter++;
      console.log( "addTiles("+tileType+","+JSON.stringify(pts)+",paintMode:"+paintMode+",rotMode:"+rotMode+") group:"+Main.groupCounter );
      
      var lastTile;
      if( rotMode == '0' || rotMode == '180' ) {
         lastTile = new Pt(0,1);
      } else if ( rotMode == '90' || rotMode == '270' ) {
         lastTile = new Pt(1,0);
      }
      
      for( var i=0 ; i < pts.length ; i++ ) {
        // get rotation
        var rot;
        if( rotMode == 'random' ) { 
          rot = Math.floor(Math.random()*4)*90;
        } else { 
          rot = rotMode;
        }
        // get offset
        var off = new Pt(0,0);
        if( paintMode == 'random' ) {
          off.x = Math.floor( Math.random()*(Main.curTileData.unitWidth+1) )*UNIT;
          off.y = Math.floor( Math.random()*(Main.curTileData.unitHeight+1) )*UNIT;
        } else if ( paintMode == 'whole' ) {
          if( rotMode == '0' || rotMode == '180' ) {
            
            lastTile.y -= 1; 
            if( lastTile.y <= -Main.curTileData.unitHeight ) {
              lastTile.x -= 1;
              lastTile.y = 0;
            }
            
          } else if ( rotMode == '90' || rotMode == '270' ) {
            
            lastTile.x -= 1;
            if( lastTile.x <= -Main.curTileData.unitWidth ) {
              lastTile.y -= 1;
              lastTile.x = 0;
            }
            
          } else {
            console.log( "Error: paintmode:whole, rotmode:random");
          }
          off.x = lastTile.x * UNIT;
          off.y = lastTile.y * UNIT;
          
        } else {
          var offset = paintMode.split(":");
          off.x = Math.floor(parseInt(offset[0])*Main.curTileData.width)*UNIT;
          off.y = Math.floor(parseInt(offset[1])*Main.curTileData.height)*UNIT;
        }
        Main.addTile(tileType,pts[i],rot,off,Main.groupCounter,Main.curTileData.path);
      }
    },
    
    addTile: function(tileType,pt,rot,off,grp,path,delay) {
      if(!delay) delay = 0;
      
      if( tileType=="Tile-wall" && rot=="180" ) {
        pt.x--; rot="0";
      } else if( tileType=="Tile-wall" && rot=="270" ) {
        pt.y--; rot="90";
      }
      
      // delete existing tile
      var overlayTiles = Main.getTilesAt(pt);
      if( overlayTiles.length > 0 ) {
        for( var i=0 ; i<overlayTiles.length ; i++ ) {
          if( overlayTiles[i].type==tileType ) {
             if( overlayTiles[i].type!="Tile-wall" ) {
               Main.deleteTile(tileType,pt);
             } else {
               if( rot == overlayTiles[i].rot ) {
                 Main.deleteTile(tileType,pt,overlayTiles[i].rot);
               }
             }
          } else {
            if( (
                  tileType=="Tile-creature" || 
                  tileType=="Tile-furniture" || 
                  tileType=="Tile-trigger"
                ) &&
                (
                  overlayTiles[i].type=="Tile-creature" || 
                  overlayTiles[i].type=="Tile-furniture" || 
                  overlayTiles[i].type=="Tile-trigger"
                )
               ) {
              Main.deleteTile(overlayTiles[i].type,pt);
            }
          }
        }
      }
      
      // append tile
      var a = document.createElement('a');
      a.href = path;
      var id = a.pathname.split('/').pop();
      id = $.trim( id.substr(0, id.lastIndexOf('.')) );

      var html = "<div class='Tile "+tileType+"' data-x='"+pt.x+"' data-y='"+pt.y+"' data-group='"+grp+"' data-rotation='"+rot+"' data-id='"+id+"'></div>";
      
      var container;
      switch(tileType) {
        case "Tile-creature": container = "Tiles-creatures"; break;
        case "Tile-furniture": container = "Tiles-furniture"; break;
        case "Tile-trigger": container = "Tiles-triggers"; break;
        case "Tile-wall": container = "Tiles-walls"; break;
        case "Tile-floor": container = "Tiles-floors"; break;
        default: console.log("Error: type("+tileType+") unknown");
      }
      var $newTile = $(html).appendTo("."+container);

      // set tile props
      $newTile.css({
        "left": pt.x * UNIT,
        "top":  pt.y * UNIT,
        "background-image": "url('"+path+"')",
        "background-position": off.x +"px "+ off.y + "px",
        "transform": "rotate("+rot+"deg)",
        "display": "none"
      });
      
      setTimeout( function(){
        $newTile.css("display","block");
      }, delay, $newTile);
      
      
      console.log( "addTile("+tileType+","+JSON.stringify(pt)+",rot:"+rot+",off:"+JSON.stringify(off)+") of group:"+grp );
    },
    
    deleteTiles: function(tileType,pts) {
      if( Main.rotationMode != "none" ) {
        console.log( "deleteTiles("+tileType+","+JSON.stringify(pts)+")" );
        for( var i=0 ; i < pts.length ; i++ ) {
          Main.deleteTile(tileType,pts[i],Main.rotationMode);
        }
      }
    },
    
    deleteTile: function(tileType,pt,rot) {
      
      if( tileType=="Tile-wall" && rot=="180" ) {
        pt.x--; rot="0";
      } else if( tileType=="Tile-wall" && rot=="270" ) {
        pt.y--; rot="90";
      }
      
      if( tileType == "Tile-furniture" ) {
        var grp = $("."+tileType+"[data-x='"+pt.x+"'][data-y='"+pt.y+"']").attr("data-group");
        $("."+tileType+"[data-group='"+grp+"']").remove();
        console.log( "deleteTile("+tileType+",("+pt.x+","+pt.y+")) for ALL of group:"+grp );
      } else if( tileType == "Tile-wall" && rot!=undefined ) {
        
        $("."+tileType+"[data-x='"+pt.x+"'][data-y='"+pt.y+"'][data-rotation='"+rot+"']").remove();
        console.log( "deleteTile("+tileType+",("+pt.x+","+pt.y+"),rotation:"+rot+")" );
        
      } else {
        $("."+tileType+"[data-x='"+pt.x+"'][data-y='"+pt.y+"']").remove();
        console.log( "deleteTile("+tileType+",("+pt.x+","+pt.y+"))" );
      }
    },
    
    setTileProperties: function(type, index) {
      switch(type) {
        case "creatures": Main.curTileClass = "Tile-creature"; break;
        case "furniture": Main.curTileClass = "Tile-furniture"; break;
        case "triggers": Main.curTileClass = "Tile-trigger"; break;
        case "walls": Main.curTileClass = "Tile-wall"; break;
        case "floors": Main.curTileClass = "Tile-floor"; break;
        default: console.log("Error: type("+type+") unknown");
      }
      Main.curTileData = campaignData[type][index];
      Main.curTileData.class = Main.curTileClass;
      Main.cursor.redraw();
      console.log( "setTileProperties("+type+","+index+")" );
    },
    
    setRotationMode: function(mode) {
      var btnDisabled = false;
      if( 
        mode=="random" && $("#radio-rotationRand").parent("label").hasClass("disabled") || 
        mode=="0" && $("#radio-rotation0").parent("label").hasClass("disabled") ||
        mode=="90" && $("#radio-rotation90").parent("label").hasClass("disabled") ||
        mode=="180" && $("#radio-rotation180").parent("label").hasClass("disabled") ||
        mode=="270" && $("#radio-rotation270").parent("label").hasClass("disabled")
      ) {
        btnDisabled = true;
      }
      if( !btnDisabled ) {
        console.log( "setRotationMode("+mode+")" );
        Main.rotationMode = mode;
        Main.cursor.redraw();
        $(".Panel-btnGrp--rotation label").removeClass("active");
        $(".Panel-btnGrp--rotation input").prop('checked', false);
        switch(mode) {
          case "random": $("#radio-rotationRand").prop('checked', true).parent('label').addClass("active"); break;
          case "0": $("#radio-rotation0").prop('checked', true).parent('label').addClass("active"); break;
          case "90": $("#radio-rotation90").prop('checked', true).parent('label').addClass("active"); break;
          case "180": $("#radio-rotation180").prop('checked', true).parent('label').addClass("active"); break;
          case "270": $("#radio-rotation270").prop('checked', true).parent('label').addClass("active"); break;
        }
        $(".Grid-selector").attr('data-rotationMode', Main.rotationMode);
      }
    },
    
    setPaintMode: function(mode) {
      var btnDisabled = false;
      if( 
        mode=="random" && $("#radio-modeRand").parent("label").hasClass("disabled") || 
        mode=="0:0" && $("#radio-modeSingle").parent("label").hasClass("disabled") ||
        mode=="0:0" && $("#radio-modeSingle").parent("label").hasClass("active") ||
        mode=="whole" && $("#radio-modeWhole").parent("label").hasClass("disabled") ) {
        btnDisabled = true;
      }
      if( !btnDisabled ) {
        console.log( "setPaintMode("+mode+")" );
        Main.paintMode = mode;
        Main.cursor.redraw();
        $(".Panel-btnGrp--mode label").removeClass("active");
        $(".Panel-btnGrp--mode input").prop('checked', false);
        $("#radio-rotationRand").parent('label').removeClass("disabled");
        switch(mode) {
          case "random": $("#radio-modeRand").prop('checked', true).parent('label').addClass("active"); break;
          case "0:0": $("#radio-modeSingle").prop('checked', true).parent('label').addClass("active"); break;
          case "whole": 
            $("#radio-modeWhole").prop('checked', true).parent('label').addClass("active");
            if($('#radio-rotationRand').is(':checked')) {
              Main.setRotationMode("0");
            }
            $("#radio-rotationRand").parent('label').addClass("disabled");
            break;
        }
        $(".Grid-selector").attr('data-paintMode', Main.paintMode);
      }
    },
    
    selectTab: function(tabId) {
      var type = tabId.replace("tab-","");
      console.log( "selectTab("+type+")");
      Main.tileType = type;
      var $t = $("#"+tabId);
      $(".Panel-paletteTabs li").removeClass("active");
      $t.parent("li").addClass("active");
      $(".Panel-palette").css("display","none");
      $(".Panel-btnGrp .btn").removeClass("active disabled");
      $(".Panel-btnGrp input").prop('checked',false).prop('disabled',false);
      switch(type) {
        case "floors": 
          $("#palette-floors").css("display","flex");
          Main.setRotationMode("random");
          Main.setPaintMode("random");
          break;
        case "walls": 
          $("#palette-walls").css("display","flex");
          Main.setRotationMode("0");
          Main.setPaintMode("0:0");
          $("#radio-rotationRand, #radio-modeRand, #radio-modeWhole").parent('label').addClass("disabled");
          break;
        case "triggers": 
          $("#palette-triggers").css("display","flex");
          Main.setRotationMode("0");
          Main.setPaintMode("0:0");
          $("#radio-rotationRand, #radio-rotation90, #radio-rotation180, #radio-rotation270, #radio-modeRand, #radio-modeWhole").parent('label').addClass("disabled");
          break;
        case "furniture": 
          $("#palette-furniture").css("display","flex");
          Main.setRotationMode("0");
          Main.setPaintMode("whole");
          $("#radio-rotationRand, #radio-modeRand, #radio-modeSingle").parent('label').addClass("disabled");
          break;
        case "creatures": 
          $("#palette-creatures").css("display","flex");
          Main.setRotationMode("0");
          Main.setPaintMode("0:0");
          $("#radio-rotationRand, #radio-rotation90, #radio-rotation180, #radio-rotation270, #radio-modeRand, #radio-modeWhole").parent('label').addClass("disabled");
          break;
      }
      Main.loadPalette();
    },
    
    loadPalette: function() {
      if( $("#palette-"+Main.tileType).children(".Panel-paletteSwatch").length <= 0 ) {
        $.swatchPromise = function(src) {
          return $.Deferred( function(task) {
            var image = new Image();
            image.onload = function () { task.resolve(image); }
            image.onerror = function () { task.reject(); }
            image.src = src;
          }).promise();
        };
        var deferredSwatches = [];
        var data = campaignData[Main.tileType];
        for( var i=0 ; i<data.length ; i++ ) {
          deferredSwatches.push( $.swatchPromise(data[i].path) );
          $("#palette-"+Main.tileType).append( "<div class='Panel-paletteSwatch is-loading'></div>" );
        }
        $.when.apply($, deferredSwatches)
        .done( function() {
          for ( var i=0 ; i<arguments.length ; i++ ) {
            var img = arguments[i];
            var data = campaignData[Main.tileType][i];
            data.width = img.width;
            data.height = img.height;
            data.unitWidth = img.width/UNIT;
            data.unitHeight = img.height/UNIT;
            var $swatch = $("#palette-"+Main.tileType+" .Panel-paletteSwatch:eq("+i+")");
            $swatch.css( "background-image", "url('"+img.src+"')" );
            $swatch.attr( "type", Main.tileType );
            $swatch.click( function() {
              Main.setTileProperties( $(this).attr("type"), $(this).index() );
              Main.cursor.redraw();
            });
            $swatch.removeClass("is-loading");
          }
          $("#palette-"+Main.tileType+" .Panel-paletteSwatch:eq(0)").click();
          Main.cursor.redraw();
        });
      } else {
        $("#palette-"+Main.tileType+" .Panel-paletteSwatch:eq(0)").click();
      }
      console.log( "loadPalette("+Main.tileType+")" );
    },
    
    saveMap: function() {
      console.log("saveMap()");
      // count
      var minX, minY;
      minX = minY = Number.MAX_VALUE;
      $(".Tile").each( function() {
        var $t = $(this);
        minX = Math.min( parseInt($t.attr("data-x")), minX );
        minY = Math.min( parseInt($t.attr("data-y")), minY );
      });
      // get data
      var mapData = [];
      $(".Tile").each( function() {
        var $t = $(this);
        //get object at (x,y)
        var x = parseInt( $t.attr("data-x") ) - minX;
        var y = parseInt( $t.attr("data-y") ) - minY;
        if( mapData[x] == undefined ) mapData[x] = [];
        if( mapData[x][y] == undefined ) mapData[x][y] = {};
        //get tile type
        var type;
        if( $t.hasClass("Tile-creature") ) { type = "creature"; }
        else if( $t.hasClass("Tile-furniture") ) { type = "furniture"; }
        else if( $t.hasClass("Tile-trigger") ) { type = "trigger"; } 
        else if( $t.hasClass("Tile-wall") ) { 
          if( $t.attr("data-rotation") == "0" ) { type = "wall-right" }
          else if( $t.attr("data-rotation") == "90" ) { type = "wall-bottom" }
        } 
        else if( $t.hasClass("Tile-floor") ) { type = "floor"; }
        mapData[x][y][type] = {};
        //get properties
        //mapData[x][y][type].id = $t.attr("data-id");  // should use this instead of path
        if( $t.hasClass("Tile-furniture") ) {
          mapData[x][y][type].group = $t.attr("data-group");
        }
        // get graphical properties
        mapData[x][y][type]["graphics"] = {};
        mapData[x][y][type]["graphics"].path = $t.css("background-image").replace('url(','').replace(')','');
        mapData[x][y][type]["graphics"].rotation = $t.attr("data-rotation");
        mapData[x][y][type]["graphics"].xOff = parseInt( $t.css('backgroundPosition').split(" ")[0] );
        mapData[x][y][type]["graphics"].yOff = parseInt( $t.css('backgroundPosition').split(" ")[1] );
 
      });
      return mapData;
    },
    
    loadMap: function(json) {
      console.log("loadMap");
      $('#modal-load').modal('hide');
      
      var mapData = $.parseJSON(json);
      //console.log( mapData );
      var count = 0;
      for( var x=0 ; x<mapData.length ; x++) {
        for( var y=0 ; y<mapData[x].length ; y++) {
          if( mapData[x][y] ) {
            
            for( var i in mapData[x][y] ) {
              
              var tileType;
              switch(i) {
                case "creature": tileType = "Tile-creature"; break;
                case "furniture": tileType = "Tile-furniture"; break;
                case "trigger": tileType = "Tile-trigger"; break;
                case "wall-right": tileType = "Tile-wall"; break;
                case "wall-bottom": tileType = "Tile-wall"; break;
                case "floor": tileType = "Tile-floor"; break;
                default: console.log("Error: type("+i+") unknown");
              }
              var pt = new Pt(x,y);
              var rot = mapData[x][y][i].graphics.rotation;
              var off = {};
              off.x = mapData[x][y][i].graphics.xOff;
              off.y = mapData[x][y][i].graphics.yOff;
              var grp = "-1";
              if( mapData[x][y][i].hasOwnProperty('group') ) grp = mapData[x][y][i].group;
              var path = mapData[x][y][i].graphics.path;
              
              count++;
              var delay = count*10;
              Main.addTile(tileType,pt,rot,off,grp,path,delay);
              //console.log( timeout );
            }
           
          }
        }
      }
    },
    
    enableControls: function() {
      $("#radio-rotationRand").change( function() { Main.setRotationMode("random"); });
      $("#radio-rotation0").change( function() { Main.setRotationMode("0"); });
      $("#radio-rotation90").change( function() { Main.setRotationMode("90"); });
      $("#radio-rotation180").change( function() { Main.setRotationMode("180"); });
      $("#radio-rotation270").change( function() { Main.setRotationMode("270"); });
      $(".Panel-paletteTabs a").click( function() { Main.selectTab($(this).attr("id")); });
      $("#radio-modeRand").change( function() { Main.setPaintMode("random"); });
      $("#radio-modeSingle").change( function() { Main.setPaintMode("0:0"); });
      $("#radio-modeWhole").change( function() { Main.setPaintMode("whole"); });
      $("#modalBtn-clearAll").click( function() {
        $(".Tiles").empty();
        Main.groupCounter = 0;
        console.log( "clearAllTiles()" );
      });
      $("#btn-save").click( function() {
        $("#modalTextarea-save").val( JSON.stringify(Main.saveMap()) );
      });
      $("#modalBtn-save").click( function() {
        var text = $("#modalTextarea-save").val();
        var filename = $("#modalInput-fileName").val()
        var blob = new Blob([text], {type: "text/plain;charset=utf-8"});
        saveAs(blob, filename+".map.json");
      });
      $("#modalBtn-load").click( function() {
        var file = $('#modalInput-loadFile').get(0).files[0];
        if (file) {
          var reader = new FileReader();
          reader.onload = function(e) {
            $("#modalBtn-clearAll").click();
            Main.loadMap( e.target.result );
          }
          reader.readAsText(file);
        } else { 
          console.log("Failed to load file");
        }
      });
    },

    init: function() {
      console.log("START");
      Main.pan.setOffset(280,0); //Main.pan.setOffset(350,70);
      Main.selectTab("tab-floors");
      Main.cursor.enable();
      Main.modify.enable();
      Main.keylisten.enable();
      Main.pan.enable();
      //Main.zoom.enable();
      Main.loadMap('[[null,{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}}],[{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/spiralstair.jpg","rotation":"0","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/spiralstair.jpg","rotation":"0","xOff":0,"yOff":-70}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":70,"yOff":210}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":210,"yOff":280}}}],[{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/spiralstair.jpg","rotation":"0","xOff":-70,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/spiralstair.jpg","rotation":"0","xOff":-70,"yOff":-70}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":210,"yOff":0}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":210,"yOff":210}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}}],[{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":70,"yOff":140}}},{"creature":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/creatures/barbarian.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"270","xOff":70,"yOff":70}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"90","xOff":140,"yOff":210}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":210,"yOff":70}}},{"furniture":{"group":"111","graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/furniture/weaponrack.png","rotation":"270","xOff":0,"yOff":0}},"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":70,"yOff":280}}}],[{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"90","xOff":70,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"270","xOff":210,"yOff":210}}},{"trigger":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/triggers/trapspear.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"270","xOff":70,"yOff":70}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":210,"yOff":280}}},{"furniture":{"group":"111","graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/furniture/weaponrack.png","rotation":"270","xOff":0,"yOff":-70}},"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":70,"yOff":140}}}],[null,{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"270","xOff":0,"yOff":140}}},{"creature":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/creatures/goblin.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":210,"yOff":70}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":210,"yOff":280}}},{"furniture":{"group":"111","graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/furniture/weaponrack.png","rotation":"270","xOff":0,"yOff":-140}},"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":0,"yOff":70}}}],[null,{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":70,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/dooropen.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"180","xOff":140,"yOff":140}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":210,"yOff":140}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/bluegrey.jpg","rotation":"0","xOff":140,"yOff":210}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}}}],[null,{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"180","xOff":350,"yOff":140}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"180","xOff":70,"yOff":140}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"90","xOff":140,"yOff":210}}},{"trigger":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/triggers/trappit.png","rotation":"0","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"270","xOff":350,"yOff":350}}},{"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/doorclosed.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"90","xOff":280,"yOff":280}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/corridor.jpg","rotation":"180","xOff":420,"yOff":280}}}],[null,null,null,null,{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/green.jpg","rotation":"0","xOff":140,"yOff":210}}},{"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/greencobbles.jpg","rotation":"90","xOff":70,"yOff":210}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/green.jpg","rotation":"90","xOff":70,"yOff":210}}}],[null,null,null,null,{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}}},{"furniture":{"group":"113","graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/furniture/chest.png","rotation":"90","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/greencobbles.jpg","rotation":"0","xOff":0,"yOff":70}}},{"creature":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/creatures/gargoyal.png","rotation":"0","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/green.jpg","rotation":"90","xOff":70,"yOff":210}}},{"wall-bottom":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"90","xOff":0,"yOff":0}},"wall-right":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/walls/stone.png","rotation":"0","xOff":0,"yOff":0}},"floor":{"graphics":{"path":"http://www.davidelrizzo.com/heroquestonline/leveleditor/images/floors/greencobbles.jpg","rotation":"270","xOff":0,"yOff":140}}}]]');
    }
    
  }
  
  
  Main.init();
  Main.enableControls();
  
}());


              
            
!
999px

Console