- // ==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'
- })();