florr.io hack

try to take over the florr.io!

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         florr.io hack
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the florr.io!
// @author       m28
// @match        https://florr.io/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
  'use strict';

  // some settings
  const client = {
    autoRespawn: {
      enabled: 0, // set to 1 to autoRespawn in spawnBiome
      spawnBiome: 'Hel'
    },
    bypassAfkCheck: {
      movementCheck: 1,
      afkButton: 1,
    },
    autoGrind: {
     enabled: 0 // set to 1 to enable autoGrind. also set autoRespawn.enabled to 1
    },
    tracers: 1, // draw tracers
  }

  let respawnState = 0 // keep track of whether need to press Continue or Ready
  let lastCheck = 0 // 100 timer
  // use original functions, add prefix
  const _console = {
    _log: window.console.log,
    log: function() {
      this._log(`%c[FlorrScript]`, `color: rgb(30, 150, 30); background: rgb(215, 255, 205)`, ...arguments)
    }
  }
  // array multiplier for canvas untransform
  const multiply = function(t,l){let e=t.length,n=t[0].length,$=l[0].length,r=Array(e);for(let f=0;f<e;++f){let o=Array($);r[f]=o;let g=t[f];for(let h=0;h<$;++h){let u=0;for(let i=0;i<n;++i)u+=g[i]*l[i][h];o[h]=u}}return r}
  // proxy identity function that does nothing
  let identity = function(a, b, c) {
    return Reflect.apply(a, b, c)
  }
  let beforeAnimationFrame = identity
  // keep track of tracers
  let tracers = {}, addTracer = function(t, color) {
    if(!color) { color = '#000000' } // use black if no color
    if(!tracers[color]) { tracers[color] = [] }
    tracers[color].push(t)
  }, mobs = []
  const parseColor = function(str) { // convert hex to [r, g, b]
    return [parseInt(str[1] + str[2], 16), parseInt(str[3] + str[4], 16), parseInt(str[5] + str[6], 16)]
  }
  // mouse position relative to screen center
  let mouse = {
    dx: 0,
    dy: 0
  }
  const main = function() {
    const canvas = document.getElementById('canvas')
    // record actual mouse position
    canvas._addEventListener('mousemove', function(e) {
      mouse.dx = (e.clientX - window.innerWidth * 0.5)
      mouse.dy = (e.clientY - window.innerHeight * 0.5)
    })
    const ctx = canvas.getContext('2d')
    beforeAnimationFrame = function(a, b, c) {
      // we want to run this right after the rendering function
      let n = performance.now()
      let w = canvas.width * 0.5, h = canvas.height * 0.5
      // converting between dom coordinates and canvas coordinates
      let ir = 1 / window.devicePixelRatio
      let dw = w * ir, dh = h * ir
      if(client.autoGrind.enabled) {
        // listeners.keydown({ keyCode:16, preventDefault:function() {} })
        let closestMob = false, closestDistance = -1
        // finding closest mob, doesn't include flowers
        for(let i=mobs.length-1;i>=0;i--) {
          let m = mobs[i]
          let dx = m[0] - w, dy = m[1] - h
          let d = dx * dx + dy * dy
          if(d < closestDistance || closestDistance < 0) {
            closestDistance = d
            closestMob = [dx, dy]
          }
        }
        if(closestMob) {
          let d = 100 * (closestDistance < 1 ? 1 : 1 / Math.sqrt(closestDistance))
          // move mouse to run toward mob
          mouse.dx = closestMob[0] * d
          mouse.dy = closestMob[1] * d
        }
      }
      // if Ready button not present reset state
      if(!buttons.Ready || !client.autoRespawn.enabled) { respawnState = 0 }
      if(n - lastCheck > 100) {
        // runs every 100 ms
        lastCheck = n
        !function() {
          if(client.autoRespawn.enabled) {
            if(respawnState < 1) {
              // change biome to specified biome
              if(buttons[client.autoRespawn.spawnBiome]) {
                if(clickButton(client.autoRespawn.spawnBiome)) {
                  respawnState ++
                }
                return
              }
            } else {
              if(buttons.Ready) {
                // click Ready button
                clickButton('Ready')
                return
              }
            } if(buttons.Continue) {
              // died, click Continue button
              clickButton('Continue')
              // reset state to make sure correct biome
              respawnState = 0
            }
          }
          if(client.bypassAfkCheck.afkButton) {
            // not fully working
            clickButton(`I'm here`)
          }
        }()
      } else if(!buttons.Continue && (client.bypassAfkCheck.movementCheck || client.autoGrind.enabled)) {
        let a = (n / 1000) % (2 * Math.PI)
        let tx = mouse.dx + dw + Math.sin(a), ty = mouse.dy + dh + Math.cos(a)
        // bypass afk check
        listeners.mousemove({
          clientX: tx,
          screenX: tx,
          clientY: ty,
          screenY: ty
        })
      }
      let transform = ctx.getTransform()
      ctx.translate(w, h)
      ctx.lineCap = 'round'
      ctx.miterLimit = 1.68
      ctx.font = '14px Ubuntu'
      for(let color in tracers) {
        let o = tracers[color]
        for(let i=o.length-1;i>=0;i--) {
          let t = o[i]
          let l = 1, a = 1
          // lower rarities are transparent, higher rarities thicker lines
          if(t[2] > 3) {
            l += (t[2] - 3) * 0.5
          } else {
            a = (t[2] + 1) / 4
          }
          let j = a
          // draw dashed line at 50% opacity
          a *= 0.5
          let r = parseColor(color)
          ctx.strokeStyle = `rgba(${r[0]}, ${r[1]}, ${r[2]}, ${a})`
          ctx.setLineDash([10, 15])
          ctx.lineWidth = l
          ctx._beginPath()
          let dx = t[0] - w, dy = t[1] - h
          ctx._moveTo(dx, dy)
          let d = dx * dx + dy * dy
          ctx._lineTo(0, 0)
          ctx._stroke()
          // draw solid line at 25% opacity of dashed line
          a *= 0.25
          ctx.strokeStyle = `rgba(${r[0]}, ${r[1]}, ${r[2]}, ${a})`
          ctx.setLineDash([])
          ctx._stroke()
          ctx._closePath()
          if(d > 300 * 300) {
            // target distance over 300, draw number
            let rd = Math.sqrt(d)
            if(rd < 350) {
              // between 300 and 350 make it transparent
              j *= (rd - 300) * 0.02
            }
            if(j > 0.05) {
              // if under 5% don't draw
              d = 1 / rd
              let y = 300 + (rd - 300) / (rd - 100) * 100
              // calculate text position
              let tx = dx * d * y
              let ty = dy * d * y + 7
              // if rgb(0, 0, 0) use white instead
              if(r[0] === 0 && r[1] === 0 && r[2] === 0) { r[0] = r[1] = r[2] = 255 }
              ctx.fillStyle = `rgb(${r[0]}, ${r[1]}, ${r[2]})`
              ctx.strokeStyle = '#000000'
              ctx.lineWidth = 1.68
              // use j^2 for opacity to make gradient steeper
              j *= j
              // when over 95% use 100% for better performance
              if(j < 0.95) { ctx.globalAlpha = j }
              // number is how many 100's away target is
              let text = '' + Math.round(rd / 100)
              ctx.textAlign = 'center'
              // stroke then fill
              ctx._strokeText(text, tx, ty)
              ctx.lineWidth = 10
              ctx._fillText(text, tx, ty)
              if(j < 0.95) { ctx.globalAlpha = 1 }
            }
          }
        }
      }
      ctx.setTransform(transform)
      let f = c[0]
      if(f.proxy) { c[0] = f.proxy } else {
        c[0] = f.proxy = new Proxy(f, { apply:function(a, b, c) {
          // we want to run these right before the rendering function
          lbuttons = buttons
          buttons = {}
          tracers = {}
          mobs = []
          window.l = listeners
          window.b = buttons
          return Reflect.apply(a, b, c)
        } })
      }
      return Reflect.apply(a, b, c)
    }
  }
  // might be used in the future
  window.console.log = new Proxy(window.console.log, { apply:function(a, b, c) {
    return Reflect.apply(a, b, c)
  } })
  const untransform = function(x, y, t) {
    let r = multiply([[x, y, 1]], [[t.a, t.b, 0], [t.c, t.d, 0], [t.e, t.f, 1]])[0]
    return [r[0] / r[2], r[1] / r[2]]
  }
  let lastText = [], lastWhiteText = []
  // rarity data, useful for determining of some text is a valid rarity type
  let rarities = {
    Common: {
      name: 'Common',
      color: '#7eef6d',
      index: 0
    },
    Unusual: {
      name: 'Unusual',
      color: '#ffe65d',
      index: 1
    },
    Rare: {
      name: 'Rare',
      color: '#4d52e3',
      index: 2
    },
    Epic: {
      name: 'Epic',
      color: '#861fde',
      index: 3
    },
    Legendary: {
      name: 'Legendary',
      color: '#de1f1f',
      index: 4
    },
    Mythic: {
      name: 'Mythic',
      color: '#1fdbde',
      index: 5
    },
    Ultra: {
      name: 'Ultra',
      color: '#ff2b75',
      index: 6
    },
    Super: {
      name: 'Super',
      color: '#000000',
      index: 7
    }
  }
  let colors = {}
  // reversing table for better performance
  for(let r in rarities) {
    colors[rarities[r].color] = rarities[r]
  }
  // funny text modification callback
  const textTransform = function(text, ctx) {
    if(text === 'Plinko') {
      text = 'Scam Machine'
    }
    return text
  }
  let buttons = {}, lbuttons = {}
  // proxy measureText to keep results consistent with fillText and strokeText
  window.CanvasRenderingContext2D.prototype._measureText = window.CanvasRenderingContext2D.prototype.measureText
  window.CanvasRenderingContext2D.prototype.measureText = new Proxy(window.CanvasRenderingContext2D.prototype.measureText, { apply:function(a, b, c) {
    c[0] = textTransform(c[0], b)
    return Reflect.apply(a, b, c)
  } })
  // proxy strokeText to keep results consistent with fillText
  window.CanvasRenderingContext2D.prototype._strokeText = window.CanvasRenderingContext2D.prototype.strokeText
  window.CanvasRenderingContext2D.prototype.strokeText = new Proxy(window.CanvasRenderingContext2D.prototype.strokeText, { apply:function(a, b, c) {
    c[0] = textTransform(c[0], b)
    return Reflect.apply(a, b, c)
  } })
  // proxy fillText for tracer detection
  window.CanvasRenderingContext2D.prototype._fillText = window.CanvasRenderingContext2D.prototype.fillText
  window.CanvasRenderingContext2D.prototype.fillText = new Proxy(window.CanvasRenderingContext2D.prototype.fillText, { apply:function(a, b, c) {
    if(lastText[1]) {
      // something detected here
      if(colors[b.fillStyle] && b.globalAlpha >= 1 && lastPaths[0][3] && client.tracers) {
        let t = lastPaths[0]
        if(c[0].startsWith('Lvl ') && parseInt(c[0].slice(4)) >= 0) {
          // flower found. treat it like a mob with rarity determined by level
          t = untransform((t[0] + t[1]) * 0.5, t[2], t[3])
          // tracer is black
          addTracer([t[0], t[1], colors[b.fillStyle].index], '#000000')
          lastPaths = [[], [], []]
        } else if(rarities[c[0]]) {
          // mob found. also includes player summons like egg beetles
          t = untransform((t[0] + t[1]) * 0.5, t[2], t[3])
          // tracer color same as rarity
          addTracer([t[0], t[1], colors[b.fillStyle].index], b.fillStyle)
          mobs.push([t[0], t[1]])
          lastPaths = [[], [], []]
        }
      }
    }
    // this fillText might be a button
    let bd = buttonData[c[0]]
    if(bd && (!bd.color || bd.color === b.fillStyle) && (!bd.font || bd.font === b.font)) {
      // button found
      let t = untransform(c[1], c[2], b.getTransform())
      // if this button was existing last render
      let o = lbuttons[c[0]]
      let n = buttons[c[0]] = {
        x: t[0],
        y: t[1],
        d: 1, // we don't want to click buttons that are moving. only click when d < 0.01
        s: performance.now(), // we want to wait 2000 ms before we click any button
        fillStyle: b.fillStyle,
        font: b.font
      }
      if(o) {
        n.d = Math.abs(n.x - o.x) + Math.abs(n.y - o.y) // calculate speed
        n.s = o.s // this button alr exists so its creation time is lower
      }
    }
    if(c[0] === `I'm here` && client.bypassAfkCheck.afkButton) {
      afkButton(untransform(c[1], c[2], b.getTransform()))
    }
    // for referencing next fillText
    lastText = [c, b.fillStyle]
    if(b.fillStyle === '#ffffff') {
      lastWhiteText = [c, b.fillStyle]
    }
    c[0] = textTransform(c[0], b)
    return Reflect.apply(a, b, c)
  } })
  let clicking = false
  // I'm here button clicker
  const afkButton = function(t) {
    if(clicking) { return }
    clicking = true
    setTimeout(function() {
      clicking = false
      clickAt(t.x, t.y)
    }, 500 + 2000 * Math.random())
  }
  // buttons we want to look for
  let buttonData = {
    Ready: {
      color: '#ffffff',
      font: '27.5px Ubuntu'
    },
    Garden: {
      color: '#ffffff',
      font: '16px Ubuntu'
    },
    Desert: {
      color: '#ffffff',
      font: '16px Ubuntu'
    },
    Ocean: {
      color: '#ffffff',
      font: '16px Ubuntu'
    },
    Jungle: {
      color: '#ffffff',
      font: '16px Ubuntu'
    },
    Hel: {
      color: '#ffffff',
      font: '16px Ubuntu'
    },
    'Play as guest': {},
    Continue: {
      color: '#ffffff',
    }
  }
  // keep track of paths, we can find health bars
  let lastPaths = [[], [], []], lastFill = ''
  let path = [], topPath = false, addSegment = function(s) {
    if(topPath) {
      topPath.push(s)
    } else {
      path.push(topPath = [s])
    }
  }
  // proxy beginPath
  window.CanvasRenderingContext2D.prototype._beginPath = window.CanvasRenderingContext2D.prototype.beginPath
  window.CanvasRenderingContext2D.prototype.beginPath = new Proxy(window.CanvasRenderingContext2D.prototype.beginPath, { apply:function(a, b, c) {
    path = []
    topPath = false
    // path restart
    return Reflect.apply(a, b, c)
  } })
  // proxy moveTo
  window.CanvasRenderingContext2D.prototype._moveTo = window.CanvasRenderingContext2D.prototype.moveTo
  window.CanvasRenderingContext2D.prototype.moveTo = new Proxy(window.CanvasRenderingContext2D.prototype.moveTo, { apply:function(a, b, c) {
    addSegment({
      type: 'moveTo',
      x: c[0],
      y: c[1]
    })
    return Reflect.apply(a, b, c)
  } })
  // proxy lineTo
  window.CanvasRenderingContext2D.prototype._lineTo = window.CanvasRenderingContext2D.prototype.lineTo
  window.CanvasRenderingContext2D.prototype.lineTo = new Proxy(window.CanvasRenderingContext2D.prototype.lineTo, { apply:function(a, b, c) {
    addSegment({
      type: 'lineTo',
      x: c[0],
      y: c[1]
    })
    return Reflect.apply(a, b, c)
  } })
  // proxy closePath
  window.CanvasRenderingContext2D.prototype._closePath = window.CanvasRenderingContext2D.prototype.closePath
  window.CanvasRenderingContext2D.prototype.closePath = new Proxy(window.CanvasRenderingContext2D.prototype.closePath, { apply:function(a, b, c) {
    path = []
    topPath = false
    // path destroyed
    return Reflect.apply(a, b, c)
  } })
  // proxy stroke
  window.CanvasRenderingContext2D.prototype._stroke = window.CanvasRenderingContext2D.prototype.stroke
  window.CanvasRenderingContext2D.prototype.stroke = new Proxy(window.CanvasRenderingContext2D.prototype.stroke, { apply:function(a, b, c) {
    // shift paths
    if(path.length === 1 && path[0].length === 2 && path[0][0].y === path[0][1].y) {
      lastPaths[0] = lastPaths[1]
      lastPaths[1] = lastPaths[2]
      lastPaths[2] = [path[0][0].x, path[0][1].x, path[0][0].y, b.getTransform()]
    }
    return Reflect.apply(a, b, c)
  } })
  // proxy fill
  window.CanvasRenderingContext2D.prototype._fill = window.CanvasRenderingContext2D.prototype.fill
  window.CanvasRenderingContext2D.prototype.fill = new Proxy(window.CanvasRenderingContext2D.prototype.fill, { apply:function(a, b, c) {
    lastFill = b.fillStyle
    return Reflect.apply(a, b, c)
  } })
  // proxy requestAnimationFrame for render hooks
  window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, { apply:function(a, b, c) {
    return beforeAnimationFrame(a, b, c)
  } })
  // wait for load
  const interval = setInterval(function() {
    if(document.body) {
      clearInterval(interval)
      main()
    }
  })
  // we can force an arraybuffer instantiation if want
  if(0) {
    window.WebAssembly.instantiateStreaming = new Proxy(window.WebAssembly.instantiateStreaming, { apply:function(a, b, c) {
      let d = new Response()
      c[0] = d
      return Reflect.apply(a, b, c)
    } })
  }
  const listeners = {}, trigger = function(type, data) {
    if(listeners[type]) { listeners[type](data) }
  }
  // universal hook for event listeners
  const listenerApply = function(a, b, c) {
    if(c[0] === 'mousemove') {
      listeners.mousemove = c[1]
    }
    if(c[0] === 'blur' || c[0] === 'focus' || c[0] === 'visibilitychange') {
      // makes it easier to afk and stuff
      return
    }
    if(b && b.id === 'canvas') {
      if(c[0] === 'mousedown') {
        listeners.mousedown = c[1]
      }
    }
    if(c[0] === 'mouseup') {
      listeners.mouseup = c[1]
    }
    if(c[0] === 'keydown') {
      listeners.keydown = c[1]
    }
    if(c[0] === 'keyup') {
      listeners.keyup = c[1]
    }
    return Reflect.apply(a, b, c)
  }
  const clickAt = function(x, y) {
    let ir = 1 / window.devicePixelRatio
    x *= ir
    y *= ir
    // move mouse
    listeners.mousemove({
      clientX: x,
      screenX: x,
      clientY: y,
      screenY: y
    })
    // mouse down
    listeners.mousedown({
      preventDefault: function() {},
      clientX: x,
      clientY: y
    })
    // mouse up
    listeners.mouseup({
      preventDefault: function() {},
      clientX: x,
      clientY: y
    })
  }
  const clickButton = function(text) {
    if(buttons[text]) {
      if(buttons[text].d > 0.01) {
        // moving fast, don't click moving buttons
        return
      }
      let n = performance.now()
      if(n - buttons[text].s < 2000) {
        // at least 2000 ms until clickable
        return
      }
      clickAt(buttons[text].x, buttons[text].y)
      return true
    }
  }
  // hook event listeners
  HTMLElement.prototype._addEventListener = HTMLElement.prototype.addEventListener
  HTMLElement.prototype.addEventListener = new Proxy(HTMLElement.prototype.addEventListener, { apply:listenerApply })
  window.addEventListener = new Proxy(window.addEventListener, { apply:listenerApply })
  document.addEventListener = new Proxy(document.addEventListener, { apply:listenerApply })
  localStorage.florrio_tutorial = 'complete'
})();