title: How could I achieve the effect of "lava" leaving traces
body: <p>After some time of "development" of the JavaScript game, I've came to a great idea, or so it seemed / sounded.</p>
  
  <p>I was thinking of creating an entity which would represent lava. That lava would move in a specific direction, using:</p>
  
  <pre><code>function Lava(pos, ch) {
    this.pos = pos;
    this.size = new Vector(1, 1);
    if(ch == '-') {
      this.speed = new Vector(3, 0)
    }
  }
  </code></pre>
  
  <p>where is <code>var acrotchar = {"-": Lava};</code>.</p>
  
  <p>The whole code can be seen <a href="https://jsfiddle.net/L3nd14oz/" rel="noreferrer">here</a> or below: </p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var LEVELS = [
    ["                                  x  x",
     "                                  xx x",
     "                      xxx         x  x",
     "                     xx!xx        x ox",
     "                     x!!!x        x xx",
     "                     xx!xx        x  x",
     "  x                   xvx         x  x",
     "  x                               xx x",
     "  x                               x  x",
     "  xx                              x  x",
     "  x                               x xx",
     "  x                                  x",
     "  x @                xxxxx     o     x",
     "  xxxxxx     xxxxxxxxx   xxxxxxxxxxxxx",
     "       x     x                        ",
     "       x!!!!!x                        ",
     "       x!!!!!x                        ",
     "       xxxxxxx                        ",
     "--------------------------------------"]
     ];
  
  //set variables (HP and EXP)
  var life = 3;
  var expo = 0;
  document.getElementById("life").innerHTML = ("Lives left: " + life);
  document.getElementById("expo").innerHTML = ("Points: " + expo);
  
  //set the playzone
  function Vector(x, y) {
   this.x = x; this.y = y;
  }
  
  Vector.prototype.plus = function(other) {
   return new Vector(this.x + other.x, this.y + other.y);
  };
  
  Vector.prototype.times = function(scale) {
   return new Vector(this.x * scale, this.y * scale);
  };
  
  // Note: uppercase words are used that means constructor are values
  var actorchars =  {
   "@": Player,
   "o": Coin,
   "=": Lava,
   "|": Lava,
   "v": Lava,
   "#": Lava,
   "-": Flood
  };
  
  function Player(pos) {
   this.pos = pos.plus(new Vector(0, -.5));
   this.size = new Vector(.5, 1);
   this.speed = new Vector(0, 0);
  }
  Player.prototype.type = "player";
  
  function Lava(pos, ch) {
   this.pos = pos;
   this.size = new Vector(1, 1);
   if (ch === "=")
    this.speed = new Vector(2, 0);
   else if (ch === '|')
    this.speed = new Vector(0, 2);
   else if (ch === 'v'){
    this.speed = new Vector(0, 5);    
    this.repeatPos = pos;
   } else if (ch === '#')
     this.speed = new Vector(0, 10);
  }
  Lava.prototype.type = "lava"
  
  function Flood(pos, ch) {
    this.pos = pos;
    this.size = new Vector(1, 1);
    if  (ch === '-') {
      this.speed = new Vector(0, 1);
      this.repeatPos = pos;  //will be removed in the future 
    }
  }
  Flood.prototype.type = "flood"
  
  //Lava.prototype.type = "Lava";
  
  // function Wall(pos, ch) {
   // this.pos = pos;
   // this.size = new Vector(1, 1);
   // if (ch === "z")
    // this.speed = new Vector(0, 1);
  // }
  // Wall.prototype.type = "wall"
  
  function Coin(pos) {
   this.basePos = this.pos = pos;
   this.size = new Vector(.6, .6);
   // take a look back
   this.wobble = Math.random() * Math.PI * 2;
  }
  Coin.prototype.type = "coin";
  
  Level.prototype.isFinished = function() {
    return this.status !== null &amp;&amp; this.finishDelay &lt; 0;
  };
  
  function Level(plan) {
   this.width = plan[0].length;
   this.height = plan.length;
   this.grid = [];
   this.actors = [];
   
   for (var y = 0; y &lt; this.height; y++) {
    var line = plan[y],  gridLine = [];
    for (var x = 0; x &lt; this.width; x++) {
     var ch = line[x], fieldType = null;
     var Actor = actorchars[ch];
     if (Actor)
      this.actors.push(new Actor(new Vector(x, y), ch));
     else if (ch === "x")
      fieldType = "wall";
     else if (ch === "z")
      fieldType = "wall";
     else if (ch === "!")
      fieldType = "lava";
     else if (ch === "|")
      fieldType = "lava";
     else if (ch === "=")
      fieldType = "lava";
     else if (ch === "#")
      fieldType = "lava";
        else if (ch === "-")
          fieldType = "flood";
     else if (ch === "v"){
      fieldType = "lava";
      console.log(fieldType);
     }
     gridLine.push(fieldType);
    }
    this.grid.push(gridLine);
   }
   this.player = this.actors.filter(function(actor) {
    return actor.type === "player";
   })[0]; 
   this.status = this.finishDelay = null;
  }
  
  function element(name, className) {
   var elem = document.createElement(name);
   if(className) elem.className = className;
   return elem;
  }
  
  function DOMDisplay(parent, level) {
   this.wrap = parent.appendChild(element("div", "game"));
   this.level = level;
   
   this.wrap.appendChild(this.drawBackground());
   this.actorLayer = null;
   this.drawFrame();
  }
  
  
  var scale = 15;
  
  
  DOMDisplay.prototype.drawBackground = function() {
   var table = element("table", "background");
   table.style.width = this.level.width * scale + "px";
   table.style.height = this.level.height * scale + "px";
   this.level.grid.forEach(function(row) {
    var rowElement = table.appendChild(element("tr"));
    rowElement.style.height = scale + "px";
    row.forEach(function(type) {
     rowElement.appendChild(element("td", type));
    });
   });
   return table;
  };
  
  DOMDisplay.prototype.drawActors = function() {
   var wrap = element("div");
   this.level.actors.forEach(function(actor) {
    var rect = wrap.appendChild(element("div", "actor " + actor.type));
    rect.style.width = actor.size.x * scale + "px";
    rect.style.height = actor.size.y * scale + "px";
    rect.style.left = actor.pos.x * scale + "px";
    rect.style.top = actor.pos.y * scale + "px";
   });
   return wrap;
  };
  
  DOMDisplay.prototype.drawFrame = function() {
   if (this.actorLayer)
    this.wrap.removeChild(this.actorLayer);
   this.actorLayer = this.wrap.appendChild(this.drawActors());
   this.wrap.className = "game " + (this.level.status || "");
   this.scrollPlayerIntoView();
  };
  
  
  // clear it later
  DOMDisplay.prototype.scrollPlayerIntoView = function() {
    var width = this.wrap.clientWidth;
    var height = this.wrap.clientHeight;
    var margin = width / 3;
  
    // The viewport
    var left = this.wrap.scrollLeft, right = left + width;
    var top = this.wrap.scrollTop, bottom = top + height;
  
    var player = this.level.player;
    var center = player.pos.plus(player.size.times(0.5))
                   .times(scale);
  
    if (center.x &lt; left + margin)
      this.wrap.scrollLeft = center.x - margin;
    else if (center.x &gt; right - margin)
      this.wrap.scrollLeft = center.x + margin - width;
    if (center.y &lt; top + margin)
      this.wrap.scrollTop = center.y - margin;
    else if (center.y &gt; bottom - margin)
      this.wrap.scrollTop = center.y + margin - height;
  };
  
  DOMDisplay.prototype.clear = function() {
   this.wrap.parentNode.removeChild(this.wrap);
  };
  
  Level.prototype.obstacleAt = function(pos, size) {
    var xStart = Math.floor(pos.x);
    var xEnd = Math.ceil(pos.x + size.x);
    var yStart = Math.floor(pos.y);
    var yEnd = Math.ceil(pos.y + size.y);
  
    if (xStart &lt; 0 || xEnd &gt; this.width || yStart &lt; 0)
      return "wall";
    if (yEnd &gt; this.height)
      return "lava", "flood";
    for (var y = yStart; y &lt; yEnd; y++) {
      for (var x = xStart; x &lt; xEnd; x++) {
        var fieldType = this.grid[y][x];
        if (fieldType) return fieldType;
      }
    }
  };
  
  Level.prototype.actorAt = function(actor) {
    for (var i = 0; i &lt; this.actors.length; i++) {
      var other = this.actors[i];
      if (other != actor &amp;&amp;
          actor.pos.x + actor.size.x &gt; other.pos.x &amp;&amp;
          actor.pos.x &lt; other.pos.x + other.size.x &amp;&amp;
          actor.pos.y + actor.size.y &gt; other.pos.y &amp;&amp;
          actor.pos.y &lt; other.pos.y + other.size.y)
        return other;
    }
  };
  
  var maxStep = 0.05;
  
  Level.prototype.animate = function(step, keys) {
    if (this.status !== null)
      this.finishDelay -= step;
  
    while (step &gt; 0) {
      var thisStep = Math.min(step, maxStep);
      this.actors.forEach(function(actor) {
        actor.act(thisStep, this, keys);
      }, this);
      step -= thisStep;
    }
  };
  
  
  Lava.prototype.act = function(step, level) {
    var newPos = this.pos.plus(this.speed.times(step));
    if (!level.obstacleAt(newPos, this.size))
      this.pos = newPos;
    else if (this.repeatPos)
      this.pos = this.repeatPos;
    else
      this.speed = this.speed.times(-1);
  };
  
  Flood.prototype.act = function(step, level) {
    var newPos = this.pos.plus(this.speed.times(step));
    if (!level.obstacleAt(newPos, this.size))
      this.pos = newPos;
    else
      this.speed = this.speed.times(-1);
  };
  
  var wobbleSpeed = 8, wobbleDist = 0.07;
  
  Coin.prototype.act = function(step) {
    this.wobble += step * wobbleSpeed;
    var wobblePos = Math.sin(this.wobble) * wobbleDist;
    this.pos = this.basePos.plus(new Vector(0, wobblePos));
  };
  
  
  var playerXSpeed = 10;
  
  Player.prototype.moveX = function(step, level, keys) {
    this.speed.x = 0;
    if (keys.left) this.speed.x -= playerXSpeed;
    if (keys.right) this.speed.x += playerXSpeed;
  
    var motion = new Vector(this.speed.x * step, 0);
    var newPos = this.pos.plus(motion);
    var obstacle = level.obstacleAt(newPos, this.size);
    if (obstacle)
      level.playerTouched(obstacle);
    else
      this.pos = newPos;
  };
  
  var gravity = 30;
  var jumpSpeed = 17;
  
  Player.prototype.moveY = function(step, level, keys) {
    this.speed.y += step * gravity;
    var motion = new Vector(0, this.speed.y * step);
    var newPos = this.pos.plus(motion);
    var obstacle = level.obstacleAt(newPos, this.size);
    if (obstacle) {
      level.playerTouched(obstacle);
      if (keys.up &amp;&amp; this.speed.y &gt; 0)
        this.speed.y = -jumpSpeed;
      else
        this.speed.y = 0;
    } else {
      this.pos = newPos;
    }
  };
  
  Player.prototype.act = function(step, level, keys) {
    this.moveX(step, level, keys);
    this.moveY(step, level, keys);
  
    var otherActor = level.actorAt(this);
    if (otherActor)
      level.playerTouched(otherActor.type, otherActor);
  
    // Losing animation
    if (level.status == "lost") {
      this.pos.y += step;
      this.size.y -= step;
    }
  };
  
  Level.prototype.playerTouched = function(type, actor) {
    //if (type == "lava" || type == "Lava" &amp;&amp; this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
    if (type == "lava" &amp;&amp; this.status === null || type == "flood" &amp;&amp; this.status === null) {
      this.status = "lost";
   life -= 1;
   console.log(life);
      expo = 0;
      document.getElementById("expo").innerHTML = ("Points: " + expo);
   document.getElementById("life").innerHTML = ("Lives left: " + life);
    if(life &lt; 0) { 
   sessionStorage.setItem("reloading", "true");
   document.location.reload();
    }
      this.finishDelay = 1;
    } else if (type == "coin") {
     expo += 1;
        document.getElementById("expo").innerHTML = ("Points: " + expo);
      this.actors = this.actors.filter(function(other) {
        return other != actor;
      });
      if (!this.actors.some(function(actor) {
        return actor.type == "coin";
      })) {
     life += 1;
     document.getElementById("life").innerHTML = ("Lives left: " + life);
        this.status = "won";
        this.finishDelay = 1;
      }
    }
  };
  
  var arrowCodes = {37: "left", 38: "up", 39: "right"};
  
  function trackKeys(codes) {
    var pressed = Object.create(null);
    function handler(event) {
      if (codes.hasOwnProperty(event.keyCode)) {
        var down = event.type == "keydown";
        pressed[codes[event.keyCode]] = down;
        event.preventDefault();
      }
    }
    addEventListener("keydown", handler);
    addEventListener("keyup", handler);
    return pressed;
  }
  
  function runAnimation(frameFunc) {
    var lastTime = null;
    function frame(time) {
      var stop = false;
      if (lastTime !== null) {
        var timeStep = Math.min(time - lastTime, 100) / 1000;
        stop = frameFunc(timeStep) === false;
      }
      lastTime = time;
      if (!stop)
        requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
  }
  
  var arrows = trackKeys(arrowCodes);
  
  function runLevel(level, Display, andThen) {
    var display = new Display(document.body, level);
    runAnimation(function(step) {
      level.animate(step, arrows);
      display.drawFrame(step);
      if (level.isFinished()) {
        display.clear();
        if (andThen)
          andThen(level.status);
        return false;
      }
    });
  }
  
  
  var lives = function() {
   ctx.font = "20px Courier";
   ctx.fontFamily = "monospace";
   ctx.fillStyle = "#666";
   ctx.textAlign = "left";
   ctx.textBaseline = "top";
   ctx.fillText("Lives left: " + life, 10, 10);
  };
  
  
  function runGame(plans, Display) {
    function startLevel(n) {
      runLevel(new Level(plans[n]), Display, function(status) {
        if (status == "lost") {
          startLevel(n);       
     } else if (n &lt; plans.length - 1)
          startLevel(n + 1);
        else
          alert("You win!");
      });
    }
    startLevel(0);
  }
  
  runGame(LEVELS, DOMDisplay);</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
    background: #222;
  }
  
  h2 {
    color: #666;
    font-family: monospace;
    text-align: center;
  }
  
  .background {
    table-layout: fixed;
    border-spacing: 0;
  }
  
  .background td {
    padding: 0;
  }
  
  .lava, .actor {
    background: #e55;
  }
  
  .wall {
    background: #444;
    border: solid 3px #333;
    box-sizing: content-box;
  }
  
  .actor {
    position: absolute;
  }
  
  .coin {
    background: #e2e838;
    border-radius: 50%;
  }
  
  .player {
    background: #335699;
    box-shadow: none;
  }
  
  .lost .player {
    background: #a04040;
  }
  
  .won .player {
    background: green;
  }
  
  .game {
    position: relative;
    overflow: hidden;
  }
  
  #life, #expo {
    font-size: 16px;
    font-family: monospace;
    color: #666;
    text-align: left;
    baseline: top;
    margin-left: 30px;
    font-weight: bold;
  }
  
  input {
    margin-left: 30px;
    
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;h2&gt;Simple JavaScript Game&lt;/h2&gt;
    
    &lt;div id="life"&gt;&lt;/div&gt;  
    &lt;div id="expo"&gt;&lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>What I would like to achieve is the following:</p>
  
  <p>Lava (red colored blocks, in code presented as "-", tagged as "flood") that would:</p>
  
  <ul>
  <li><p><s>move in a specific direction (is already done, more or less),</s></p></li>
  <li><p>leaves "trace" behind it's movement, (technically speaking, the lava level in this case would seem as it is rising up),</p></li>
  <li><p>"flood" is able to go through the objects (optional, as I have to check the whole code carefully on my own to see where did I give the entity the "collisions")</p></li>
  </ul>
  
  <p>I would really appreciate any help, especially with the lava rising effect. </p>