<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-BR">
	<id>https://wiki.gla.com.br/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Igor+is+back</id>
	<title>Wiki Gla - Contribuições do usuário [pt-br]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.gla.com.br/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Igor+is+back"/>
	<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php/Especial:Contribui%C3%A7%C3%B5es/Igor_is_back"/>
	<updated>2026-05-21T00:39:21Z</updated>
	<subtitle>Contribuições do usuário</subtitle>
	<generator>MediaWiki 1.36.1</generator>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45877</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45877"/>
		<updated>2026-04-09T21:46:32Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle|dummy=1}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45876</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45876"/>
		<updated>2026-04-09T21:46:21Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Jogar&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Win&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-mobile-controls&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-joystick-canvas&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-action-sprite&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; max-width: 100vw; }&lt;br /&gt;
  #ib-game-container canvas { display: block; image-rendering: pixelated; }&lt;br /&gt;
  #ib-gameCanvas { max-width: 100%; height: auto; }&lt;br /&gt;
&lt;br /&gt;
  #ib-game-container:fullscreen,&lt;br /&gt;
  #ib-game-container:-webkit-full-screen {&lt;br /&gt;
    display: flex; width: 100vw; height: 100vh;&lt;br /&gt;
    align-items: center; justify-content: center; background: #000;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-game-container:fullscreen #ib-gameCanvas,&lt;br /&gt;
  #ib-game-container:-webkit-full-screen #ib-gameCanvas {&lt;br /&gt;
    width: auto; height: 100vh; max-width: 100vw;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-mobile-controls {&lt;br /&gt;
    display: none; position: absolute; bottom: 0; left: 0;&lt;br /&gt;
    width: 100%; justify-content: space-between; align-items: flex-end;&lt;br /&gt;
    padding: 16px; box-sizing: border-box; pointer-events: none;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-joystick-canvas, #ib-action-sprite { pointer-events: all; opacity: 0.8; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const KEY_MAP = { w: 'ArrowUp', a: 'ArrowLeft', s: 'ArrowDown', d: 'ArrowRight' };&lt;br /&gt;
  const MOVE_DURATION = 24;&lt;br /&gt;
  const DIAG_DURATION = Math.round(MOVE_DURATION * Math.SQRT2);&lt;br /&gt;
  const DIAG = [&lt;br /&gt;
    ['ArrowUp',   'ArrowLeft',  -1, -1, 2],&lt;br /&gt;
    ['ArrowDown', 'ArrowLeft',  -1,  1, 1],&lt;br /&gt;
    ['ArrowDown', 'ArrowRight',  1,  1, 0],&lt;br /&gt;
    ['ArrowUp',   'ArrowRight',  1, -1, 2],&lt;br /&gt;
  ];&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  const keyTimes = {};&lt;br /&gt;
  const DIR_DX = [1, -1, 0, 0];&lt;br /&gt;
  const DIR_DY = [0,  0, -1, 1];&lt;br /&gt;
&lt;br /&gt;
  function keyActive(k) { return keys[k] || performance.now() - (keyTimes[k] || 0) &amp;lt; 120; }&lt;br /&gt;
&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    const key = KEY_MAP[e.key] || e.key;&lt;br /&gt;
    if (key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[key][2]; return; }&lt;br /&gt;
    keyTimes[key] = performance.now(); keys[key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[KEY_MAP[e.key] || e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; c * 10 + r), ARTIFACT_COL * 10 + ARTIFACT_ROW]);&lt;br /&gt;
&lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  const spriteCache = new Map();&lt;br /&gt;
  function getSprite(col, row) {&lt;br /&gt;
    const key = col * 10 + row;&lt;br /&gt;
    if (spriteCache.has(key)) return spriteCache.get(key);&lt;br /&gt;
    const c = document.createElement('canvas');&lt;br /&gt;
    c.width = c.height = TILE_SIZE;&lt;br /&gt;
    c.getContext('2d').drawImage(mainSprite, col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
    spriteCache.set(key, c); return c;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function bakeSprites() {&lt;br /&gt;
    for (let c = 0; c &amp;lt; 5; c++) for (let r = 0; r &amp;lt; 6; r++) getSprite(c, r);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
   _startMove(dx, dy, dir, duration = MOVE_DURATION) {&lt;br /&gt;
    const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
    this.direction = dir;&lt;br /&gt;
    if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(nx * 10 + ny)) return false;&lt;br /&gt;
    this.tileX = nx; this.tileY = ny;&lt;br /&gt;
    this.startX = this.x; this.startY = this.y;&lt;br /&gt;
    this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
    this.moving = true; this.moveFrame = 0; this.moveDuration = duration;&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (let i = 0; i &amp;lt; DIAG.length; i++) {&lt;br /&gt;
        const [k1, k2, dx, dy, dir] = DIAG[i];&lt;br /&gt;
        if (keyActive(k1) &amp;amp;&amp;amp; keyActive(k2) &amp;amp;&amp;amp; this._startMove(dx, dy, dir, DIAG_DURATION)) return;&lt;br /&gt;
      }&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = DIR_DX[this.direction], dy = DIR_DY[this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      const t = this.moveFrame / this.moveDuration;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * t;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * t;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= this.moveDuration) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx, alpha) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i } = this;&lt;br /&gt;
      const rx = this.prevX + (this.x - this.prevX) * alpha;&lt;br /&gt;
      const ry = this.prevY + (this.y - this.prevY) * alpha;&lt;br /&gt;
      ctx.drawImage(getSprite(i &amp;gt; 0 ? 3 : f, d), Math.round(rx), Math.round(ry));&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      ctx.drawImage(getSprite(this.open ? 1 : 0, 4), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(getSprite(col, 5), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const offscreen = document.createElement('canvas');&lt;br /&gt;
  offscreen.width = canvas.width;&lt;br /&gt;
  offscreen.height = canvas.height;&lt;br /&gt;
  const offCtx = offscreen.getContext('2d');&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function bakeBackground() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        offCtx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawStats() {&lt;br /&gt;
    ctx.font = '16px monospace';&lt;br /&gt;
    ctx.fillStyle = '#fff';&lt;br /&gt;
    ctx.fillText(`FPS: ${fps}  UPS: ${ups}`, 8, 20);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render(alpha) {&lt;br /&gt;
    ctx.drawImage(offscreen, 0, 0);&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    for (let i = 0; i &amp;lt; objects.length; i++) objects[i].draw(ctx);&lt;br /&gt;
    player.draw(ctx, alpha);&lt;br /&gt;
    drawStats();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
  let fps = 0, ups = 0, fCount = 0, uCount = 0, statTime = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; uCount++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    fCount++;&lt;br /&gt;
    if (timestamp - statTime &amp;gt;= 1000) { fps = fCount; ups = uCount; fCount = 0; uCount = 0; statTime = timestamp; }&lt;br /&gt;
    render(lag / MS_PER_UPDATE);&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function isMobile() { return navigator.maxTouchPoints &amp;gt; 0; }&lt;br /&gt;
&lt;br /&gt;
  let joyX = 0, joyY = 0;&lt;br /&gt;
  function setJoystickKeys(dx, dy) {&lt;br /&gt;
    keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false;&lt;br /&gt;
    if (Math.hypot(dx, dy) &amp;lt; 20) return;&lt;br /&gt;
    const a = Math.atan2(dy, dx), s = Math.PI / 8;&lt;br /&gt;
    keys.ArrowRight = a &amp;gt; -3*s &amp;amp;&amp;amp; a &amp;lt; 3*s;&lt;br /&gt;
    keys.ArrowLeft  = a &amp;gt;  5*s || a &amp;lt; -5*s;&lt;br /&gt;
    keys.ArrowDown  = a &amp;gt;    s &amp;amp;&amp;amp; a &amp;lt;  7*s;&lt;br /&gt;
    keys.ArrowUp    = a &amp;lt;   -s &amp;amp;&amp;amp; a &amp;gt; -7*s;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
function drawJoystick(ox, oy) {&lt;br /&gt;
    const c = document.getElementById('ib-joystick-canvas');&lt;br /&gt;
    const jc = c.getContext('2d');&lt;br /&gt;
    jc.clearRect(0, 0, 128, 128);&lt;br /&gt;
    &lt;br /&gt;
    const maxOff = 20;&lt;br /&gt;
    const cx = Math.max(-maxOff, Math.min(maxOff, ox));&lt;br /&gt;
    const cy = Math.max(-maxOff, Math.min(maxOff, oy));&lt;br /&gt;
    jc.drawImage(getSprite(1, 7), cx, cy);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function onJoystickStart(e) { e.preventDefault(); joyX = e.touches[0].clientX; joyY = e.touches[0].clientY; drawJoystick(0, 0); }&lt;br /&gt;
  function onJoystickMove(e) {&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    const dx = e.touches[0].clientX - joyX, dy = e.touches[0].clientY - joyY;&lt;br /&gt;
    const dist = Math.hypot(dx, dy), maxR = 40, s = dist &amp;gt; maxR ? maxR / dist : 1;&lt;br /&gt;
    setJoystickKeys(dx, dy); drawJoystick(dx * s, dy * s);&lt;br /&gt;
  }&lt;br /&gt;
  function onJoystickEnd(e) { e.preventDefault(); keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false; drawJoystick(0, 0); }&lt;br /&gt;
  function onActionStart(e) { e.preventDefault(); player.interact(); }&lt;br /&gt;
&lt;br /&gt;
  function addJoystickListeners(el) {&lt;br /&gt;
    el.addEventListener('touchstart', onJoystickStart, { passive: false });&lt;br /&gt;
    el.addEventListener('touchmove',  onJoystickMove,  { passive: false });&lt;br /&gt;
    el.addEventListener('touchend',   onJoystickEnd,   { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawMobileSprites() {&lt;br /&gt;
    drawJoystick(0, 0);&lt;br /&gt;
    document.getElementById('ib-action-sprite').getContext('2d').drawImage(getSprite(0, 7), 0, 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function initMobileControls() {&lt;br /&gt;
    if (!isMobile()) return;&lt;br /&gt;
    document.getElementById('ib-mobile-controls').style.display = 'flex';&lt;br /&gt;
    drawMobileSprites();&lt;br /&gt;
    addJoystickListeners(document.getElementById('ib-joystick-canvas'));&lt;br /&gt;
    document.getElementById('ib-action-sprite').addEventListener('touchstart', onActionStart, { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    bakeSprites(); bakeBackground(); initMobileControls();&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    if (isMobile()) {&lt;br /&gt;
      document.getElementById('ib-game-container').requestFullscreen().catch(() =&amp;gt; {});&lt;br /&gt;
      screen.orientation?.lock('landscape').catch(() =&amp;gt; {});&lt;br /&gt;
    }&lt;br /&gt;
    randomizeObjects(); playClicked = true; tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45875</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45875"/>
		<updated>2026-04-09T21:39:44Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle|dummy=3}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45874</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45874"/>
		<updated>2026-04-09T21:39:36Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Play&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;You Win&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-mobile-controls&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-joystick-canvas&amp;quot; width=&amp;quot;192&amp;quot; height=&amp;quot;192&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-action-sprite&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; max-width: 100vw; }&lt;br /&gt;
  #ib-game-container canvas { display: block; image-rendering: pixelated; }&lt;br /&gt;
  #ib-gameCanvas { max-width: 100%; height: auto; }&lt;br /&gt;
&lt;br /&gt;
  #ib-game-container:fullscreen,&lt;br /&gt;
  #ib-game-container:-webkit-full-screen {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    width: 100vw;&lt;br /&gt;
    height: 100vh;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    background: #000;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ib-game-container:fullscreen #ib-gameCanvas,&lt;br /&gt;
  #ib-game-container:-webkit-full-screen #ib-gameCanvas {&lt;br /&gt;
    width: auto;&lt;br /&gt;
    height: 100vh;&lt;br /&gt;
    max-width: 100vw;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  #ib-mobile-controls {&lt;br /&gt;
    display: none; position: absolute; bottom: 0; left: 0;&lt;br /&gt;
    width: 100%; justify-content: space-between; align-items: flex-end;&lt;br /&gt;
    padding: 16px; box-sizing: border-box; pointer-events: none;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-joystick-canvas, #ib-action-sprite { pointer-events: all; opacity: 0.8; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const KEY_MAP = { w: 'ArrowUp', a: 'ArrowLeft', s: 'ArrowDown', d: 'ArrowRight' };&lt;br /&gt;
  const MOVE_DURATION = 24;&lt;br /&gt;
  const DIAG_DURATION = Math.round(MOVE_DURATION * Math.SQRT2);&lt;br /&gt;
  const DIAG = [&lt;br /&gt;
    ['ArrowUp',   'ArrowLeft',  -1, -1, 2],&lt;br /&gt;
    ['ArrowDown', 'ArrowLeft',  -1,  1, 1],&lt;br /&gt;
    ['ArrowDown', 'ArrowRight',  1,  1, 0],&lt;br /&gt;
    ['ArrowUp',   'ArrowRight',  1, -1, 2],&lt;br /&gt;
  ];&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  const keyTimes = {};&lt;br /&gt;
  const DIR_DX = [1, -1, 0, 0];&lt;br /&gt;
  const DIR_DY = [0,  0, -1, 1];&lt;br /&gt;
&lt;br /&gt;
  function keyActive(k) { return keys[k] || performance.now() - (keyTimes[k] || 0) &amp;lt; 120; }&lt;br /&gt;
&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    const key = KEY_MAP[e.key] || e.key;&lt;br /&gt;
    if (key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[key][2]; return; }&lt;br /&gt;
    keyTimes[key] = performance.now(); keys[key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[KEY_MAP[e.key] || e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; c * 10 + r), ARTIFACT_COL * 10 + ARTIFACT_ROW]);&lt;br /&gt;
&lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  const spriteCache = new Map();&lt;br /&gt;
  function getSprite(col, row) {&lt;br /&gt;
    const key = col * 10 + row;&lt;br /&gt;
    if (spriteCache.has(key)) return spriteCache.get(key);&lt;br /&gt;
    const c = document.createElement('canvas');&lt;br /&gt;
    c.width = c.height = TILE_SIZE;&lt;br /&gt;
    c.getContext('2d').drawImage(mainSprite, col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
    spriteCache.set(key, c); return c;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function bakeSprites() {&lt;br /&gt;
    for (let c = 0; c &amp;lt; 5; c++) for (let r = 0; r &amp;lt; 6; r++) getSprite(c, r);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
    _startMove(dx, dy, dir, duration = MOVE_DURATION) {&lt;br /&gt;
      const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
      if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(nx * 10 + ny)) return false;&lt;br /&gt;
      this.direction = dir; this.tileX = nx; this.tileY = ny;&lt;br /&gt;
      this.startX = this.x; this.startY = this.y;&lt;br /&gt;
      this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
      this.moving = true; this.moveFrame = 0; this.moveDuration = duration;&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (let i = 0; i &amp;lt; DIAG.length; i++) {&lt;br /&gt;
        const [k1, k2, dx, dy, dir] = DIAG[i];&lt;br /&gt;
        if (keyActive(k1) &amp;amp;&amp;amp; keyActive(k2) &amp;amp;&amp;amp; this._startMove(dx, dy, dir, DIAG_DURATION)) return;&lt;br /&gt;
      }&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = DIR_DX[this.direction], dy = DIR_DY[this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      const t = this.moveFrame / this.moveDuration;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * t;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * t;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= this.moveDuration) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx, alpha) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i } = this;&lt;br /&gt;
      const rx = this.prevX + (this.x - this.prevX) * alpha;&lt;br /&gt;
      const ry = this.prevY + (this.y - this.prevY) * alpha;&lt;br /&gt;
      ctx.drawImage(getSprite(i &amp;gt; 0 ? 3 : f, d), Math.round(rx), Math.round(ry));&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      ctx.drawImage(getSprite(this.open ? 1 : 0, 4), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(getSprite(col, 5), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const offscreen = document.createElement('canvas');&lt;br /&gt;
  offscreen.width = canvas.width;&lt;br /&gt;
  offscreen.height = canvas.height;&lt;br /&gt;
  const offCtx = offscreen.getContext('2d');&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function bakeBackground() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        offCtx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawStats() {&lt;br /&gt;
    ctx.font = '16px monospace';&lt;br /&gt;
    ctx.fillStyle = '#fff';&lt;br /&gt;
    ctx.fillText(`FPS: ${fps}  UPS: ${ups}`, 8, 20);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render(alpha) {&lt;br /&gt;
    ctx.drawImage(offscreen, 0, 0);&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    for (let i = 0; i &amp;lt; objects.length; i++) objects[i].draw(ctx);&lt;br /&gt;
    player.draw(ctx, alpha);&lt;br /&gt;
    drawStats();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
  let fps = 0, ups = 0, fCount = 0, uCount = 0, statTime = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; uCount++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    fCount++;&lt;br /&gt;
    if (timestamp - statTime &amp;gt;= 1000) { fps = fCount; ups = uCount; fCount = 0; uCount = 0; statTime = timestamp; }&lt;br /&gt;
    render(lag / MS_PER_UPDATE);&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function isMobile() { return navigator.maxTouchPoints &amp;gt; 0; }&lt;br /&gt;
&lt;br /&gt;
  let joyX = 0, joyY = 0;&lt;br /&gt;
  function setJoystickKeys(dx, dy) {&lt;br /&gt;
    keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false;&lt;br /&gt;
    if (Math.hypot(dx, dy) &amp;lt; 20) return;&lt;br /&gt;
    const a = Math.atan2(dy, dx), s = Math.PI / 8;&lt;br /&gt;
    keys.ArrowRight = a &amp;gt; -3*s &amp;amp;&amp;amp; a &amp;lt; 3*s;&lt;br /&gt;
    keys.ArrowLeft  = a &amp;gt;  5*s || a &amp;lt; -5*s;&lt;br /&gt;
    keys.ArrowDown  = a &amp;gt;    s &amp;amp;&amp;amp; a &amp;lt;  7*s;&lt;br /&gt;
    keys.ArrowUp    = a &amp;lt;   -s &amp;amp;&amp;amp; a &amp;gt; -7*s;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawJoystick(ox, oy) {&lt;br /&gt;
    const c = document.getElementById('ib-joystick-canvas');&lt;br /&gt;
    const jc = c.getContext('2d');&lt;br /&gt;
    jc.clearRect(0, 0, 192, 192);&lt;br /&gt;
    jc.drawImage(getSprite(1, 7), 32 + ox, 32 + oy);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function onJoystickStart(e) { e.preventDefault(); joyX = e.touches[0].clientX; joyY = e.touches[0].clientY; drawJoystick(0, 0); }&lt;br /&gt;
  function onJoystickMove(e) {&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    const dx = e.touches[0].clientX - joyX, dy = e.touches[0].clientY - joyY;&lt;br /&gt;
    const dist = Math.hypot(dx, dy), maxR = 40, s = dist &amp;gt; maxR ? maxR / dist : 1;&lt;br /&gt;
    setJoystickKeys(dx, dy); drawJoystick(dx * s, dy * s);&lt;br /&gt;
  }&lt;br /&gt;
  function onJoystickEnd(e) { e.preventDefault(); keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false; drawJoystick(0, 0); }&lt;br /&gt;
  function onActionStart(e) { e.preventDefault(); player.interact(); }&lt;br /&gt;
&lt;br /&gt;
  function addJoystickListeners(el) {&lt;br /&gt;
    el.addEventListener('touchstart', onJoystickStart, { passive: false });&lt;br /&gt;
    el.addEventListener('touchmove',  onJoystickMove,  { passive: false });&lt;br /&gt;
    el.addEventListener('touchend',   onJoystickEnd,   { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawMobileSprites() {&lt;br /&gt;
    drawJoystick(0, 0);&lt;br /&gt;
    document.getElementById('ib-action-sprite').getContext('2d').drawImage(getSprite(0, 7), 0, 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function initMobileControls() {&lt;br /&gt;
    if (!isMobile()) return;&lt;br /&gt;
    document.getElementById('ib-mobile-controls').style.display = 'flex';&lt;br /&gt;
    drawMobileSprites();&lt;br /&gt;
    addJoystickListeners(document.getElementById('ib-joystick-canvas'));&lt;br /&gt;
    document.getElementById('ib-action-sprite').addEventListener('touchstart', onActionStart, { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    bakeSprites(); bakeBackground(); initMobileControls();&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    if (isMobile()) {&lt;br /&gt;
      document.getElementById('ib-game-container').requestFullscreen().catch(() =&amp;gt; {});&lt;br /&gt;
      screen.orientation?.lock('landscape').catch(() =&amp;gt; {});&lt;br /&gt;
    }&lt;br /&gt;
    randomizeObjects(); playClicked = true; tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45873</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45873"/>
		<updated>2026-04-09T21:34:52Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle|dummy=1}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45872</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45872"/>
		<updated>2026-04-09T21:34:41Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Play&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;You Win&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-mobile-controls&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-joystick-canvas&amp;quot; width=&amp;quot;192&amp;quot; height=&amp;quot;192&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
    &amp;lt;canvas id=&amp;quot;ib-action-sprite&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; max-width: 100vw; }&lt;br /&gt;
  #ib-game-container canvas { display: block; image-rendering: pixelated; max-width: 100%; height: auto; }&lt;br /&gt;
  #ib-mobile-controls {&lt;br /&gt;
    display: none; position: absolute; bottom: 0; left: 0;&lt;br /&gt;
    width: 100%; justify-content: space-between; align-items: flex-end;&lt;br /&gt;
    padding: 16px; box-sizing: border-box; pointer-events: none;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-joystick-canvas, #ib-action-sprite { pointer-events: all; opacity: 0.8; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const KEY_MAP = { w: 'ArrowUp', a: 'ArrowLeft', s: 'ArrowDown', d: 'ArrowRight' };&lt;br /&gt;
  const MOVE_DURATION = 24;&lt;br /&gt;
  const DIAG_DURATION = Math.round(MOVE_DURATION * Math.SQRT2);&lt;br /&gt;
  const DIAG = [&lt;br /&gt;
    ['ArrowUp',   'ArrowLeft',  -1, -1, 2],&lt;br /&gt;
    ['ArrowDown', 'ArrowLeft',  -1,  1, 1],&lt;br /&gt;
    ['ArrowDown', 'ArrowRight',  1,  1, 0],&lt;br /&gt;
    ['ArrowUp',   'ArrowRight',  1, -1, 2],&lt;br /&gt;
  ];&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  const keyTimes = {};&lt;br /&gt;
  const DIR_DX = [1, -1, 0, 0];&lt;br /&gt;
  const DIR_DY = [0,  0, -1, 1];&lt;br /&gt;
&lt;br /&gt;
  function keyActive(k) { return keys[k] || performance.now() - (keyTimes[k] || 0) &amp;lt; 120; }&lt;br /&gt;
&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    const key = KEY_MAP[e.key] || e.key;&lt;br /&gt;
    if (key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[key][2]; return; }&lt;br /&gt;
    keyTimes[key] = performance.now(); keys[key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[KEY_MAP[e.key] || e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; c * 10 + r), ARTIFACT_COL * 10 + ARTIFACT_ROW]);&lt;br /&gt;
&lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  const spriteCache = new Map();&lt;br /&gt;
  function getSprite(col, row) {&lt;br /&gt;
    const key = col * 10 + row;&lt;br /&gt;
    if (spriteCache.has(key)) return spriteCache.get(key);&lt;br /&gt;
    const c = document.createElement('canvas');&lt;br /&gt;
    c.width = c.height = TILE_SIZE;&lt;br /&gt;
    c.getContext('2d').drawImage(mainSprite, col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
    spriteCache.set(key, c); return c;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function bakeSprites() {&lt;br /&gt;
    for (let c = 0; c &amp;lt; 5; c++) for (let r = 0; r &amp;lt; 6; r++) getSprite(c, r);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
    _startMove(dx, dy, dir, duration = MOVE_DURATION) {&lt;br /&gt;
      const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
      if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(nx * 10 + ny)) return false;&lt;br /&gt;
      this.direction = dir; this.tileX = nx; this.tileY = ny;&lt;br /&gt;
      this.startX = this.x; this.startY = this.y;&lt;br /&gt;
      this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
      this.moving = true; this.moveFrame = 0; this.moveDuration = duration;&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (let i = 0; i &amp;lt; DIAG.length; i++) {&lt;br /&gt;
        const [k1, k2, dx, dy, dir] = DIAG[i];&lt;br /&gt;
        if (keyActive(k1) &amp;amp;&amp;amp; keyActive(k2) &amp;amp;&amp;amp; this._startMove(dx, dy, dir, DIAG_DURATION)) return;&lt;br /&gt;
      }&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = DIR_DX[this.direction], dy = DIR_DY[this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      const t = this.moveFrame / this.moveDuration;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * t;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * t;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= this.moveDuration) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx, alpha) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i } = this;&lt;br /&gt;
      const rx = this.prevX + (this.x - this.prevX) * alpha;&lt;br /&gt;
      const ry = this.prevY + (this.y - this.prevY) * alpha;&lt;br /&gt;
      ctx.drawImage(getSprite(i &amp;gt; 0 ? 3 : f, d), Math.round(rx), Math.round(ry));&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      ctx.drawImage(getSprite(this.open ? 1 : 0, 4), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(getSprite(col, 5), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const offscreen = document.createElement('canvas');&lt;br /&gt;
  offscreen.width = canvas.width;&lt;br /&gt;
  offscreen.height = canvas.height;&lt;br /&gt;
  const offCtx = offscreen.getContext('2d');&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function bakeBackground() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        offCtx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawStats() {&lt;br /&gt;
    ctx.font = '16px monospace';&lt;br /&gt;
    ctx.fillStyle = '#fff';&lt;br /&gt;
    ctx.fillText(`FPS: ${fps}  UPS: ${ups}`, 8, 20);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render(alpha) {&lt;br /&gt;
    ctx.drawImage(offscreen, 0, 0);&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    for (let i = 0; i &amp;lt; objects.length; i++) objects[i].draw(ctx);&lt;br /&gt;
    player.draw(ctx, alpha);&lt;br /&gt;
    drawStats();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
  let fps = 0, ups = 0, fCount = 0, uCount = 0, statTime = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; uCount++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    fCount++;&lt;br /&gt;
    if (timestamp - statTime &amp;gt;= 1000) { fps = fCount; ups = uCount; fCount = 0; uCount = 0; statTime = timestamp; }&lt;br /&gt;
    render(lag / MS_PER_UPDATE);&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function isMobile() { return navigator.maxTouchPoints &amp;gt; 0; }&lt;br /&gt;
&lt;br /&gt;
  let joyX = 0, joyY = 0;&lt;br /&gt;
  function setJoystickKeys(dx, dy) {&lt;br /&gt;
    keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false;&lt;br /&gt;
    if (Math.hypot(dx, dy) &amp;lt; 20) return;&lt;br /&gt;
    const a = Math.atan2(dy, dx), s = Math.PI / 8;&lt;br /&gt;
    keys.ArrowRight = a &amp;gt; -3*s &amp;amp;&amp;amp; a &amp;lt; 3*s;&lt;br /&gt;
    keys.ArrowLeft  = a &amp;gt;  5*s || a &amp;lt; -5*s;&lt;br /&gt;
    keys.ArrowDown  = a &amp;gt;    s &amp;amp;&amp;amp; a &amp;lt;  7*s;&lt;br /&gt;
    keys.ArrowUp    = a &amp;lt;   -s &amp;amp;&amp;amp; a &amp;gt; -7*s;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawJoystick(ox, oy) {&lt;br /&gt;
    const c = document.getElementById('ib-joystick-canvas');&lt;br /&gt;
    const jc = c.getContext('2d');&lt;br /&gt;
    jc.clearRect(0, 0, 192, 192);&lt;br /&gt;
    jc.drawImage(getSprite(1, 7), 32 + ox, 32 + oy);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function onJoystickStart(e) { e.preventDefault(); joyX = e.touches[0].clientX; joyY = e.touches[0].clientY; drawJoystick(0, 0); }&lt;br /&gt;
  function onJoystickMove(e) {&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    const dx = e.touches[0].clientX - joyX, dy = e.touches[0].clientY - joyY;&lt;br /&gt;
    const dist = Math.hypot(dx, dy), maxR = 40, s = dist &amp;gt; maxR ? maxR / dist : 1;&lt;br /&gt;
    setJoystickKeys(dx, dy); drawJoystick(dx * s, dy * s);&lt;br /&gt;
  }&lt;br /&gt;
  function onJoystickEnd(e) { e.preventDefault(); keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false; drawJoystick(0, 0); }&lt;br /&gt;
  function onActionStart(e) { e.preventDefault(); player.interact(); }&lt;br /&gt;
&lt;br /&gt;
  function addJoystickListeners(el) {&lt;br /&gt;
    el.addEventListener('touchstart', onJoystickStart, { passive: false });&lt;br /&gt;
    el.addEventListener('touchmove',  onJoystickMove,  { passive: false });&lt;br /&gt;
    el.addEventListener('touchend',   onJoystickEnd,   { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawMobileSprites() {&lt;br /&gt;
    drawJoystick(0, 0);&lt;br /&gt;
    document.getElementById('ib-action-sprite').getContext('2d').drawImage(getSprite(0, 7), 0, 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function initMobileControls() {&lt;br /&gt;
    if (!isMobile()) return;&lt;br /&gt;
    document.getElementById('ib-mobile-controls').style.display = 'flex';&lt;br /&gt;
    drawMobileSprites();&lt;br /&gt;
    addJoystickListeners(document.getElementById('ib-joystick-canvas'));&lt;br /&gt;
    document.getElementById('ib-action-sprite').addEventListener('touchstart', onActionStart, { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    bakeSprites(); bakeBackground(); initMobileControls();&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    if (isMobile()) {&lt;br /&gt;
      document.getElementById('ib-game-container').requestFullscreen().catch(() =&amp;gt; {});&lt;br /&gt;
      screen.orientation?.lock('landscape').catch(() =&amp;gt; {});&lt;br /&gt;
    }&lt;br /&gt;
    randomizeObjects(); playClicked = true; tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45871</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45871"/>
		<updated>2026-04-09T21:24:16Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle|dummy=2}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45870</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45870"/>
		<updated>2026-04-09T21:23:05Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: feat: mobile support fot aws movement mobile and diagonal, performance improvements&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Jogar&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;VITÓRIA&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-mobile-controls&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;ib-joystick-zone&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;canvas id=&amp;quot;ib-joystick-sprite&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;ib-action-zone&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;canvas id=&amp;quot;ib-action-sprite&amp;quot; width=&amp;quot;128&amp;quot; height=&amp;quot;128&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; max-width: 100vw; }&lt;br /&gt;
  #ib-gameCanvas { display: block; image-rendering: pixelated; max-width: 100%; height: auto; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-mobile-controls {&lt;br /&gt;
    display: none; position: absolute; bottom: 0; left: 0;&lt;br /&gt;
    width: 100%; justify-content: space-between; align-items: flex-end;&lt;br /&gt;
    padding: 16px; box-sizing: border-box; pointer-events: none;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-joystick-zone, #ib-action-zone {&lt;br /&gt;
    width: 192px; height: 192px; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    pointer-events: all; opacity: 0.8;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const KEY_MAP = { w: 'ArrowUp', a: 'ArrowLeft', s: 'ArrowDown', d: 'ArrowRight' };&lt;br /&gt;
  const MOVE_DURATION = 24;&lt;br /&gt;
  const DIAG_DURATION = Math.round(MOVE_DURATION * Math.SQRT2);&lt;br /&gt;
  const DIAG = [&lt;br /&gt;
    ['ArrowUp',   'ArrowLeft',  -1, -1, 2],&lt;br /&gt;
    ['ArrowDown', 'ArrowLeft',  -1,  1, 1],&lt;br /&gt;
    ['ArrowDown', 'ArrowRight',  1,  1, 0],&lt;br /&gt;
    ['ArrowUp',   'ArrowRight',  1, -1, 2],&lt;br /&gt;
  ];&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  const keyTimes = {};&lt;br /&gt;
  const DIR_DX = [1, -1, 0, 0];&lt;br /&gt;
  const DIR_DY = [0,  0, -1, 1];&lt;br /&gt;
&lt;br /&gt;
  function keyActive(k) { return keys[k] || performance.now() - (keyTimes[k] || 0) &amp;lt; 120; }&lt;br /&gt;
&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    const key = KEY_MAP[e.key] || e.key;&lt;br /&gt;
    if (key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[key][2]; return; }&lt;br /&gt;
    keyTimes[key] = performance.now(); keys[key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[KEY_MAP[e.key] || e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; c * 10 + r), ARTIFACT_COL * 10 + ARTIFACT_ROW]);&lt;br /&gt;
&lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  const spriteCache = new Map();&lt;br /&gt;
  function getSprite(col, row) {&lt;br /&gt;
    const key = col * 10 + row;&lt;br /&gt;
    if (spriteCache.has(key)) return spriteCache.get(key);&lt;br /&gt;
    const c = document.createElement('canvas');&lt;br /&gt;
    c.width = c.height = TILE_SIZE;&lt;br /&gt;
    c.getContext('2d').drawImage(mainSprite, col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
    spriteCache.set(key, c); return c;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function bakeSprites() {&lt;br /&gt;
    for (let c = 0; c &amp;lt; 5; c++) for (let r = 0; r &amp;lt; 6; r++) getSprite(c, r);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
    _startMove(dx, dy, dir, duration = MOVE_DURATION) {&lt;br /&gt;
      const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
      if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(nx * 10 + ny)) return false;&lt;br /&gt;
      this.direction = dir; this.tileX = nx; this.tileY = ny;&lt;br /&gt;
      this.startX = this.x; this.startY = this.y;&lt;br /&gt;
      this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
      this.moving = true; this.moveFrame = 0; this.moveDuration = duration;&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (let i = 0; i &amp;lt; DIAG.length; i++) {&lt;br /&gt;
        const [k1, k2, dx, dy, dir] = DIAG[i];&lt;br /&gt;
        if (keyActive(k1) &amp;amp;&amp;amp; keyActive(k2) &amp;amp;&amp;amp; this._startMove(dx, dy, dir, DIAG_DURATION)) return;&lt;br /&gt;
      }&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = DIR_DX[this.direction], dy = DIR_DY[this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      const t = this.moveFrame / this.moveDuration;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * t;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * t;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= this.moveDuration) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      this.prevX = this.x; this.prevY = this.y;&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx, alpha) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i } = this;&lt;br /&gt;
      const rx = this.prevX + (this.x - this.prevX) * alpha;&lt;br /&gt;
      const ry = this.prevY + (this.y - this.prevY) * alpha;&lt;br /&gt;
      ctx.drawImage(getSprite(i &amp;gt; 0 ? 3 : f, d), Math.round(rx), Math.round(ry));&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      ctx.drawImage(getSprite(this.open ? 1 : 0, 4), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(getSprite(col, 5), this.tileX * TILE_SIZE, this.tileY * TILE_SIZE);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const offscreen = document.createElement('canvas');&lt;br /&gt;
  offscreen.width = canvas.width;&lt;br /&gt;
  offscreen.height = canvas.height;&lt;br /&gt;
  const offCtx = offscreen.getContext('2d');&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function bakeBackground() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        offCtx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawStats() {&lt;br /&gt;
    ctx.font = '16px monospace';&lt;br /&gt;
    ctx.fillStyle = '#fff';&lt;br /&gt;
    ctx.fillText(`FPS: ${fps}  UPS: ${ups}`, 8, 20);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render(alpha) {&lt;br /&gt;
    ctx.drawImage(offscreen, 0, 0);&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    for (let i = 0; i &amp;lt; objects.length; i++) objects[i].draw(ctx);&lt;br /&gt;
    player.draw(ctx, alpha);&lt;br /&gt;
    drawStats();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
  let fps = 0, ups = 0, fCount = 0, uCount = 0, statTime = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; uCount++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    fCount++;&lt;br /&gt;
    if (timestamp - statTime &amp;gt;= 1000) { fps = fCount; ups = uCount; fCount = 0; uCount = 0; statTime = timestamp; }&lt;br /&gt;
    render(lag / MS_PER_UPDATE);&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function isMobile() { return navigator.maxTouchPoints &amp;gt; 0; }&lt;br /&gt;
&lt;br /&gt;
  let joyX = 0, joyY = 0;&lt;br /&gt;
  function setJoystickKeys(dx, dy) {&lt;br /&gt;
    keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false;&lt;br /&gt;
    if (Math.hypot(dx, dy) &amp;lt; 20) return;&lt;br /&gt;
    const a = Math.atan2(dy, dx), s = Math.PI / 8;&lt;br /&gt;
    keys.ArrowRight = a &amp;gt; -3*s &amp;amp;&amp;amp; a &amp;lt; 3*s;&lt;br /&gt;
    keys.ArrowLeft  = a &amp;gt;  5*s || a &amp;lt; -5*s;&lt;br /&gt;
    keys.ArrowDown  = a &amp;gt;    s &amp;amp;&amp;amp; a &amp;lt;  7*s;&lt;br /&gt;
    keys.ArrowUp    = a &amp;lt;   -s &amp;amp;&amp;amp; a &amp;gt; -7*s;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function onJoystickStart(e) { e.preventDefault(); joyX = e.touches[0].clientX; joyY = e.touches[0].clientY; }&lt;br /&gt;
  function onJoystickMove(e)  { e.preventDefault(); setJoystickKeys(e.touches[0].clientX - joyX, e.touches[0].clientY - joyY); }&lt;br /&gt;
  function onJoystickEnd(e)   { e.preventDefault(); keys.ArrowRight = keys.ArrowLeft = keys.ArrowUp = keys.ArrowDown = false; }&lt;br /&gt;
  function onActionStart(e)   { e.preventDefault(); player.interact(); }&lt;br /&gt;
&lt;br /&gt;
  function addJoystickListeners(el) {&lt;br /&gt;
    el.addEventListener('touchstart', onJoystickStart, { passive: false });&lt;br /&gt;
    el.addEventListener('touchmove',  onJoystickMove,  { passive: false });&lt;br /&gt;
    el.addEventListener('touchend',   onJoystickEnd,   { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function drawMobileSprites() {&lt;br /&gt;
    document.getElementById('ib-joystick-sprite').getContext('2d').drawImage(getSprite(1, 7), 0, 0);&lt;br /&gt;
    document.getElementById('ib-action-sprite').getContext('2d').drawImage(getSprite(0, 7), 0, 0);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function initMobileControls() {&lt;br /&gt;
    if (!isMobile()) return;&lt;br /&gt;
    document.getElementById('ib-mobile-controls').style.display = 'flex';&lt;br /&gt;
    drawMobileSprites();&lt;br /&gt;
    addJoystickListeners(document.getElementById('ib-joystick-zone'));&lt;br /&gt;
    document.getElementById('ib-action-zone').addEventListener('touchstart', onActionStart, { passive: false });&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    bakeSprites(); bakeBackground(); initMobileControls();&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    if (isMobile()) {&lt;br /&gt;
      document.documentElement.requestFullscreen().catch(() =&amp;gt; {});&lt;br /&gt;
      screen.orientation?.lock('landscape').catch(() =&amp;gt; {});&lt;br /&gt;
    }&lt;br /&gt;
    randomizeObjects(); playClicked = true; tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Ib_noland_sprite.png&amp;diff=45869</id>
		<title>Arquivo:Ib noland sprite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Ib_noland_sprite.png&amp;diff=45869"/>
		<updated>2026-04-09T21:16:54Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: Igor is back carregada uma nova versão de Arquivo:Ib noland sprite.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Descrição do arquivo ==&lt;br /&gt;
Sprite para testar o puzze do noland na wiki, mais informacoes pergunte ao Igor Is Back.&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45458</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45458"/>
		<updated>2026-04-05T17:44:25Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle|dummy=1}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45457</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45457"/>
		<updated>2026-04-05T17:43:38Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MeuJogo|dummy=1}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45456</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45456"/>
		<updated>2026-04-05T17:43:28Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: chore: dummy change&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:MeuJogo|dummy=1}} // so pra tentar forcar a att&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45455</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45455"/>
		<updated>2026-04-05T17:35:25Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: refactor: change some tittles to portuguese as default&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Jogar&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;XTRANHO&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; }&lt;br /&gt;
  #ib-gameCanvas { display: block; image-rendering: pixelated; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    if (e.key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(e.key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[e.key][2]; return; }&lt;br /&gt;
    keys[e.key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; `${c},${r}`), `${ARTIFACT_COL},${ARTIFACT_ROW}`]);&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
    _startMove(dx, dy, dir) {&lt;br /&gt;
      const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
      if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(`${nx},${ny}`)) return false;&lt;br /&gt;
      this.direction = dir; this.tileX = nx; this.tileY = ny;&lt;br /&gt;
      this.startX = this.x; this.startY = this.y;&lt;br /&gt;
      this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
      this.moving = true; this.moveFrame = 0;&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = [1,-1,0,0][this.direction], dy = [0,0,-1,1][this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * this.moveFrame / 24;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * this.moveFrame / 24;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= 24) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i, x, y } = this;&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      ctx.drawImage(mainSprite, (i &amp;gt; 0 ? 3 : f) * s, d * s, s, s, Math.round(x), Math.round(y), s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      ctx.drawImage(mainSprite, (this.open ? s : 0), 4 * s, s, s, this.tileX * s, this.tileY * s, s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(mainSprite, col * s, 5 * s, s, s, this.tileX * s, this.tileY * s, s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function drawTiles() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        ctx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render() {&lt;br /&gt;
    drawTiles();&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    objects.forEach(o =&amp;gt; o.draw(ctx));&lt;br /&gt;
    player.draw(ctx);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    render();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    randomizeObjects();&lt;br /&gt;
    playClicked = true;&lt;br /&gt;
    tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45454</id>
		<title>Usuário:Igor is back</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Usu%C3%A1rio:Igor_is_back&amp;diff=45454"/>
		<updated>2026-04-05T17:32:50Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: chore: test noland puzzle widget&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#widget:IbNolandPuzzle}}&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45453</id>
		<title>Widget:IbNolandPuzzle</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Widget:IbNolandPuzzle&amp;diff=45453"/>
		<updated>2026-04-05T17:31:11Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: feat: noland puzzle v1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div id=&amp;quot;ib-game-container&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-start-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;button id=&amp;quot;ib-play-btn&amp;quot;&amp;gt;Play&amp;lt;/button&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;ib-end-screen&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;You Win&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;canvas id=&amp;quot;ib-gameCanvas&amp;quot;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  #ib-game-container { position: relative; display: inline-block; }&lt;br /&gt;
  #ib-gameCanvas { display: block; image-rendering: pixelated; }&lt;br /&gt;
  #ib-start-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: flex;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: #1a1a2e; z-index: 10;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-end-screen {&lt;br /&gt;
    position: absolute; inset: 0; display: none;&lt;br /&gt;
    justify-content: center; align-items: center;&lt;br /&gt;
    background: rgba(0,0,0,0.85); z-index: 10;&lt;br /&gt;
    color: #e2e2e2; font-size: 64px; letter-spacing: 8px;&lt;br /&gt;
  }&lt;br /&gt;
  #ib-play-btn {&lt;br /&gt;
    padding: 16px 48px; font-size: 24px; cursor: pointer;&lt;br /&gt;
    background: #16213e; color: #e2e2e2;&lt;br /&gt;
    border: 2px solid #e2e2e2; letter-spacing: 4px;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
  const COLS = 7, ROWS = 5, TILE_SIZE = 128;&lt;br /&gt;
  const canvas = document.getElementById('ib-gameCanvas');&lt;br /&gt;
  const ctx = canvas.getContext('2d');&lt;br /&gt;
  canvas.width = COLS * TILE_SIZE;&lt;br /&gt;
  canvas.height = ROWS * TILE_SIZE;&lt;br /&gt;
&lt;br /&gt;
  const DIRS = {&lt;br /&gt;
    ArrowRight: [1,  0, 0],&lt;br /&gt;
    ArrowLeft:  [-1, 0, 1],&lt;br /&gt;
    ArrowUp:    [0, -1, 2],&lt;br /&gt;
    ArrowDown:  [0,  1, 3]&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  const keys = {};&lt;br /&gt;
  document.addEventListener('keydown', e =&amp;gt; {&lt;br /&gt;
    if (e.key === '1') { player.interact(); return; }&lt;br /&gt;
    if (!(e.key in DIRS)) return;&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    if (e.ctrlKey) { player.direction = DIRS[e.key][2]; return; }&lt;br /&gt;
    keys[e.key] = true;&lt;br /&gt;
  });&lt;br /&gt;
  document.addEventListener('keyup', e =&amp;gt; { keys[e.key] = false; });&lt;br /&gt;
&lt;br /&gt;
  const OBJECT_POS = [[1,0],[3,0],[5,0],[6,2],[5,4],[3,4],[1,4],[0,2]];&lt;br /&gt;
  const ARTIFACT_COL = 3, ARTIFACT_ROW = 2;&lt;br /&gt;
  const OBJECT_TILES = new Set([...OBJECT_POS.map(([c,r]) =&amp;gt; `${c},${r}`), `${ARTIFACT_COL},${ARTIFACT_ROW}`]);&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  const mainSprite = new Image();&lt;br /&gt;
  mainSprite.src = 'https://wiki.gla.com.br/images/3/30/Ib_noland_sprite.png';&lt;br /&gt;
&lt;br /&gt;
  const tileImage = new Image();&lt;br /&gt;
  tileImage.src = 'https://wiki.gla.com.br/images/9/98/Ib_basic_tile.png';&lt;br /&gt;
&lt;br /&gt;
  class Character {&lt;br /&gt;
    constructor(tileX, tileY) {&lt;br /&gt;
      this.tileX = tileX; this.tileY = tileY;&lt;br /&gt;
      this.x = tileX * TILE_SIZE; this.y = tileY * TILE_SIZE;&lt;br /&gt;
      this.direction = 0; this.animFrame = 0;&lt;br /&gt;
      this.moving = false; this.moveFrame = 0;&lt;br /&gt;
      this.interactFrame = 0; this.cooldown = 0;&lt;br /&gt;
    }&lt;br /&gt;
    _startMove(dx, dy, dir) {&lt;br /&gt;
      const nx = this.tileX + dx, ny = this.tileY + dy;&lt;br /&gt;
      if (nx &amp;lt; 0 || nx &amp;gt;= COLS || ny &amp;lt; 0 || ny &amp;gt;= ROWS || OBJECT_TILES.has(`${nx},${ny}`)) return false;&lt;br /&gt;
      this.direction = dir; this.tileX = nx; this.tileY = ny;&lt;br /&gt;
      this.startX = this.x; this.startY = this.y;&lt;br /&gt;
      this.targetX = nx * TILE_SIZE; this.targetY = ny * TILE_SIZE;&lt;br /&gt;
      this.moving = true; this.moveFrame = 0;&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    tryMove() {&lt;br /&gt;
      for (const key in DIRS)&lt;br /&gt;
        if (keys[key] &amp;amp;&amp;amp; this._startMove(...DIRS[key])) break;&lt;br /&gt;
    }&lt;br /&gt;
    _finishMove() {&lt;br /&gt;
      this.x = this.targetX; this.y = this.targetY;&lt;br /&gt;
      this.moving = false; this.animFrame = 0;&lt;br /&gt;
    }&lt;br /&gt;
    interact() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0 || this.moving || this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      const dx = [1,-1,0,0][this.direction], dy = [0,0,-1,1][this.direction];&lt;br /&gt;
      const tx = this.tileX + dx, ty = this.tileY + dy;&lt;br /&gt;
      if (tx === ARTIFACT_COL &amp;amp;&amp;amp; ty === ARTIFACT_ROW) { if (artifact.open) endGame(); return; }&lt;br /&gt;
      this.interactFrame = 1; this.cooldown = 60;&lt;br /&gt;
      triggerPedestal(tx, ty);&lt;br /&gt;
    }&lt;br /&gt;
    _updateMove() {&lt;br /&gt;
      this.moveFrame++;&lt;br /&gt;
      this.x = this.startX + (this.targetX - this.startX) * this.moveFrame / 24;&lt;br /&gt;
      this.y = this.startY + (this.targetY - this.startY) * this.moveFrame / 24;&lt;br /&gt;
      this.animFrame = (Math.floor(this.moveFrame / 8) % 2) + 1;&lt;br /&gt;
      if (this.moveFrame &amp;gt;= 24) this._finishMove();&lt;br /&gt;
    }&lt;br /&gt;
    update() {&lt;br /&gt;
      if (this.cooldown &amp;gt; 0) this.cooldown--;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0 &amp;amp;&amp;amp; ++this.interactFrame &amp;gt; 24) this.interactFrame = 0;&lt;br /&gt;
      if (this.interactFrame &amp;gt; 0) return;&lt;br /&gt;
      if (!this.moving) { this.tryMove(); return; }&lt;br /&gt;
      this._updateMove();&lt;br /&gt;
    }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const { direction: d, animFrame: f, interactFrame: i, x, y } = this;&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      ctx.drawImage(mainSprite, (i &amp;gt; 0 ? 3 : f) * s, d * s, s, s, Math.round(x), Math.round(y), s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class Artifact {&lt;br /&gt;
    constructor() { this.tileX = ARTIFACT_COL; this.tileY = ARTIFACT_ROW; this.open = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      ctx.drawImage(mainSprite, (this.open ? s : 0), 4 * s, s, s, this.tileX * s, this.tileY * s, s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  class GameObject {&lt;br /&gt;
    constructor(tileX, tileY) { this.tileX = tileX; this.tileY = tileY; this.active = false; }&lt;br /&gt;
    draw(ctx) {&lt;br /&gt;
      const s = TILE_SIZE;&lt;br /&gt;
      const col = this.active ? (Math.floor(pedestalTick / 8) % 4 + 1) : 0;&lt;br /&gt;
      ctx.drawImage(mainSprite, col * s, 5 * s, s, s, this.tileX * s, this.tileY * s, s, s);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const artifact = new Artifact();&lt;br /&gt;
  const player = new Character(3, 3);&lt;br /&gt;
  const objects = OBJECT_POS.map(([c,r]) =&amp;gt; new GameObject(c, r));&lt;br /&gt;
&lt;br /&gt;
  function drawTiles() {&lt;br /&gt;
    for (let r = 0; r &amp;lt; ROWS; r++)&lt;br /&gt;
      for (let c = 0; c &amp;lt; COLS; c++)&lt;br /&gt;
        ctx.drawImage(tileImage, c * TILE_SIZE, r * TILE_SIZE, TILE_SIZE, TILE_SIZE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function render() {&lt;br /&gt;
    drawTiles();&lt;br /&gt;
    artifact.draw(ctx);&lt;br /&gt;
    objects.forEach(o =&amp;gt; o.draw(ctx));&lt;br /&gt;
    player.draw(ctx);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  const MS_PER_UPDATE = 1000 / 60;&lt;br /&gt;
  let lastTime = 0, lag = 0, pedestalTick = 0;&lt;br /&gt;
&lt;br /&gt;
  function gameLoop(timestamp) {&lt;br /&gt;
    lag += Math.min(timestamp - lastTime, 100);&lt;br /&gt;
    lastTime = timestamp;&lt;br /&gt;
    while (lag &amp;gt;= MS_PER_UPDATE) { player.update(); pedestalTick++; lag -= MS_PER_UPDATE; }&lt;br /&gt;
    render();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function triggerPedestal(col, row) {&lt;br /&gt;
    if (artifact.open) return;&lt;br /&gt;
    const idx = objects.findIndex(o =&amp;gt; o.tileX === col &amp;amp;&amp;amp; o.tileY === row);&lt;br /&gt;
    if (idx === -1) return;&lt;br /&gt;
    const n = objects.length;&lt;br /&gt;
    [idx, (idx + 1) % n, (idx + n - 1) % n].forEach(i =&amp;gt; { objects[i].active = !objects[i].active; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) artifact.open = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function endGame() {&lt;br /&gt;
    document.getElementById('ib-end-screen').style.display = 'flex';&lt;br /&gt;
    setTimeout(() =&amp;gt; location.reload(), 2000);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  function randomizeObjects() {&lt;br /&gt;
    objects.forEach(o =&amp;gt; { o.active = Math.random() &amp;lt; 0.5; });&lt;br /&gt;
    if (objects.every(o =&amp;gt; !o.active)) objects[0].active = true;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  let started = false, playClicked = false;&lt;br /&gt;
  function tryStart() {&lt;br /&gt;
    if (started || !playClicked || !tileImage.complete || !mainSprite.complete) return;&lt;br /&gt;
    started = true;&lt;br /&gt;
    lastTime = performance.now();&lt;br /&gt;
    requestAnimationFrame(gameLoop);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  tileImage.onload = tryStart;&lt;br /&gt;
  mainSprite.onload = tryStart;&lt;br /&gt;
&lt;br /&gt;
  document.getElementById('ib-play-btn').addEventListener('click', () =&amp;gt; {&lt;br /&gt;
    document.getElementById('ib-start-screen').style.display = 'none';&lt;br /&gt;
    randomizeObjects();&lt;br /&gt;
    playClicked = true;&lt;br /&gt;
    tryStart();&lt;br /&gt;
  });&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Ib_basic_tile.png&amp;diff=45452</id>
		<title>Arquivo:Ib basic tile.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Ib_basic_tile.png&amp;diff=45452"/>
		<updated>2026-04-05T17:21:09Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: tile basico, mais informacoes pergunte ao Igor Is Back&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Descrição do arquivo ==&lt;br /&gt;
tile basico, mais informacoes pergunte ao Igor Is Back&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
	<entry>
		<id>https://wiki.gla.com.br/index.php?title=Arquivo:Ib_noland_sprite.png&amp;diff=45451</id>
		<title>Arquivo:Ib noland sprite.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.gla.com.br/index.php?title=Arquivo:Ib_noland_sprite.png&amp;diff=45451"/>
		<updated>2026-04-05T17:20:20Z</updated>

		<summary type="html">&lt;p&gt;Igor is back: Sprite para testar o puzze do noland na wiki, mais informacoes pergunte ao Igor Is Back.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Descrição do arquivo ==&lt;br /&gt;
Sprite para testar o puzze do noland na wiki, mais informacoes pergunte ao Igor Is Back.&lt;/div&gt;</summary>
		<author><name>Igor is back</name></author>
	</entry>
</feed>