> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.ALuTIcV$K3D/rev.28
 * 
 * authors: 
 *   GoToLoop

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/**
 * Random Tiled Terrain Gen (v4.12)
 * by  Asimes (2014/Jun)
 * mod Matyze & GoToLoop
 *
 * forum.Processing.org/two/discussion/5985/
 * how-can-a-roguelike-have-so-large-maps#Item_16
 *
 * OpenProcessing.org/sketch/436477
 * studio.ProcessingTogether.com/sp/pad/export/ro.9Ql2E8JBC54LJ
 */

static final byte VOID = ' ', HERO = '@', MOUNT = '^';
static final byte TREE = 'T', GRASS = '#';
static final byte LAND = '%', WATER = '~';

static final int COLORS = 0200;
static final color[] colors = new color[COLORS];
//static  // comment this line out for JS Mode!
{
  colors[VOID]   = #808080;
  colors[HERO]   = #E0E0E0;
  colors[MOUNT]  = #803000;
  colors[GRASS]  = #008000;
  colors[TREE]   = #005000;
  colors[LAND]   = #C0C000;
  colors[WATER]  = #0000C0;
};

void terrainGenerator(byte[][] terrain, float noiseScale) {
  noiseSeed((long) random(MIN_INT, MAX_INT));

  for (int row = terrain.length; row-- != 0; ) {
    byte[] columns = terrain[row];

    for (int col = columns.length; col-- != 0; ) {
      float val = noise(row*noiseScale, col*noiseScale);
      byte type;

      if      (val > .65)  type = MOUNT;
      else if (val > .55)  type = GRASS;
      else if (val > .45)  type = TREE;
      else if (val > .35)  type = LAND;
      else                 type = WATER;

      columns[col] = type;
    }
  }
}

static final int GRID = 05000, UNIT = 030;
static final int GW = 8*UNIT << 2, GH = 6*UNIT << 2;
static final int UW = GW/UNIT, UH = GH/UNIT;
final byte[][] tiles = new byte[GRID][GRID];

static final byte[] FPS = { 25, 40, 60, 80, 100 };
int fps;

static final int DIAM  = UNIT>>1, SMOOTH = 3;
static final color INK = #FF0000, BG = 0;
static final float NOISE = .05, WEIGHT = 1.5;

static final boolean ONLINE = 1/2 == 1/2.;
static final boolean OUTLINE = true;

static final String RENDER = ONLINE? JAVA2D : FX2D; // P3+
//static final String RENDER = JAVA2D; // P2
//static final String RENDER = P2D; // P1

int px, py, ix, iy;
int north, south, west, east;
boolean rogue = true;

void settings() {
  size(GW, GH, RENDER);
  smooth(SMOOTH); // P2+
  //smooth(); // P1
}

void setup() {
  if (width != GW)  settings();
  frameRate(FPS[fps]);

  if (OUTLINE)  stroke(BG);
  else          noStroke();
  strokeWeight(WEIGHT);

  textSize(UNIT);
  textAlign(LEFT, TOP);

  rectMode(CORNER);
  ellipseMode(CENTER);

  px = (width>>1)  + DIAM;
  py = (height>>1) + DIAM;

  reset();
}

void draw() {
  if (rogue)  background(BG);

  moveHero();
  renderTerrain(tiles);

  if (rogue)  drawHeroChar();
  else        drawHeroTile();

  if (!ONLINE)  info();
}

void info() {
  frame.setTitle("Grid: " + GRID + " x " + GRID
    + "    (" + nf(ix, 4) + ", " + nf(iy, 4)
    + ")    FPS: " + nf(round(frameRate), 2));
}

void reset() {
  terrainGenerator(tiles, NOISE);
  teleport();
}

void drawHeroTile() {
  fill(INK);
  ellipse(px, py, DIAM, DIAM);
}

void drawHeroChar() {
  fill(colors[HERO]);
  text((char) HERO, px - DIAM, py - DIAM);
}

void drawTile(int x, int y, int diam, color c) {
  fill(c);
  rect(x, y, diam, diam);
}

void drawChar(int x, int y, char ch, color c) {
  fill(c);
  text(ch, x + (UNIT>>2), y);
}

void renderTerrain(byte[][] terrain) {
  for (int row = UH; row-- != 0; ) {
    int oy = iy+row - (UH>>1);

    if (oy < 0 | oy >= GRID)  for (int col = UW; col-- != 0; )
      if (rogue)  drawChar(col*UNIT, row*UNIT, (char) VOID, colors[VOID]);
      else        drawTile(col*UNIT, row*UNIT, UNIT, colors[VOID]);

    else {
      byte[] columns = terrain[oy];

      for (int col = UW; col-- != 0; ) {
        int ox  = ix+col - (UW>>1);
        char ch;
        color c;

        if (ox >= 0 & ox < GRID) {
          ch = (char) columns[ox];
          c  = colors[ch];
        } else {
          ch = VOID;
          c  = colors[VOID];
        }

        if (rogue)  drawChar(col*UNIT, row*UNIT, ch, c);
        else        drawTile(col*UNIT, row*UNIT, UNIT, c);
      }
    }
  }
}

void keyPressed() {
  int k = keyCode;

  if (k == ENTER | k == RETURN)  frameRate(FPS[fps = (fps+1)%FPS.length]);
  else if (k == CONTROL | k == 'T')  teleport();
  else if (k == DELETE  | k == ALT)  reset();
  else if (k == SHIFT   | k == ' ')  rogue ^= true;
  else setDirection(k, 1);
}

void keyReleased() {
  setDirection(keyCode, 0);
}

void setDirection(int k, int spd) {
  if      (k == UP    | k == 'W')  north = spd;
  else if (k == DOWN  | k == 'S')  south = spd;
  else if (k == LEFT  | k == 'A')  west  = spd;
  else if (k == RIGHT | k == 'D')  east  = spd;
}

void moveHero() {
  int ox = ix + east  - west;
  int oy = iy + south - north;

  if (ox < 0 | ox >= GRID | oy < 0 | oy >= GRID)  return;

  byte ch = tiles[oy][ox];
  if (ch != WATER & ch != MOUNT) {
    ix = ox;
    iy = oy;
  }
}

void teleport() {
  byte ch;

  do {
    ix = (int) random(GRID);
    iy = (int) random(GRID);

    ch = tiles[iy][ix];
  } while (ch == WATER | ch == MOUNT);
}