game.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. (() => {
  2. /**
  3. * Animation
  4. */
  5. class Animation {
  6. static ANIMATION_STEPS = 10;
  7. static animate(config) {
  8. const steps = config.steps ? config.steps : Animation.ANIMATION_STEPS;
  9. const delay = config.delay ? config.delay : 25;
  10. const len = config.end - config.start;
  11. const stepSize = len / steps;
  12. config.step(config.start);
  13. let currentStep = 1;
  14. let ti = timer.createTimer(0, delay, () => {
  15. try {
  16. config.step(config.start + stepSize * currentStep);
  17. } catch (e) {}
  18. if (currentStep >= steps) {
  19. timer.stopTimer(ti);
  20. if (config.finish) config.finish();
  21. }
  22. currentStep++;
  23. });
  24. }
  25. }
  26. class Vibro {
  27. static vibrate = hmSensor.createSensor(hmSensor.id.VIBRATE);
  28. static timer = false;
  29. static initComplete = false;
  30. static timerStep = 25;
  31. static firstRun() {
  32. hmUI.createWidget(hmUI.widget.WIDGET_DELEGATE, {
  33. pause_call: () => Vibro.cancel(),
  34. });
  35. Vibro.initComplete = true;
  36. }
  37. static _prepBase(tfn) {
  38. if (!Vibro.initComplete) {
  39. Vibro.firstRun();
  40. }
  41. Vibro.cancel();
  42. Vibro.vibrate.motorenable = 1;
  43. Vibro.vibrate.crowneffecton = 1;
  44. Vibro.vibrate.scene = 1;
  45. tfn();
  46. Vibro.timer = timer.createTimer(0, Vibro.timerStep, tfn);
  47. }
  48. static run(length, delay = 0, count = 1, callback = false) {
  49. let counter = 0,
  50. time = 0;
  51. Vibro._prepBase(() => {
  52. if (counter >= count) {
  53. Vibro.cancel();
  54. if (callback !== false) callback();
  55. return;
  56. }
  57. time += Vibro.timerStep;
  58. if (time <= length) {
  59. Vibro.vibrate.start();
  60. } else if (time <= length + delay) {
  61. Vibro.vibrate.stop();
  62. } else {
  63. counter += 1;
  64. time = 0;
  65. if (counter < count) Vibro.vibrate.start();
  66. }
  67. });
  68. }
  69. static melody(array, callback = false) {
  70. let index = 0,
  71. time = 0;
  72. Vibro._prepBase(() => {
  73. if (index >= array.length) {
  74. Vibro.cancel();
  75. if (callback !== false) callback();
  76. return;
  77. }
  78. if (time < array[index]) {
  79. time += Vibro.timerStep;
  80. return;
  81. } else {
  82. time = 0;
  83. index += 1;
  84. if (index < array.length)
  85. index % 2 == 0 ? Vibro.vibrate.start() : Vibro.vibrate.stop();
  86. }
  87. });
  88. }
  89. static cancel() {
  90. if (Vibro.timer !== false) {
  91. timer.stopTimer(Vibro.timer);
  92. Vibro.timer = false;
  93. }
  94. Vibro.vibrate.stop();
  95. }
  96. }
  97. /**
  98. * Электроника ИМ-02
  99. */
  100. class ElIM02 {
  101. view_score = null;
  102. seg = {}; // segments
  103. eggs = [];
  104. topOffset = 160;
  105. eggTopY = 258;
  106. eggBottomY = 312;
  107. ALPHA_VISIBLE = 192;
  108. ALPHA_BG = 32;
  109. gameState = {};
  110. started = false;
  111. gameTimer = null;
  112. animTimer = null;
  113. segmentData = {
  114. player_tr: { x: 101, y: 111 },
  115. player_right: { x: 56, y: 105 },
  116. player_br: { x: 98, y: 158 },
  117. player_tl: { x: 50, y: 105 },
  118. player_left: { x: 84, y: 107 },
  119. player_bl: { x: 44, y: 156 },
  120. bonus0: { x: 17, y: 25 },
  121. bonus1: { x: 17, y: 25 },
  122. fail_left: { x: 24, y: 212, src: "fail_anim.png" },
  123. fail_right: { x: 117, y: 212, src: "fail_anim.png" },
  124. fail_1: { x: 4, y: -1, src: "fail.png" },
  125. fail_2: { x: 34, y: -1, src: "fail.png" },
  126. fail_3: { x: 64, y: -1, src: "fail.png" },
  127. gameover: { x: 53, y: 151 },
  128. };
  129. constructor(bgColor) {
  130. this.bgColor = bgColor;
  131. }
  132. fetchResults() {
  133. const current = [];
  134. for (let i = 0; i < 5; i++) {
  135. current[i] = hmFS.SysProGetInt("el02_top" + i);
  136. if (!current[i]) current[i] = i == 4 ? 0 : (4 - i) * 100;
  137. }
  138. return current;
  139. }
  140. addResult(v) {
  141. const current = this.fetchResults();
  142. if (v > current[current.length-1])
  143. current[current.length-1] = v;
  144. current.sort();
  145. current.reverse();
  146. for (let i = 0; i < current.length; i++) {
  147. hmFS.SysProSetInt("el02_top" + i, current[i]);
  148. }
  149. }
  150. initGameState() {
  151. this.gameState = {
  152. eggs: [-1, -1, -1, -1],
  153. fails: 0,
  154. score: 0,
  155. posY: 0,
  156. posX: 0,
  157. bonus: false,
  158. gameover: false,
  159. tick: -1,
  160. };
  161. this.refreshEggs();
  162. this.setPosition(0, 0);
  163. }
  164. start() {
  165. if (this.started) return;
  166. this.initGameUI();
  167. this.restart();
  168. }
  169. restart() {
  170. if (this.started) return;
  171. this.initGameState();
  172. Vibro.run(50);
  173. this.modify(this.seg.gameover, false, true);
  174. this.playGuide(() => {
  175. this.gameTimer = timer.createTimer(50, 200, () => this.gameTick());
  176. this.animTimer = timer.createTimer(50, 350, () => this.animTick());
  177. });
  178. this.started = true;
  179. }
  180. kill() {
  181. if (!this.started) return;
  182. timer.stopTimer(this.gameTimer);
  183. timer.stopTimer(this.animTimer);
  184. this.started = false;
  185. }
  186. playGuide(callback) {
  187. if (this.autoPlay) return callback();
  188. let fg = hmUI.createWidget(hmUI.widget.IMG, {
  189. x: 2,
  190. y: 239,
  191. src: "guide.png",
  192. });
  193. Animation.animate({
  194. start: 0,
  195. end: 5,
  196. steps: 5,
  197. delay: 400,
  198. step: (v) => {
  199. fg.setProperty(hmUI.prop.MORE, {
  200. alpha: v % 2 == 1 ? 180 : 0,
  201. });
  202. },
  203. finish: () => {
  204. hmUI.deleteWidget(fg);
  205. callback();
  206. },
  207. });
  208. }
  209. gameover() {
  210. this.gameState.gameover = true;
  211. this.gameState.bonus = false;
  212. this.addResult(this.gameState.score);
  213. const hidden = ["player_right", "player_tr", "player_br"];
  214. for (var key in this.seg) {
  215. this.modify(this.seg[key], hidden.indexOf(key) < 0, false);
  216. }
  217. Vibro.melody([250, 25, 250, 25, 250, 25, 250]);
  218. const ti = timer.createTimer(2500, 2500, () => {
  219. hmApp.goBack();
  220. console.log("back");
  221. timer.stopTimer(ti);
  222. });
  223. }
  224. gameTick() {
  225. if (this.autoPlay && this.gameState.fails >= 3) {
  226. this.gameState.score = 0;
  227. this.gameState.fails = 0;
  228. this.view_score.setProperty(
  229. hmUI.prop.TEXT,
  230. this.gameState.score.toString()
  231. );
  232. }
  233. if (this.gameState.fails >= 3) {
  234. if (!this.gameState.gameover) this.gameover();
  235. return;
  236. }
  237. // Game speed
  238. const speed = Math.max(1, 3 - Math.floor(this.gameState.score / 500));
  239. this.gameState.tick += 1;
  240. if (this.gameState.tick % speed != 0) return;
  241. this.modify(this.seg.fail_left, true, false);
  242. this.modify(this.seg.fail_right, true, false);
  243. let cur = this.gameState.eggs;
  244. let free = [];
  245. for (let i = 0; i < 4; i++) {
  246. // If == 3, check catch state
  247. if (cur[i] == 3) this.catchEgg(i);
  248. // Move
  249. if (cur[i] > -1) {
  250. cur[i] += 1;
  251. } else free.push(i);
  252. }
  253. // Spawn new egg (5-35% rate)
  254. const l = free.length;
  255. const th = 0.05 + (this.gameState.score / 1000) * 0.3;
  256. if ((Math.random() > 1 - th && l > 1) || free.length == 4) {
  257. const fi = Math.floor(Math.random() * l);
  258. const i = free[fi];
  259. cur[i] = 0;
  260. }
  261. // Bonus? (4% rate)
  262. if (!this.gameState.bonus && Math.random() > 0.96) {
  263. this.gameState.bonus = Date.now();
  264. } else if (Date.now() - this.gameState.bonus > 5000) {
  265. this.gameState.bonus = 0;
  266. }
  267. // Update UI
  268. this.refreshEggs();
  269. // AutoPlay
  270. if (this.autoPlay) {
  271. let topVal = 0,
  272. topIndex = -1;
  273. for (let i = 0; i < 4; i++) {
  274. if (this.gameState.eggs[i] > topVal) {
  275. topVal = this.gameState.eggs[i];
  276. topIndex = i;
  277. }
  278. }
  279. if (topIndex > -1 && Math.random() < (speed == 3 ? 0.75 : 0.5)) {
  280. this.setPosition(topIndex > 1 ? 1 : 0, topIndex % 2);
  281. }
  282. }
  283. }
  284. animTick() {
  285. const val = this.gameState.fails;
  286. const bonus = this.gameState.bonus;
  287. const gameover = this.gameState.gameover;
  288. this.modify(this.seg.fail_1, true, val >= 1 || (val == 0.5 && this.tick));
  289. this.modify(this.seg.fail_2, true, val >= 2 || (val == 1.5 && this.tick));
  290. this.modify(this.seg.fail_3, true, val >= 3 || (val == 2.5 && this.tick));
  291. this.modify(this.seg.bonus0, true, bonus && this.tick);
  292. this.modify(this.seg.bonus1, true, bonus && !this.tick);
  293. this.modify(this.seg.gameover, gameover, gameover && this.tick);
  294. this.tick = !this.tick;
  295. }
  296. catchEgg(i) {
  297. const validX = this.gameState.posX == (i > 1 ? 1 : 0);
  298. const validY = this.gameState.posY == i % 2;
  299. if (validY && validX) {
  300. this.gameState.score += 25;
  301. this.view_score.setProperty(
  302. hmUI.prop.TEXT,
  303. this.gameState.score.toString()
  304. );
  305. if (!this.autoPlay) Vibro.run(25);
  306. } else {
  307. const v = this.gameState.fails;
  308. const bonus = this.gameState.bonus;
  309. this.gameState.fails = bonus ? v + 0.5 : Math.floor(v + 1);
  310. const an = i > 1 ? this.seg.fail_right : this.seg.fail_left;
  311. this.modify(an, true, true);
  312. if (!this.autoPlay) Vibro.run(200);
  313. }
  314. this.gameState.eggs[i] = -1;
  315. }
  316. initGameUI() {
  317. let currentBgColor = hmFS.SysProGetInt("im02_bg");
  318. if (!currentBgColor) currentBgColor = 0;
  319. hmUI.createWidget(hmUI.widget.FILL_RECT, {
  320. x: 0,
  321. y: 0,
  322. w: 192,
  323. h: 490,
  324. color: this.bgColor,
  325. });
  326. hmUI.createWidget(hmUI.widget.IMG, {
  327. x: 0,
  328. y: this.topOffset,
  329. src: "game_bg.png",
  330. });
  331. // Draw all segments
  332. for (let key in this.segmentData) {
  333. this.seg[key] = hmUI.createWidget(hmUI.widget.IMG, {
  334. src: "" + key + ".png",
  335. alpha: 20,
  336. ...this.segmentData[key],
  337. y: this.topOffset + this.segmentData[key].y,
  338. });
  339. }
  340. for (let i = 0; i < 4; i++) {
  341. this.eggs[i] = hmUI.createWidget(hmUI.widget.IMG, {
  342. x: -20,
  343. y: -20,
  344. src: "egg.png",
  345. });
  346. }
  347. // Score
  348. this.view_score = hmUI.createWidget(hmUI.widget.TEXT_IMG, {
  349. x: 121,
  350. y: 159,
  351. text: "0",
  352. alpha: this.ALPHA_VISIBLE,
  353. font_array: [...Array(10).keys()].map((i) => `font/${i}.png`),
  354. });
  355. // Controls
  356. if (this.autoPlay) return;
  357. hmUI
  358. .createWidget(hmUI.widget.IMG, {
  359. x: 0,
  360. y: 0,
  361. w: 96,
  362. h: 320,
  363. src: "",
  364. })
  365. .addEventListener(hmUI.event.CLICK_UP, () => {
  366. this.setPosition(0, 0);
  367. });
  368. hmUI
  369. .createWidget(hmUI.widget.IMG, {
  370. x: 96,
  371. y: 0,
  372. w: 96,
  373. h: 320,
  374. src: "",
  375. })
  376. .addEventListener(hmUI.event.CLICK_UP, () => {
  377. this.setPosition(1, 0);
  378. });
  379. hmUI
  380. .createWidget(hmUI.widget.IMG, {
  381. x: 0,
  382. y: 320,
  383. w: 96,
  384. h: 170,
  385. src: "",
  386. })
  387. .addEventListener(hmUI.event.CLICK_UP, () => {
  388. this.setPosition(0, 1);
  389. });
  390. hmUI
  391. .createWidget(hmUI.widget.IMG, {
  392. x: 96,
  393. y: 320,
  394. w: 96,
  395. h: 170,
  396. src: "",
  397. })
  398. .addEventListener(hmUI.event.CLICK_UP, () => {
  399. this.setPosition(1, 1);
  400. });
  401. }
  402. dropEgg(slot) {
  403. this.gameState.eggs[slot] = 0;
  404. this.refreshEggs();
  405. }
  406. refreshEggs() {
  407. for (let i = 0; i < 4; i++) {
  408. const val = this.gameState.eggs[i];
  409. let baseX = 8 + 10 * val;
  410. let baseY = (i % 2 ? this.eggBottomY : this.eggTopY) + val * 6;
  411. if (i > 1) baseX = 192 - baseX;
  412. if (val < 0) baseX = -20;
  413. this.eggs[i].setProperty(hmUI.prop.MORE, {
  414. x: baseX,
  415. y: baseY,
  416. });
  417. }
  418. }
  419. setPosition(posX, posY) {
  420. if (this.gameState.gameover) return;
  421. this.gameState.posX = posX;
  422. this.gameState.posY = posY;
  423. this.modify(this.seg.player_left, posX == 0, posX == 0);
  424. this.modify(this.seg.player_right, posX == 1, posX == 1);
  425. this.modify(this.seg.player_tl, posX == 0, posY == 0);
  426. this.modify(this.seg.player_tr, posX == 1, posY == 0);
  427. this.modify(this.seg.player_bl, posX == 0, posY == 1);
  428. this.modify(this.seg.player_br, posX == 1, posY == 1);
  429. }
  430. modify(w, visible, fullVisible) {
  431. w.setProperty(hmUI.prop.VISIBLE, visible);
  432. if (!visible) return;
  433. w.setProperty(hmUI.prop.MORE, {
  434. alpha: fullVisible ? this.ALPHA_VISIBLE : this.ALPHA_BG,
  435. });
  436. }
  437. }
  438. let __$$app$$__ = __$$hmAppManager$$__.currentApp;
  439. let __$$module$$__ = __$$app$$__.current;
  440. __$$module$$__.module = DeviceRuntimeCore.Page({
  441. onInit(bg) {
  442. hmSetting.setBrightScreen(360);
  443. hmUI.setLayerScrolling(false);
  444. new ElIM02(bg).start();
  445. },
  446. onDestroy() {
  447. Vibro.cancel();
  448. hmSetting.setBrightScreenCancel();
  449. }
  450. });
  451. })();