ElIM02.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /**
  2. * Электроника ИМ-02
  3. */
  4. class ElIM02 {
  5. static colorVariants = [
  6. 0xc8ffdb,
  7. 0x6da175,
  8. 0xc4b984,
  9. 0xc484a2,
  10. 0xbbbbbb,
  11. 0xfbffd9
  12. ];
  13. view_score = null;
  14. seg = {}; // segments
  15. eggs = [];
  16. topOffset = 160;
  17. gameState = {};
  18. started = false;
  19. eggTopY = 258;
  20. eggBottomY = 312;
  21. ticker = false;
  22. autoPlay = false;
  23. bgWidget = null;
  24. gameTimer = null;
  25. animTimer = null;
  26. ALPHA_VISIBLE = 192;
  27. ALPHA_BG = 32;
  28. segmentData = {
  29. player_tr: {x: 101, y: 111},
  30. player_right: {x: 56, y: 105},
  31. player_br: {x: 98, y: 158},
  32. player_tl: {x: 50, y: 105},
  33. player_left: {x: 84, y: 107},
  34. player_bl: {x: 44, y: 156},
  35. bonus0: {x: 17, y: 25},
  36. bonus1: {x: 17, y: 25},
  37. fail_left: {x: 24, y: 212, src: "app_im02/fail_anim.png"},
  38. fail_right: {x: 117, y: 212, src: "app_im02/fail_anim.png"},
  39. fail_1: {x: 4, y: -1, src: "app_im02/fail.png"},
  40. fail_2: {x: 34, y: -1, src: "app_im02/fail.png"},
  41. fail_3: {x: 64, y: -1, src: "app_im02/fail.png"},
  42. gameover: {x: 53, y: 151}
  43. }
  44. static im02Entrypoint() {
  45. (new ElIM02()).start();
  46. }
  47. static entry = {
  48. icon: "app_im02/icon.png",
  49. start: () => Tk.run(ElIM02TopScore.im02TopScore)
  50. }
  51. initGameState() {
  52. this.gameState = {
  53. eggs: [-1, -1, -1, -1],
  54. fails: 0,
  55. score: 0,
  56. posY: 0,
  57. posX: 0,
  58. bonus: false,
  59. gameover: false,
  60. tick: -1
  61. };
  62. this.refreshEggs();
  63. this.setPosition(0, 0);
  64. }
  65. start() {
  66. if(this.started) return;
  67. if(!this.autoPlay) {
  68. hmSetting.setBrightScreen(180);
  69. }
  70. this.initGameUI();
  71. this.restart();
  72. }
  73. restart() {
  74. if(this.started) return;
  75. this.initGameState();
  76. if(!this.autoPlay)
  77. Vibro.run(50);
  78. this.modify(this.seg.gameover, false, true);
  79. this.playGuide(() => {
  80. this.gameTimer = Tk.createTimer(50, 200, () => this.gameTick());
  81. this.animTimer = Tk.createTimer(50, 350, () => this.animTick());
  82. });
  83. this.started = true;
  84. }
  85. kill() {
  86. if(!this.started) return;
  87. Tk.stopTimer(this.gameTimer);
  88. Tk.stopTimer(this.animTimer);
  89. this.started = false;
  90. }
  91. playGuide(callback) {
  92. if(this.autoPlay) return callback();
  93. let fg = Tk.createWidget(hmUI.widget.IMG, {
  94. x: 2, y: 239,
  95. src: "app_im02/guide.png"
  96. });
  97. Animation.animate({
  98. start: 0,
  99. end: 5,
  100. steps: 5,
  101. delay: 400,
  102. step: (v) => {
  103. fg.setProperty(hmUI.prop.MORE, {
  104. alpha: v % 2 == 1 ? 180 : 0
  105. });
  106. },
  107. finish: () => {
  108. Tk.deleteWidget(fg);
  109. callback();
  110. }
  111. })
  112. }
  113. gameover() {
  114. this.gameState.gameover = true;
  115. this.gameState.bonus = false;
  116. ElIM02TopScore.addResult(this.gameState.score);
  117. const hidden = ["player_right", "player_tr", "player_br"];
  118. for(var key in this.seg) {
  119. this.modify(this.seg[key], hidden.indexOf(key) < 0, false);
  120. }
  121. Vibro.melody([250, 25, 250, 25, 250, 25, 250]);
  122. const ti = Tk.delay(2500, () => {
  123. Tk.run(ElIM02TopScore.im02TopScore);
  124. });
  125. }
  126. gameTick() {
  127. if(this.autoPlay && this.gameState.fails >= 3) {
  128. this.gameState.score = 0;
  129. this.gameState.fails = 0;
  130. this.view_score.setProperty(hmUI.prop.TEXT, this.gameState.score.toString());
  131. }
  132. if(this.gameState.fails >= 3) {
  133. if(!this.gameState.gameover) this.gameover();
  134. return;
  135. }
  136. // Game speed
  137. const speed = Math.max(1, 3 - Math.floor(this.gameState.score / 500));
  138. this.gameState.tick += 1;
  139. if(this.gameState.tick % speed != 0) return;
  140. this.modify(this.seg.fail_left, true, false);
  141. this.modify(this.seg.fail_right, true, false);
  142. let cur = this.gameState.eggs;
  143. let free = [];
  144. for(let i = 0; i < 4; i++) {
  145. // If == 3, check catch state
  146. if(cur[i] == 3) this.catchEgg(i);
  147. // Move
  148. if(cur[i] > -1) {
  149. cur[i] += 1;
  150. } else free.push(i);
  151. }
  152. // Spawn new egg (5-35% rate)
  153. const l = free.length;
  154. const th = 0.05 + (this.gameState.score / 1000)*0.3;
  155. if((Math.random() > (1-th) && l > 1) || free.length == 4) {
  156. const fi = Math.floor(Math.random() * l);
  157. const i = free[fi];
  158. cur[i] = 0;
  159. }
  160. // Bonus? (4% rate)
  161. if(!this.gameState.bonus && Math.random() > 0.96) {
  162. this.gameState.bonus = Date.now();
  163. } else if(Date.now() - this.gameState.bonus > 5000) {
  164. this.gameState.bonus = 0;
  165. }
  166. // Update UI
  167. this.refreshEggs();
  168. // AutoPlay
  169. if(this.autoPlay) {
  170. let topVal = 0, topIndex = -1;
  171. for(let i = 0; i < 4; i++) {
  172. if(this.gameState.eggs[i] > topVal) {
  173. topVal = this.gameState.eggs[i];
  174. topIndex = i;
  175. }
  176. }
  177. if(topIndex > -1 && Math.random() < (speed == 3 ? 0.75 : 0.5)) {
  178. this.setPosition(topIndex > 1 ? 1 : 0, topIndex % 2);
  179. }
  180. }
  181. }
  182. animTick() {
  183. const val = this.gameState.fails;
  184. const bonus = this.gameState.bonus;
  185. const gameover = this.gameState.gameover;
  186. this.modify(this.seg.fail_1, true, (val >= 1) || (val == 0.5 && this.tick));
  187. this.modify(this.seg.fail_2, true, (val >= 2) || (val == 1.5 && this.tick));
  188. this.modify(this.seg.fail_3, true, (val >= 3) || (val == 2.5 && this.tick));
  189. this.modify(this.seg.bonus0, true, bonus && this.tick);
  190. this.modify(this.seg.bonus1, true, bonus && !this.tick);
  191. this.modify(this.seg.gameover, gameover, gameover && this.tick);
  192. this.tick = !this.tick;
  193. }
  194. catchEgg(i) {
  195. const validX = this.gameState.posX == (i > 1 ? 1 : 0);
  196. const validY = this.gameState.posY == i % 2;
  197. if(validY && validX) {
  198. this.gameState.score += 25;
  199. this.view_score.setProperty(hmUI.prop.TEXT, this.gameState.score.toString());
  200. if(!this.autoPlay) Vibro.run(25);
  201. } else {
  202. const v = this.gameState.fails;
  203. const bonus = this.gameState.bonus;
  204. this.gameState.fails = bonus ? v + 0.5 : Math.floor(v + 1);
  205. const an = i > 1 ? this.seg.fail_right : this.seg.fail_left;
  206. this.modify(an, true, true);
  207. if(!this.autoPlay) Vibro.run(200);
  208. }
  209. this.gameState.eggs[i] = -1;
  210. }
  211. initGameUI() {
  212. let currentBgColor = hmFS.SysProGetInt("im02_bg");
  213. if(!currentBgColor) currentBgColor = 0;
  214. this.bgWidget = Tk.createWidget(hmUI.widget.FILL_RECT, {
  215. x: 0, y: 0, w: 192, h: 490,
  216. color: ElIM02.colorVariants[currentBgColor]
  217. });
  218. Tk.createWidget(hmUI.widget.IMG, {
  219. x: 0, y: this.topOffset,
  220. src: "app_im02/game_bg.png"
  221. });
  222. // Draw all segments
  223. for(let key in this.segmentData) {
  224. this.seg[key] = Tk.createWidget(hmUI.widget.IMG, {
  225. src: "app_im02/" + key + ".png",
  226. alpha: 20,
  227. ...this.segmentData[key],
  228. y: this.topOffset + this.segmentData[key].y
  229. })
  230. }
  231. for(let i = 0; i < 4; i++) {
  232. this.eggs[i] = Tk.createWidget(hmUI.widget.IMG, {
  233. x: -20,
  234. y: -20,
  235. src: "app_im02/egg.png"
  236. })
  237. }
  238. // Score
  239. this.view_score = Tk.createWidget(hmUI.widget.TEXT_IMG, {
  240. x: 121, y: 159,
  241. text: "0",
  242. alpha: this.ALPHA_VISIBLE,
  243. font_array: Tk.mkFontArrayAt("app_im02/font/{}.png")
  244. })
  245. // Controls
  246. if(this.autoPlay) return;
  247. Tk.createWidget(hmUI.widget.IMG, {
  248. x: 0, y: 0, w: 96, h: 320, src: ""
  249. }).addEventListener(hmUI.event.CLICK_UP, () => {
  250. this.setPosition(0, 0);
  251. });
  252. Tk.createWidget(hmUI.widget.IMG, {
  253. x: 96, y: 0, w: 96, h: 320, src: ""
  254. }).addEventListener(hmUI.event.CLICK_UP, () => {
  255. this.setPosition(1, 0);
  256. });
  257. Tk.createWidget(hmUI.widget.IMG, {
  258. x: 0, y: 320, w: 96, h: 170, src: ""
  259. }).addEventListener(hmUI.event.CLICK_UP, () => {
  260. this.setPosition(0, 1);
  261. });
  262. Tk.createWidget(hmUI.widget.IMG, {
  263. x: 96, y: 320, w: 96, h: 170, src: ""
  264. }).addEventListener(hmUI.event.CLICK_UP, () => {
  265. this.setPosition(1, 1);
  266. });
  267. }
  268. dropEgg(slot) {
  269. this.gameState.eggs[slot] = 0;
  270. this.refreshEggs();
  271. }
  272. refreshEggs() {
  273. for(let i = 0; i < 4; i++) {
  274. const val = this.gameState.eggs[i];
  275. let baseX = 8 + 10*val;
  276. let baseY = (i % 2 ? this.eggBottomY : this.eggTopY) + (val * 6);
  277. if(i > 1) baseX = 192 - baseX;
  278. if(val < 0) baseX = -20;
  279. this.eggs[i].setProperty(hmUI.prop.MORE, {
  280. x: baseX,
  281. y: baseY,
  282. // pos_x: baseX - 5,
  283. // pos_y: baseY - 5,
  284. // angle: val*20*(i > 1 ? -1 : 1)
  285. })
  286. }
  287. }
  288. setPosition(posX, posY) {
  289. if(this.gameState.gameover) return;
  290. this.gameState.posX = posX;
  291. this.gameState.posY = posY;
  292. this.modify(this.seg.player_left, posX == 0, posX == 0);
  293. this.modify(this.seg.player_right, posX == 1, posX == 1);
  294. this.modify(this.seg.player_tl, posX == 0, posY == 0);
  295. this.modify(this.seg.player_tr, posX == 1, posY == 0);
  296. this.modify(this.seg.player_bl, posX == 0, posY == 1);
  297. this.modify(this.seg.player_br, posX == 1, posY == 1);
  298. }
  299. modify(w, visible, fullVisible) {
  300. w.setProperty(hmUI.prop.VISIBLE, visible);
  301. if(!visible) return;
  302. w.setProperty(hmUI.prop.MORE, {
  303. alpha: fullVisible ? this.ALPHA_VISIBLE : this.ALPHA_BG
  304. });
  305. }
  306. }
  307. TK_CONFIG.applications.push(ElIM02.entry);