element miner

2022/4/11 20:00:18

  1. // ==UserScript==
  2. // @name element miner
  3. // @namespace Violentmonkey Scripts
  4. // @match *
  5. // @include *
  6. // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js
  7. // @grant none
  8. // @version 1.0
  9. // @author ec50n9
  10. // @description 2022/4/11 20:00:18
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. this.$ = this.jQuery = jQuery.noConflict(true);
  15.  
  16. // 道具
  17. function Prop(crane, type = 'bomb') {
  18. this.crane = crane;
  19. this.countDown = 200;
  20. this.size = 32;
  21. this.deg = 0;
  22. this.color = 'red';
  23. this.zIndex = 0;
  24. this.WAGGLE_DEG = 15;
  25. this.SCORES = {
  26. bomb: -100,
  27. flower: 50,
  28. diamond: 100
  29. }
  30. this.TYPES = {
  31. bomb: '💣',
  32. flower: '🌹',
  33. diamond: '💎'
  34. };
  35. this.BOOM_TEXTS = {
  36. bomb: '💥BOOM!!!',
  37. flower: '❤️HEY!!!',
  38. diamond: '💰HOOO!!!!'
  39. }
  40. this.type = (type in this.TYPES) ? type : 'bomb';
  41. this.score = this.SCORES[this.type];
  42. this.text = this.TYPES[this.type];
  43. this.boomText = this.BOOM_TEXTS[this.type];
  44. this.element = $(`<div>${this.TYPES[this.type]}</div>`).css({
  45. 'position': 'fixed',
  46. 'left': `${crane.captured.element.offset().left + crane.captured.element.width() / 2}px`,
  47. 'top': `${crane.captured.element.offset().top + crane.captured.element.height() / 2}px`,
  48. 'white-space': 'nowrap',
  49. 'color': this.color,
  50. 'font-size': `${this.size}px`,
  51. 'transform': `translate(-50%, -50%) rotate(${this.deg}deg)`,
  52. 'z-index': this.zIndex
  53. });
  54. }
  55. Prop.prototype.appendTo = function (target) {
  56. target.prepend(this.element);
  57. }
  58.  
  59. // 抓手
  60. function Crane(left = 0, top = 0) {
  61. // 得分
  62. this.score = 0;
  63. this.porpList = [];
  64. // 位置
  65. this.position = {
  66. left,
  67. top
  68. };
  69. // 角度
  70. this.deg = -180;
  71. // 绳子
  72. this.rope = {
  73. width: 10,
  74. length: 50,
  75. step: 2,
  76. DEFAULT_STEP: 2,
  77. DEFAULT_LEN: 50,
  78. MIN_LEN: 0,
  79. MAX_LEN: 1000
  80. };
  81. // 钩子
  82. this.hock = {
  83. src: 'https://s3.bmp.ovh/imgs/2022/04/12/1c986b60d886b9dd.png',
  84. src_default: 'https://s3.bmp.ovh/imgs/2022/04/12/1c986b60d886b9dd.png',
  85. src_capture: 'https://s3.bmp.ovh/imgs/2022/04/12/fd3fdb4cf18eb9f2.png'
  86. }
  87. // 当前状态
  88. this.cur_status = 0;
  89. this.STATUS = {
  90. static: 0,
  91. elongating: 1,
  92. reducing: 2,
  93. inRecovery: 3
  94. };
  95. // 被捕获
  96. this.captured = {
  97. element: null,
  98. dis_left: 0,
  99. dis_top: 0
  100. };
  101. // 操作台
  102. this.ele_console = $('<div></div>').css({
  103. 'position': 'fixed',
  104. 'left': `${this.position.left}px`,
  105. 'top': `${this.position.top}px`,
  106. // 'transform': 'translate(-50%, -50%)',
  107. 'width': '10em',
  108. 'height': '10em',
  109. 'background-image': 'url(https://s3.bmp.ovh/imgs/2022/04/13/ebbf40101c3c1038.png)',
  110. 'background-size': '100% auto',
  111. 'overflow': 'visible',
  112. 'z-index': '997'
  113. });
  114. // 操作台图片
  115. this.ele_console_img = $('<img src="https://s3.bmp.ovh/imgs/2022/04/13/3cf047c6fc4f1a33.png"/>').css({
  116. position: 'absolute',
  117. bottom: '0',
  118. left: '20%',
  119. width: '60%',
  120. });
  121. this.ele_console.append(this.ele_console_img);
  122. // 抓手容器
  123. this.ele_hock_container = $('<div></div>').css({
  124. 'margin-top': '90%',
  125. 'transform-origin': 'top center',
  126. 'transform': `rotate(${this.deg}deg)`,
  127. 'z-index': '999'
  128. });
  129. this.ele_console.append(this.ele_hock_container);
  130. // 分数
  131. this.ele_score = $(`<div>${this.score}</div>`).css({
  132. 'position': 'absolute',
  133. 'top': '-1.5em',
  134. 'font-size': '1em',
  135. 'color': 'green',
  136. 'white-space': 'nowrap'
  137. });
  138. this.ele_console.prepend(this.ele_score);
  139. // 绳子
  140. this.ele_rope = $('<div></div>').css({
  141. 'width': `${this.rope.width}px`,
  142. 'height': `${this.rope.length}px`,
  143. 'margin': '0 auto',
  144. 'background-image': 'url(https://s3.bmp.ovh/imgs/2022/04/13/65f0a51cebe9be16.png)',
  145. 'background-size': '100% auto',
  146. 'background-repeat': 'no-repeat repeat'
  147. });
  148. this.ele_hock_container.append(this.ele_rope);
  149. // 抓手
  150. this.ele_hock = $(`<div></div>`).css({
  151. 'width': '4em',
  152. 'height': '4em',
  153. 'margin': '0 auto',
  154. 'margin-top': '-4px'
  155. });
  156. this.ele_hock_img = $(`<img style="width:inherit; height:inherit; object-fit:contain;"
  157. src="${this.hock.src}">`);
  158. this.ele_hock.append(this.ele_hock_img);
  159. this.ele_hock_container.append(this.ele_hock);
  160. // 点击事件
  161. let that = this;
  162. this.ele_console.click(function () {
  163. if (that.status == that.STATUS.elongating) {
  164. that.catch();
  165. } else {
  166. that.elongate();
  167. }
  168. });
  169. }
  170.  
  171. // 添加到
  172. Crane.prototype.appendTo = function (target) {
  173. target.append(this.ele_console);
  174. }
  175. // 增加角度
  176. Crane.prototype.addDeg = function (increment) {
  177. this.deg += increment;
  178. if (this.deg == 360) {
  179. this.deg = 0;
  180. } else if (this.deg > 360) {
  181. this.deg %= 360;
  182. } else if (this.deg < 0) {
  183. this.deg = 360 + this.deg % 360;
  184. }
  185. }
  186. // 设置分数
  187. Crane.prototype.updateScore = function (increment) {
  188. let newScore = this.score + increment;
  189. if (newScore < 0) {
  190. this.score = 0;
  191. } else {
  192. this.score = newScore;
  193. }
  194. }
  195. // 静态
  196. Crane.prototype.static = function () {
  197. this.status = this.STATUS.static;
  198. }
  199. // 伸长
  200. Crane.prototype.elongate = function () {
  201. this.status = this.STATUS.elongating;
  202. }
  203. // 恢复
  204. Crane.prototype.recovery = function () {
  205. this.status = this.STATUS.inRecovery;
  206.  
  207. // 移除元素
  208. if (this.captured.element) {
  209. this.updateScore(10);
  210. this.captured.element.remove();
  211. this.captured.element = null;
  212. }
  213.  
  214. // 恢复绳子伸长的步长
  215. this.rope.step = this.rope.DEFAULT_STEP;
  216. }
  217. // 捕捉
  218. Crane.prototype.catch = function () {
  219. this.status = this.STATUS.reducing;
  220.  
  221. let scrollLeft = $(document).scrollLeft();
  222. let scrollTop = $(document).scrollTop();
  223.  
  224. let x = this.ele_hock.offset().left - scrollLeft + this.ele_hock.width() / 2,
  225. y = this.ele_hock.offset().top - scrollTop + this.ele_hock.height() / 2;
  226.  
  227. let elements = document.elementsFromPoint(x, y);
  228. if (elements.length > 3) {
  229. this.captured.element = $(elements[3]);
  230. let tagName = this.captured.element[0].tagName.toLowerCase();
  231. // 忽略html元素
  232. if (tagName == 'html' || tagName == 'body') {
  233. this.captured.element = null;
  234. return;
  235. }
  236. // 计算上和左的距离
  237. this.captured.dis_left = this.ele_hock.offset().left - this.captured.element.offset().left + scrollLeft;
  238. this.captured.dis_top = this.ele_hock.offset().top - this.captured.element.offset().top + scrollTop;
  239. // 计算尺寸
  240. let size = this.captured.element.width() + this.captured.element.height();
  241. // this.rope.step = 1;
  242. // 初始化元素
  243. this.captured.element.css({
  244. 'position': 'fixed',
  245. 'left': `${this.captured.element.offset().left- $(document).scrollLeft()}px`,
  246. 'top': `${this.captured.element.offset().top- $(document).scrollTop()}px`,
  247. 'width': `${this.captured.element.width()}px`,
  248. 'height': `${this.captured.element.height()}px`,
  249. 'z-index': '995'
  250. });
  251. // 炸弹
  252. if (tagName == 'img') {
  253. let random = Math.random();
  254. let porp;
  255. if (random > .7) {
  256. porp = new Prop(this, 'bomb');
  257. } else if (random > .5) {
  258. porp = new Prop(this, 'flower');
  259. } else if (random > .3) {
  260. porp = new Prop(this, 'diamond');
  261. } else {
  262. porp = new Prop(this, 'bomb');
  263. }
  264. if (porp) {
  265. porp.appendTo(this.captured.element.parent());
  266. }
  267. this.porpList.push(porp);
  268. }
  269. }
  270. }
  271. // 更新
  272. Crane.prototype.update = function () {
  273. // 爪子
  274. if (this.status == this.STATUS.elongating) {
  275. // 伸长
  276. if (this.rope.length < this.rope.MAX_LEN) {
  277. this.rope.length += this.rope.step;
  278. } else {
  279. this.catch();
  280. }
  281. } else if (this.status == this.STATUS.reducing) {
  282. // 收缩
  283. if (this.rope.length > this.rope.MIN_LEN) {
  284. this.rope.length -= this.rope.step;
  285. } else {
  286. this.recovery();
  287. }
  288. } else if (this.status == this.STATUS.inRecovery) {
  289. // 恢复
  290. if (this.rope.length > this.rope.DEFAULT_LEN) {
  291. this.rope.length -= 1;
  292. } else if (this.rope.length < this.rope.DEFAULT_LEN) {
  293. this.rope.length += 1;
  294. } else {
  295. this.static();
  296. }
  297. } else {
  298. this.addDeg(.5);
  299. }
  300.  
  301. // 抓手状态
  302. if (this.captured.element) {
  303. // 抓着东西
  304. this.hock.src = this.hock.src_capture;
  305. } else {
  306. // 没有东西
  307. this.hock.src = this.hock.src_default;
  308. }
  309.  
  310. // 道具
  311. for (let i in this.porpList) {
  312. let porp = this.porpList[i];
  313. if (porp.countDown > 0) {
  314. porp.countDown -= 1;
  315. } else if (porp.countDown > -64) {
  316. porp.size += 1;
  317. porp.countDown -= 1;
  318. } else if (porp.countDown == -64) {
  319. porp.deg = porp.WAGGLE_DEG;
  320. porp.text = porp.boomText;
  321. porp.zIndex = 999;
  322. porp.countDown -= 1;
  323. porp.crane.updateScore(porp.score);
  324. } else if (porp.countDown > -96) {
  325. if (porp.countDown % 8 == 0) {
  326. porp.deg = -porp.deg;
  327. }
  328. porp.countDown -= 1;
  329. } else {
  330. porp.element.remove();
  331. this.porpList.splice(i, 1);
  332. }
  333. }
  334. }
  335. // 绘制
  336. Crane.prototype.draw = function () {
  337. // 分数
  338. this.ele_score.text('分数:' + this.score);
  339. // 旋转角度和长度
  340. this.ele_hock_container.css('transform', `rotate(${this.deg}deg)`);
  341. this.ele_rope.css({
  342. 'width': `${this.rope.width}px`,
  343. 'height': `${this.rope.length}px`
  344. });
  345. // 钩子图片
  346. this.ele_hock_img.attr('src', this.hock.src);
  347. // 拖动元素
  348. if (this.captured.element) {
  349. this.captured.element.css({
  350. 'left': `${this.ele_hock.offset().left - this.captured.dis_left}px`,
  351. 'top': `${this.ele_hock.offset().top - this.captured.dis_top}px`
  352. });
  353. }
  354. // 道具
  355. for (let porp of this.porpList) {
  356. porp.element.text(porp.text).css({
  357. 'transform': `translate(-50%, -50%) rotate(${porp.deg}deg)`,
  358. 'color': porp.color,
  359. 'font-size': `${porp.size}px`,
  360. 'z-index': porp.zIndex
  361. });
  362. }
  363. }
  364.  
  365. // 休眠
  366. async function sleep(delay) { return new Promise((resolve) => setTimeout(resolve, delay)); }
  367. // 游戏循环
  368. async function game_loop(cranes) {
  369. while (true) {
  370. for (let crane of cranes) {
  371. crane.update();
  372. crane.draw();
  373. }
  374. await sleep(10);
  375. }
  376. }
  377.  
  378. $(function () {
  379. console.log('开始矿工之旅!!!');
  380. let crane = new Crane(400, 100);
  381. crane.appendTo($('body'));
  382. let cranes = [crane];
  383.  
  384. game_loop(cranes);
  385. });