Greasy Fork 还支持 简体中文。

florr.io hack

try to take over the florr.io!

  1. // ==UserScript==
  2. // @name florr.io hack
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description try to take over the florr.io!
  6. // @author m28
  7. // @match https://florr.io/*
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant none
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // some settings
  17. const client = {
  18. autoRespawn: {
  19. enabled: 0, // set to 1 to autoRespawn in spawnBiome
  20. spawnBiome: 'Hel'
  21. },
  22. bypassAfkCheck: {
  23. movementCheck: 1,
  24. afkButton: 1,
  25. },
  26. autoGrind: {
  27. enabled: 0 // set to 1 to enable autoGrind. also set autoRespawn.enabled to 1
  28. },
  29. tracers: 1, // draw tracers
  30. }
  31.  
  32. let respawnState = 0 // keep track of whether need to press Continue or Ready
  33. let lastCheck = 0 // 100 timer
  34. // use original functions, add prefix
  35. const _console = {
  36. _log: window.console.log,
  37. log: function() {
  38. this._log(`%c[FlorrScript]`, `color: rgb(30, 150, 30); background: rgb(215, 255, 205)`, ...arguments)
  39. }
  40. }
  41. // array multiplier for canvas untransform
  42. 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}
  43. // proxy identity function that does nothing
  44. let identity = function(a, b, c) {
  45. return Reflect.apply(a, b, c)
  46. }
  47. let beforeAnimationFrame = identity
  48. // keep track of tracers
  49. let tracers = {}, addTracer = function(t, color) {
  50. if(!color) { color = '#000000' } // use black if no color
  51. if(!tracers[color]) { tracers[color] = [] }
  52. tracers[color].push(t)
  53. }, mobs = []
  54. const parseColor = function(str) { // convert hex to [r, g, b]
  55. return [parseInt(str[1] + str[2], 16), parseInt(str[3] + str[4], 16), parseInt(str[5] + str[6], 16)]
  56. }
  57. // mouse position relative to screen center
  58. let mouse = {
  59. dx: 0,
  60. dy: 0
  61. }
  62. const main = function() {
  63. const canvas = document.getElementById('canvas')
  64. // record actual mouse position
  65. canvas._addEventListener('mousemove', function(e) {
  66. mouse.dx = (e.clientX - window.innerWidth * 0.5)
  67. mouse.dy = (e.clientY - window.innerHeight * 0.5)
  68. })
  69. const ctx = canvas.getContext('2d')
  70. beforeAnimationFrame = function(a, b, c) {
  71. // we want to run this right after the rendering function
  72. let n = performance.now()
  73. let w = canvas.width * 0.5, h = canvas.height * 0.5
  74. // converting between dom coordinates and canvas coordinates
  75. let ir = 1 / window.devicePixelRatio
  76. let dw = w * ir, dh = h * ir
  77. if(client.autoGrind.enabled) {
  78. // listeners.keydown({ keyCode:16, preventDefault:function() {} })
  79. let closestMob = false, closestDistance = -1
  80. // finding closest mob, doesn't include flowers
  81. for(let i=mobs.length-1;i>=0;i--) {
  82. let m = mobs[i]
  83. let dx = m[0] - w, dy = m[1] - h
  84. let d = dx * dx + dy * dy
  85. if(d < closestDistance || closestDistance < 0) {
  86. closestDistance = d
  87. closestMob = [dx, dy]
  88. }
  89. }
  90. if(closestMob) {
  91. let d = 100 * (closestDistance < 1 ? 1 : 1 / Math.sqrt(closestDistance))
  92. // move mouse to run toward mob
  93. mouse.dx = closestMob[0] * d
  94. mouse.dy = closestMob[1] * d
  95. }
  96. }
  97. // if Ready button not present reset state
  98. if(!buttons.Ready || !client.autoRespawn.enabled) { respawnState = 0 }
  99. if(n - lastCheck > 100) {
  100. // runs every 100 ms
  101. lastCheck = n
  102. !function() {
  103. if(client.autoRespawn.enabled) {
  104. if(respawnState < 1) {
  105. // change biome to specified biome
  106. if(buttons[client.autoRespawn.spawnBiome]) {
  107. if(clickButton(client.autoRespawn.spawnBiome)) {
  108. respawnState ++
  109. }
  110. return
  111. }
  112. } else {
  113. if(buttons.Ready) {
  114. // click Ready button
  115. clickButton('Ready')
  116. return
  117. }
  118. } if(buttons.Continue) {
  119. // died, click Continue button
  120. clickButton('Continue')
  121. // reset state to make sure correct biome
  122. respawnState = 0
  123. }
  124. }
  125. if(client.bypassAfkCheck.afkButton) {
  126. // not fully working
  127. clickButton(`I'm here`)
  128. }
  129. }()
  130. } else if(!buttons.Continue && (client.bypassAfkCheck.movementCheck || client.autoGrind.enabled)) {
  131. let a = (n / 1000) % (2 * Math.PI)
  132. let tx = mouse.dx + dw + Math.sin(a), ty = mouse.dy + dh + Math.cos(a)
  133. // bypass afk check
  134. listeners.mousemove({
  135. clientX: tx,
  136. screenX: tx,
  137. clientY: ty,
  138. screenY: ty
  139. })
  140. }
  141. let transform = ctx.getTransform()
  142. ctx.translate(w, h)
  143. ctx.lineCap = 'round'
  144. ctx.miterLimit = 1.68
  145. ctx.font = '14px Ubuntu'
  146. for(let color in tracers) {
  147. let o = tracers[color]
  148. for(let i=o.length-1;i>=0;i--) {
  149. let t = o[i]
  150. let l = 1, a = 1
  151. // lower rarities are transparent, higher rarities thicker lines
  152. if(t[2] > 3) {
  153. l += (t[2] - 3) * 0.5
  154. } else {
  155. a = (t[2] + 1) / 4
  156. }
  157. let j = a
  158. // draw dashed line at 50% opacity
  159. a *= 0.5
  160. let r = parseColor(color)
  161. ctx.strokeStyle = `rgba(${r[0]}, ${r[1]}, ${r[2]}, ${a})`
  162. ctx.setLineDash([10, 15])
  163. ctx.lineWidth = l
  164. ctx._beginPath()
  165. let dx = t[0] - w, dy = t[1] - h
  166. ctx._moveTo(dx, dy)
  167. let d = dx * dx + dy * dy
  168. ctx._lineTo(0, 0)
  169. ctx._stroke()
  170. // draw solid line at 25% opacity of dashed line
  171. a *= 0.25
  172. ctx.strokeStyle = `rgba(${r[0]}, ${r[1]}, ${r[2]}, ${a})`
  173. ctx.setLineDash([])
  174. ctx._stroke()
  175. ctx._closePath()
  176. if(d > 300 * 300) {
  177. // target distance over 300, draw number
  178. let rd = Math.sqrt(d)
  179. if(rd < 350) {
  180. // between 300 and 350 make it transparent
  181. j *= (rd - 300) * 0.02
  182. }
  183. if(j > 0.05) {
  184. // if under 5% don't draw
  185. d = 1 / rd
  186. let y = 300 + (rd - 300) / (rd - 100) * 100
  187. // calculate text position
  188. let tx = dx * d * y
  189. let ty = dy * d * y + 7
  190. // if rgb(0, 0, 0) use white instead
  191. if(r[0] === 0 && r[1] === 0 && r[2] === 0) { r[0] = r[1] = r[2] = 255 }
  192. ctx.fillStyle = `rgb(${r[0]}, ${r[1]}, ${r[2]})`
  193. ctx.strokeStyle = '#000000'
  194. ctx.lineWidth = 1.68
  195. // use j^2 for opacity to make gradient steeper
  196. j *= j
  197. // when over 95% use 100% for better performance
  198. if(j < 0.95) { ctx.globalAlpha = j }
  199. // number is how many 100's away target is
  200. let text = '' + Math.round(rd / 100)
  201. ctx.textAlign = 'center'
  202. // stroke then fill
  203. ctx._strokeText(text, tx, ty)
  204. ctx.lineWidth = 10
  205. ctx._fillText(text, tx, ty)
  206. if(j < 0.95) { ctx.globalAlpha = 1 }
  207. }
  208. }
  209. }
  210. }
  211. ctx.setTransform(transform)
  212. let f = c[0]
  213. if(f.proxy) { c[0] = f.proxy } else {
  214. c[0] = f.proxy = new Proxy(f, { apply:function(a, b, c) {
  215. // we want to run these right before the rendering function
  216. lbuttons = buttons
  217. buttons = {}
  218. tracers = {}
  219. mobs = []
  220. window.l = listeners
  221. window.b = buttons
  222. return Reflect.apply(a, b, c)
  223. } })
  224. }
  225. return Reflect.apply(a, b, c)
  226. }
  227. }
  228. // might be used in the future
  229. window.console.log = new Proxy(window.console.log, { apply:function(a, b, c) {
  230. return Reflect.apply(a, b, c)
  231. } })
  232. const untransform = function(x, y, t) {
  233. let r = multiply([[x, y, 1]], [[t.a, t.b, 0], [t.c, t.d, 0], [t.e, t.f, 1]])[0]
  234. return [r[0] / r[2], r[1] / r[2]]
  235. }
  236. let lastText = [], lastWhiteText = []
  237. // rarity data, useful for determining of some text is a valid rarity type
  238. let rarities = {
  239. Common: {
  240. name: 'Common',
  241. color: '#7eef6d',
  242. index: 0
  243. },
  244. Unusual: {
  245. name: 'Unusual',
  246. color: '#ffe65d',
  247. index: 1
  248. },
  249. Rare: {
  250. name: 'Rare',
  251. color: '#4d52e3',
  252. index: 2
  253. },
  254. Epic: {
  255. name: 'Epic',
  256. color: '#861fde',
  257. index: 3
  258. },
  259. Legendary: {
  260. name: 'Legendary',
  261. color: '#de1f1f',
  262. index: 4
  263. },
  264. Mythic: {
  265. name: 'Mythic',
  266. color: '#1fdbde',
  267. index: 5
  268. },
  269. Ultra: {
  270. name: 'Ultra',
  271. color: '#ff2b75',
  272. index: 6
  273. },
  274. Super: {
  275. name: 'Super',
  276. color: '#000000',
  277. index: 7
  278. }
  279. }
  280. let colors = {}
  281. // reversing table for better performance
  282. for(let r in rarities) {
  283. colors[rarities[r].color] = rarities[r]
  284. }
  285. // funny text modification callback
  286. const textTransform = function(text, ctx) {
  287. if(text === 'Plinko') {
  288. text = 'Scam Machine'
  289. }
  290. return text
  291. }
  292. let buttons = {}, lbuttons = {}
  293. // proxy measureText to keep results consistent with fillText and strokeText
  294. window.CanvasRenderingContext2D.prototype._measureText = window.CanvasRenderingContext2D.prototype.measureText
  295. window.CanvasRenderingContext2D.prototype.measureText = new Proxy(window.CanvasRenderingContext2D.prototype.measureText, { apply:function(a, b, c) {
  296. c[0] = textTransform(c[0], b)
  297. return Reflect.apply(a, b, c)
  298. } })
  299. // proxy strokeText to keep results consistent with fillText
  300. window.CanvasRenderingContext2D.prototype._strokeText = window.CanvasRenderingContext2D.prototype.strokeText
  301. window.CanvasRenderingContext2D.prototype.strokeText = new Proxy(window.CanvasRenderingContext2D.prototype.strokeText, { apply:function(a, b, c) {
  302. c[0] = textTransform(c[0], b)
  303. return Reflect.apply(a, b, c)
  304. } })
  305. // proxy fillText for tracer detection
  306. window.CanvasRenderingContext2D.prototype._fillText = window.CanvasRenderingContext2D.prototype.fillText
  307. window.CanvasRenderingContext2D.prototype.fillText = new Proxy(window.CanvasRenderingContext2D.prototype.fillText, { apply:function(a, b, c) {
  308. if(lastText[1]) {
  309. // something detected here
  310. if(colors[b.fillStyle] && b.globalAlpha >= 1 && lastPaths[0][3] && client.tracers) {
  311. let t = lastPaths[0]
  312. if(c[0].startsWith('Lvl ') && parseInt(c[0].slice(4)) >= 0) {
  313. // flower found. treat it like a mob with rarity determined by level
  314. t = untransform((t[0] + t[1]) * 0.5, t[2], t[3])
  315. // tracer is black
  316. addTracer([t[0], t[1], colors[b.fillStyle].index], '#000000')
  317. lastPaths = [[], [], []]
  318. } else if(rarities[c[0]]) {
  319. // mob found. also includes player summons like egg beetles
  320. t = untransform((t[0] + t[1]) * 0.5, t[2], t[3])
  321. // tracer color same as rarity
  322. addTracer([t[0], t[1], colors[b.fillStyle].index], b.fillStyle)
  323. mobs.push([t[0], t[1]])
  324. lastPaths = [[], [], []]
  325. }
  326. }
  327. }
  328. // this fillText might be a button
  329. let bd = buttonData[c[0]]
  330. if(bd && (!bd.color || bd.color === b.fillStyle) && (!bd.font || bd.font === b.font)) {
  331. // button found
  332. let t = untransform(c[1], c[2], b.getTransform())
  333. // if this button was existing last render
  334. let o = lbuttons[c[0]]
  335. let n = buttons[c[0]] = {
  336. x: t[0],
  337. y: t[1],
  338. d: 1, // we don't want to click buttons that are moving. only click when d < 0.01
  339. s: performance.now(), // we want to wait 2000 ms before we click any button
  340. fillStyle: b.fillStyle,
  341. font: b.font
  342. }
  343. if(o) {
  344. n.d = Math.abs(n.x - o.x) + Math.abs(n.y - o.y) // calculate speed
  345. n.s = o.s // this button alr exists so its creation time is lower
  346. }
  347. }
  348. if(c[0] === `I'm here` && client.bypassAfkCheck.afkButton) {
  349. afkButton(untransform(c[1], c[2], b.getTransform()))
  350. }
  351. // for referencing next fillText
  352. lastText = [c, b.fillStyle]
  353. if(b.fillStyle === '#ffffff') {
  354. lastWhiteText = [c, b.fillStyle]
  355. }
  356. c[0] = textTransform(c[0], b)
  357. return Reflect.apply(a, b, c)
  358. } })
  359. let clicking = false
  360. // I'm here button clicker
  361. const afkButton = function(t) {
  362. if(clicking) { return }
  363. clicking = true
  364. setTimeout(function() {
  365. clicking = false
  366. clickAt(t.x, t.y)
  367. }, 500 + 2000 * Math.random())
  368. }
  369. // buttons we want to look for
  370. let buttonData = {
  371. Ready: {
  372. color: '#ffffff',
  373. font: '27.5px Ubuntu'
  374. },
  375. Garden: {
  376. color: '#ffffff',
  377. font: '16px Ubuntu'
  378. },
  379. Desert: {
  380. color: '#ffffff',
  381. font: '16px Ubuntu'
  382. },
  383. Ocean: {
  384. color: '#ffffff',
  385. font: '16px Ubuntu'
  386. },
  387. Jungle: {
  388. color: '#ffffff',
  389. font: '16px Ubuntu'
  390. },
  391. Hel: {
  392. color: '#ffffff',
  393. font: '16px Ubuntu'
  394. },
  395. 'Play as guest': {},
  396. Continue: {
  397. color: '#ffffff',
  398. }
  399. }
  400. // keep track of paths, we can find health bars
  401. let lastPaths = [[], [], []], lastFill = ''
  402. let path = [], topPath = false, addSegment = function(s) {
  403. if(topPath) {
  404. topPath.push(s)
  405. } else {
  406. path.push(topPath = [s])
  407. }
  408. }
  409. // proxy beginPath
  410. window.CanvasRenderingContext2D.prototype._beginPath = window.CanvasRenderingContext2D.prototype.beginPath
  411. window.CanvasRenderingContext2D.prototype.beginPath = new Proxy(window.CanvasRenderingContext2D.prototype.beginPath, { apply:function(a, b, c) {
  412. path = []
  413. topPath = false
  414. // path restart
  415. return Reflect.apply(a, b, c)
  416. } })
  417. // proxy moveTo
  418. window.CanvasRenderingContext2D.prototype._moveTo = window.CanvasRenderingContext2D.prototype.moveTo
  419. window.CanvasRenderingContext2D.prototype.moveTo = new Proxy(window.CanvasRenderingContext2D.prototype.moveTo, { apply:function(a, b, c) {
  420. addSegment({
  421. type: 'moveTo',
  422. x: c[0],
  423. y: c[1]
  424. })
  425. return Reflect.apply(a, b, c)
  426. } })
  427. // proxy lineTo
  428. window.CanvasRenderingContext2D.prototype._lineTo = window.CanvasRenderingContext2D.prototype.lineTo
  429. window.CanvasRenderingContext2D.prototype.lineTo = new Proxy(window.CanvasRenderingContext2D.prototype.lineTo, { apply:function(a, b, c) {
  430. addSegment({
  431. type: 'lineTo',
  432. x: c[0],
  433. y: c[1]
  434. })
  435. return Reflect.apply(a, b, c)
  436. } })
  437. // proxy closePath
  438. window.CanvasRenderingContext2D.prototype._closePath = window.CanvasRenderingContext2D.prototype.closePath
  439. window.CanvasRenderingContext2D.prototype.closePath = new Proxy(window.CanvasRenderingContext2D.prototype.closePath, { apply:function(a, b, c) {
  440. path = []
  441. topPath = false
  442. // path destroyed
  443. return Reflect.apply(a, b, c)
  444. } })
  445. // proxy stroke
  446. window.CanvasRenderingContext2D.prototype._stroke = window.CanvasRenderingContext2D.prototype.stroke
  447. window.CanvasRenderingContext2D.prototype.stroke = new Proxy(window.CanvasRenderingContext2D.prototype.stroke, { apply:function(a, b, c) {
  448. // shift paths
  449. if(path.length === 1 && path[0].length === 2 && path[0][0].y === path[0][1].y) {
  450. lastPaths[0] = lastPaths[1]
  451. lastPaths[1] = lastPaths[2]
  452. lastPaths[2] = [path[0][0].x, path[0][1].x, path[0][0].y, b.getTransform()]
  453. }
  454. return Reflect.apply(a, b, c)
  455. } })
  456. // proxy fill
  457. window.CanvasRenderingContext2D.prototype._fill = window.CanvasRenderingContext2D.prototype.fill
  458. window.CanvasRenderingContext2D.prototype.fill = new Proxy(window.CanvasRenderingContext2D.prototype.fill, { apply:function(a, b, c) {
  459. lastFill = b.fillStyle
  460. return Reflect.apply(a, b, c)
  461. } })
  462. // proxy requestAnimationFrame for render hooks
  463. window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, { apply:function(a, b, c) {
  464. return beforeAnimationFrame(a, b, c)
  465. } })
  466. // wait for load
  467. const interval = setInterval(function() {
  468. if(document.body) {
  469. clearInterval(interval)
  470. main()
  471. }
  472. })
  473. // we can force an arraybuffer instantiation if want
  474. if(0) {
  475. window.WebAssembly.instantiateStreaming = new Proxy(window.WebAssembly.instantiateStreaming, { apply:function(a, b, c) {
  476. let d = new Response()
  477. c[0] = d
  478. return Reflect.apply(a, b, c)
  479. } })
  480. }
  481. const listeners = {}, trigger = function(type, data) {
  482. if(listeners[type]) { listeners[type](data) }
  483. }
  484. // universal hook for event listeners
  485. const listenerApply = function(a, b, c) {
  486. if(c[0] === 'mousemove') {
  487. listeners.mousemove = c[1]
  488. }
  489. if(c[0] === 'blur' || c[0] === 'focus' || c[0] === 'visibilitychange') {
  490. // makes it easier to afk and stuff
  491. return
  492. }
  493. if(b && b.id === 'canvas') {
  494. if(c[0] === 'mousedown') {
  495. listeners.mousedown = c[1]
  496. }
  497. }
  498. if(c[0] === 'mouseup') {
  499. listeners.mouseup = c[1]
  500. }
  501. if(c[0] === 'keydown') {
  502. listeners.keydown = c[1]
  503. }
  504. if(c[0] === 'keyup') {
  505. listeners.keyup = c[1]
  506. }
  507. return Reflect.apply(a, b, c)
  508. }
  509. const clickAt = function(x, y) {
  510. let ir = 1 / window.devicePixelRatio
  511. x *= ir
  512. y *= ir
  513. // move mouse
  514. listeners.mousemove({
  515. clientX: x,
  516. screenX: x,
  517. clientY: y,
  518. screenY: y
  519. })
  520. // mouse down
  521. listeners.mousedown({
  522. preventDefault: function() {},
  523. clientX: x,
  524. clientY: y
  525. })
  526. // mouse up
  527. listeners.mouseup({
  528. preventDefault: function() {},
  529. clientX: x,
  530. clientY: y
  531. })
  532. }
  533. const clickButton = function(text) {
  534. if(buttons[text]) {
  535. if(buttons[text].d > 0.01) {
  536. // moving fast, don't click moving buttons
  537. return
  538. }
  539. let n = performance.now()
  540. if(n - buttons[text].s < 2000) {
  541. // at least 2000 ms until clickable
  542. return
  543. }
  544. clickAt(buttons[text].x, buttons[text].y)
  545. return true
  546. }
  547. }
  548. // hook event listeners
  549. HTMLElement.prototype._addEventListener = HTMLElement.prototype.addEventListener
  550. HTMLElement.prototype.addEventListener = new Proxy(HTMLElement.prototype.addEventListener, { apply:listenerApply })
  551. window.addEventListener = new Proxy(window.addEventListener, { apply:listenerApply })
  552. document.addEventListener = new Proxy(document.addEventListener, { apply:listenerApply })
  553. localStorage.florrio_tutorial = 'complete'
  554. })();