Simanalix343

Idle Game Maker Moddable

Sep 30th, 2020
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 169.89 KB | None | 0 0
  1.  
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>Idle Game Maker</title>
  6. <!--
  7. Code and graphics copyright Orteil, 2017
  8. Feel free to alter this code to your liking, but please do not re-host it, do not profit from it and do not present it as your own.
  9. -->
  10.  
  11. <link rel="shortcut icon" href="img/favicon.ico" />
  12. <style>
  13.  
  14. /* reset CSS */
  15. html, body, div, span, applet, object, iframe,
  16. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  17. a, abbr, acronym, address, big, cite, code,
  18. del, dfn, em, img, ins, kbd, q, s, samp,
  19. small, strike, strong, sub, sup, tt, var,
  20. b, u, i, center,
  21. dl, dt, dd, ol, ul, li,
  22. fieldset, form, label, legend,
  23. table, caption, tbody, tfoot, thead, tr, th, td,
  24. article, aside, canvas, details, embed,
  25. figure, figcaption, footer, header, hgroup,
  26. menu, nav, output, ruby, section, summary,
  27. time, mark, audio, video {
  28. margin: 0;
  29. padding: 0;
  30. border: 0;
  31. font-size: 100%;
  32. font: inherit;
  33. vertical-align: baseline;
  34. -moz-box-sizing:border-box;
  35. box-sizing:border-box;
  36. }
  37. /* HTML5 display-role reset for older browsers */
  38. article, aside, details, figcaption, figure,
  39. footer, header, hgroup, menu, nav, section {
  40. display: block;
  41. }
  42. body {
  43. line-height: 1;
  44. }
  45. ol, ul {
  46. list-style: none;
  47. }
  48. blockquote, q {
  49. quotes: none;
  50. }
  51. blockquote:before, blockquote:after,
  52. q:before, q:after {
  53. content: '';
  54. content: none;
  55. }
  56. table {
  57. border-collapse: collapse;
  58. border-spacing: 0;
  59. }
  60.  
  61.  
  62.  
  63.  
  64. /*=====================================================================================
  65. BASE
  66. =======================================================================================*/
  67. html,body
  68. {
  69. width:100%;
  70. height:100%;
  71. }
  72. body
  73. {
  74. -webkit-touch-callout: none;
  75. -webkit-user-select: none;
  76. -khtml-user-select: none;
  77. -moz-user-select: -moz-none;
  78. -moz-user-select: none;
  79. -ms-user-select: none;
  80. user-select: none;
  81. color:#ccc;
  82. text-shadow:1px 1px 0px #000;
  83. background:#000;
  84. font-family:Tahoma,Arial,sans-serif;
  85. font-size:11px;
  86. }
  87.  
  88. b{font-weight:bold;}
  89. i{font-style:italic;}
  90. u{text-decoration:underline;}
  91. small{font-size:80%;}
  92.  
  93. .outFrame #game
  94. {
  95. position:absolute;
  96. left:0px;top:26px;right:0px;bottom:102px;
  97. display:block;
  98. background:#000;
  99. }
  100. .inFrame #game
  101. {
  102. position:absolute;
  103. left:0px;top:0px;right:0px;bottom:0px;
  104. display:none;
  105. }
  106.  
  107. #player,#main
  108. {
  109. position:absolute;
  110. width:100%;
  111. height:100%;
  112. }
  113.  
  114. .outFrame #code
  115. {
  116. position:absolute;
  117. left:0px;top:26px;right:0px;bottom:102px;
  118. display:block;
  119. background:#000;
  120. }
  121. #editor
  122. {
  123. position:absolute;
  124. width:100%;
  125. height:100%;
  126. display:none;
  127. }
  128. .withCode #editor
  129. {
  130. display:block;
  131. }
  132.  
  133. .editor #game
  134. {
  135. overflow-x:hidden;
  136. overflow-y:scroll;
  137. }
  138. .editor #codeWrap
  139. {
  140. width:100%;
  141. height:100%;
  142. padding:8px;
  143. }
  144. .editor #code
  145. {
  146. width:100%;
  147. height:100%;
  148. padding:16px;
  149. margin:0px;
  150. outline:none;
  151. box-sizing:border-box;
  152. }
  153.  
  154.  
  155. #topBar,#ad
  156. {
  157. color:#999;
  158. font-size:11px;
  159. background:#222;
  160. }
  161.  
  162. #topBar
  163. {
  164. position:absolute;
  165. left:0px;right:0px;top:0px;height:26px;
  166. background:url(img/darkNoiseTopBar.jpg) repeat-x bottom,url(img/darkNoise.jpg);
  167. }
  168. #topBar>div
  169. {
  170. display:inline-block;
  171. float:left;
  172. border-right:1px solid #000;
  173. box-shadow:0px 0px 3px 1px rgba(255,255,255,0.2) inset;
  174. padding:5px 8px 7px 8px;
  175. }
  176. #topBar a
  177. {color:#ccc;}
  178. #topBar a:hover
  179. {color:#fff;}
  180. #topBar a.blueLink
  181. {color:#06c;}
  182. #topBar a.blueLink:hover
  183. {color:#28f;text-shadow:0px 0px 3px #06c;}
  184. #topBar>#links
  185. {
  186. display:block;
  187. position:absolute;
  188. right:0px;
  189. top:0px;
  190. z-index:100000000000;
  191. float:none;
  192. }
  193.  
  194. #ad
  195. {
  196. position:absolute;
  197. left:0px;right:0px;bottom:0px;height:102px;
  198. background:linear-gradient(to right,rgba(0,0,0,0) 15%,rgba(0,0,0,0.75)),url(img/darkNoiseRightBar.jpg) repeat-y left,url(img/darkNoise.jpg);
  199. }
  200.  
  201.  
  202.  
  203. .outFrame #game
  204. {
  205. right:13%;
  206. bottom:0px;
  207. }
  208. .outFrame.withCode #game
  209. {
  210. left:30%;
  211. }
  212. .outFrame #code
  213. {
  214. left:0px;
  215. width:30%;
  216. bottom:0px;
  217. }
  218. #ad
  219. {
  220. left:auto;
  221. right:0px;
  222. top:26px;
  223. bottom:0px;
  224. width:13%;
  225. height:auto;
  226. }
  227.  
  228. #fpsCounter
  229. {
  230. background:#333;
  231. color:#fff;
  232. position:absolute;
  233. left:0px;
  234. bottom:0px;
  235. padding:3px;
  236. z-index:10000001;
  237. opacity:0.5;
  238. pointer-events:none;
  239. }
  240. #fpsGraph
  241. {
  242. background:#333;
  243. color:#fff;
  244. position:absolute;
  245. left:0px;
  246. bottom:0px;
  247. padding:3px;
  248. width:128px;
  249. height:64px;
  250. z-index:10000000;
  251. opacity:0.5;
  252. pointer-events:none;
  253. }
  254.  
  255. #wrap
  256. {
  257. position:absolute;left:0px;right:0px;top:0px;bottom:0px;
  258. overflow:hidden;
  259. }
  260.  
  261. </style>
  262.  
  263. </head>
  264.  
  265. <body>
  266. <div id="wrap" class="outFrame">
  267. <div id="topBar"></div>
  268.  
  269. <div id="code">
  270. </div>
  271. <div id="game">
  272. </div>
  273.  
  274. <div id="ad">
  275. <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
  276. <!-- IGM responsive
  277. <ins class="adsbygoogle"
  278. style="display:block"
  279. data-ad-client="ca-pub-8491708950677704"
  280. data-ad-slot="2107382385"
  281. data-ad-format="auto"></ins>
  282. <script>
  283. (adsbygoogle = window.adsbygoogle || []).push({});
  284. </script>-->
  285. </div>
  286. </div>
  287. <script>
  288. console.log("starting script");
  289. function l(what) {return document.getElementById(what);}
  290. function choose(arr) {return arr[Math.floor(Math.random()*arr.length)];}
  291. function randomFloor(x) {if ((x%1)<Math.random()) return Math.floor(x); else return Math.ceil(x);}
  292. String.prototype.replaceAll=function(search,replacement)
  293. //{var target=this;return target.replace(new RegExp(search,'g'),replacement);};
  294. {return this.split(search).join(replacement);};
  295. function AddEvent(html_element,event_name,event_function)
  296. {
  297. if(html_element.attachEvent) html_element.attachEvent("on" + event_name, function() {event_function.call(html_element);});
  298. else if(html_element.addEventListener) html_element.addEventListener(event_name, event_function, false);
  299. }
  300.  
  301. function LoadScript(url,callback,onerror)
  302. {
  303. var script=document.createElement('script');
  304. script.setAttribute('src',url);
  305. if (callback) script.onload=callback;
  306. if (onerror) script.onerror=onerror;
  307. document.head.appendChild(script);
  308. return script;
  309. }
  310.  
  311. function ajax(url,callback)
  312. {
  313. var ajaxRequest;
  314. try{ajaxRequest=new XMLHttpRequest();} catch (e){try{ajaxRequest=new ActiveXObject('Msxml2.XMLHTTP');} catch (e) {try{ajaxRequest=new ActiveXObject('Microsoft.XMLHTTP');} catch (e){alert("Something broke!");return false;}}}
  315. if (callback){ajaxRequest.onreadystatechange=function(){if(ajaxRequest.readyState==4){callback(ajaxRequest.responseText);}}}
  316. ajaxRequest.open('GET',url+'&nocache='+(new Date().getTime()),true);ajaxRequest.send(null);
  317. }
  318.  
  319. var INFRAME=INFRAME||true;
  320. var EDITOR=EDITOR||false;
  321.  
  322. G={};
  323.  
  324. console.log("first functions loaded");
  325.  
  326. G.Launch=function()
  327. {
  328. if (!INFRAME)
  329. {
  330. console.log("INFRAME is false, now loading base");
  331. l('topBar').innerHTML=
  332. '<div><b>Idle Game Maker</b>&trade; &copy; <a href="http://orteil.dashnet.org" target="_blank">Orteil</a>, 2017 - <a href="http://dashnet.org" target="_blank">DashNet</a></div>'+
  333. '<div><a href="http://twitter.com/orteil42" target="_blank">twitter</a></div>'+
  334. '<div><a href="http://orteil42.tumblr.com" target="_blank">tumblr</a></div>'+
  335. '<div>Help? Bugs? Ideas? Check out the <a href="http://forum.dashnet.org" target="_blank">forum</a>!</div>'+
  336. '<div><a href="./help.html" target="_blank">Game-making help</a></div>'+
  337. '<div>Also : <a href="http://orteil.dashnet.org/cookieclicker/" target="_blank">Cookie Clicker</a></div>'+
  338. '';
  339. }
  340. console.log("defining initialization");
  341. G.Init=function(){};
  342. G._Init=function()
  343. {
  344. G.T=0;
  345. G.drawT=0;
  346. G.fps=30;
  347.  
  348. G.l=l('game');
  349. G.wrapl=l('wrap');
  350.  
  351. G.local=true;
  352. if (window.location.protocol=='http:' || window.location.protocol=='https:'){G.local=false;console.log("you use http protocall");}
  353. G.isIE=false;
  354. if (document.documentMode || /Edge/.test(navigator.userAgent)) G.isIE=true;
  355.  
  356. if (!G.local && !INFRAME)
  357. {
  358. window.cookieconsent_options={"message":"This website uses cookies for ads and traffic analysis.","dismiss":"Got it!","learnMore":"Learn more","link":"http://orteil.dashnet.org/cookieconsentpolicy.html","target":"_blank","theme":"http://orteil.dashnet.org/cookieconsent.css","domain":"dashnet.org"};
  359. LoadScript("//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js");
  360. }
  361. console.log("cookie consent loaded");
  362. G.w=window.innerWidth;
  363. G.h=window.innerHeight;
  364. G.resizing=false;
  365. if (!G.stabilizeResize) G.stabilizeResize=function(){}
  366. G._stabilizeResize=function()
  367. {
  368. G.resizing=false;
  369. G.stabilizeResize();
  370. }
  371. G.resize=function()
  372. {
  373. G.resizing=true;
  374. }
  375. window.addEventListener('resize',function(event)
  376. {
  377. G.w=window.innerWidth;
  378. G.h=window.innerHeight;
  379. G.resize();
  380. });
  381. console.log("screen resizing adaption added");
  382. G.mouseDown=false;//mouse button just got pressed
  383. G.mouseUp=false;//mouse button just got released
  384. G.mousePressed=false;//mouse button is currently down
  385. G.clickL=0;//what element got clicked
  386. AddEvent(document,'mousedown',function(event){G.mouseDown=true;G.mousePressed=true;G.mouseDragFrom=event.target;G.mouseDragFromX=G.mouseX;G.mouseDragFromY=G.mouseY;});
  387. AddEvent(document,'mouseup',function(event){G.mouseUp=true;G.mouseDragFrom=0;});
  388. AddEvent(document,'click',function(event){G.clickL=event.target;});
  389.  
  390. G.mouseX=0;
  391. G.mouseY=0;
  392. G.mouseMoved=0;
  393. G.draggedFrames=0;//increment every frame when we're moving the mouse and we're clicking
  394. G.GetMouseCoords=function(e)
  395. {
  396. var posx=0;
  397. var posy=0;
  398. if (!e) var e=window.event;
  399. if (e.pageX||e.pageY)
  400. {
  401. posx=e.pageX;
  402. posy=e.pageY;
  403. }
  404. else if (e.clientX || e.clientY)
  405. {
  406. posx=e.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;
  407. posy=e.clientY+document.body.scrollTop+document.documentElement.scrollTop;
  408. }
  409. var x=0;
  410. var y=0;
  411. G.mouseX=posx-x;
  412. G.mouseY=posy-y;
  413. G.mouseMoved=1;
  414. }
  415. AddEvent(document,'mousemove',G.GetMouseCoords);
  416. console.log("mouse tracker loaded");
  417.  
  418.  
  419. G.Scroll=0;
  420. G.handleScroll=function(e)
  421. {
  422. if (!e) e=event;
  423. G.Scroll=(e.detail<0||e.wheelDelta>0)?1:-1;
  424. };
  425. AddEvent(document,'DOMMouseScroll',G.handleScroll);
  426. AddEvent(document,'mousewheel',G.handleScroll);
  427.  
  428. G.keys={};//key is being held down
  429. G.keysD={};//key was just pressed down
  430. G.keysU={};//key was just pressed up
  431. //shift=16, ctrl=17
  432. AddEvent(window,'keyup',function(e){
  433. if ((document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') && e.keyCode!=27) return;
  434. if (e.keyCode==27) {}//esc
  435. else if (e.keyCode==13) {}//enter
  436. G.keys[e.keyCode]=0;
  437. G.keysD[e.keyCode]=0;
  438. G.keysU[e.keyCode]=1;
  439. });
  440. AddEvent(window,'keydown',function(e){
  441. if (!G.keys[e.keyCode])//prevent repeats
  442. {
  443. if (e.ctrlKey && e.keyCode==83) {e.preventDefault();}//ctrl-s
  444. if ((document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') && e.keyCode!=27) return;
  445. if (e.keyCode==32) {e.preventDefault();}//space
  446. G.keys[e.keyCode]=1;
  447. G.keysD[e.keyCode]=1;
  448. G.keysU[e.keyCode]=0;
  449. //console.log('Key pressed : '+e.keyCode);
  450. }
  451. });
  452. console.log("key press tracker is done");
  453. AddEvent(window,'blur',function(e){
  454. G.keys={};
  455. G.keysD={};
  456. G.keysU={};
  457. });
  458.  
  459. //latency compensator stuff
  460. G.time=new Date().getTime();
  461. G.fpsMeasure=new Date().getTime();
  462. G.accumulatedDelay=0;
  463. G.catchupLogic=0;
  464. G.fpsStartTime=0;
  465. G.frameNumber=0;
  466. G.getFps=function()
  467. {
  468. G.frameNumber++;
  469. var currentTime=(Date.now()-G.fpsStartTime )/1000;
  470. var result=Math.ceil((G.frameNumber/currentTime));
  471. if (currentTime>1)
  472. {
  473. G.fpsStartTime=Date.now();
  474. G.frameNumber=0;
  475. }
  476. return result;
  477. }
  478.  
  479. var div=document.createElement('div');
  480. div.id='fpsCounter';
  481. G.wrapl.appendChild(div);
  482. G.fpsCounter=div;
  483. var div=document.createElement('canvas');
  484. div.id='fpsGraph';
  485. div.width=128;
  486. div.height=64;
  487. G.wrapl.appendChild(div);
  488. G.fpsGraph=div;
  489. G.fpsGraphCtx=G.fpsGraph.getContext('2d');
  490. var ctx=G.fpsGraphCtx;
  491. ctx.fillStyle='#000';
  492. ctx.fillRect(0,0,128,64);
  493. G.currentFps=0;
  494. G.previousFps=0;
  495. console.log("fps counter prepped.");
  496.  
  497. G.resize();
  498.  
  499. G.Init();
  500.  
  501. G.Loop();
  502. console.log("Game initialized");
  503.  
  504. }
  505.  
  506. //callbacks system : basically we have functions that return HTML but also add a callback to the callbacks array; after the HTML has been added to the DOM we call G.addCallbacks() to apply all the pending callbacks - this lets us centralize HTML and callbacks in one function
  507. console.log("defining call back system");
  508. G.Callbacks=[];
  509. G.callbackDepth=0;
  510. G.addCallbacks=function()
  511. {
  512. if (G.callbackDepth>0) return false;//prevent nesting callbacks
  513. G.callbackDepth++;
  514. var len=G.Callbacks.length;
  515. for (var i=0;i<len;i++)
  516. {G.Callbacks[i]();}
  517. G.Callbacks=[];
  518. G.callbackDepth--;
  519. }
  520. G.pushCallback=function(func)
  521. {
  522. G.Callbacks.push(func);
  523. }
  524.  
  525. console.log("defining button system");
  526. G.buttonsN=0;
  527. G.button=function(obj)
  528. {
  529. //returns a string for a new button; creates a callback that must be applied after the html has been created, with G.addCallbacks()
  530. //obj can have text, tooltip (text that shows on hover), onclick (function executed when button is clicked), classes (CSS classes added to the button), id (force button to have that id)
  531. var id=obj.id||('button-'+G.buttonsN);
  532. var str='<div '+(obj.style?('style="'+obj.style+'" '):'')+'class="systemButton'+(obj.classes?(' '+obj.classes):'')+'" id="'+id+'">'+(obj.text||'-')+'</div>';
  533. if (obj.onclick || obj.tooltip)
  534. {
  535. G.pushCallback(function(id,obj){return function(){
  536. if (l(id))
  537. {
  538. if (typeof obj.tooltip==='function') G.addTooltip(l(id),{func:obj.tooltip});
  539. else G.addTooltip(l(id),{text:obj.tooltip});
  540. if (obj.onclick) l(id).onclick=obj.onclick;
  541. }
  542. }}(id,obj));
  543. }
  544. G.buttonsN++;
  545. return str;
  546. }
  547.  
  548. console.log("defining automatically updating text system");
  549. G.textN=0;
  550. G.updateTextTimer=function(id,func,freq)
  551. {
  552. var el=l('updatabletextspan-'+id);
  553. if (el)
  554. {
  555. var delay=freq*1000;
  556. if (freq<0) {freq=-freq;delay=100;}//a trick : the first update always occurs 100ms after being declared
  557. el.innerHTML=func();
  558. G.addCallbacks();
  559. setTimeout(function(id,func){return function(){G.updateTextTimer(id,func,freq);}}(id,func),delay);
  560. }
  561. }
  562. G.selfUpdatingText=function(func,freq)
  563. {
  564. if (!freq) freq=1;
  565. //returns a string for a span of text that updates itself every second; creates a callback that must be applied after the html has been created, with G.addCallbacks()
  566. var id=G.textN;
  567. var str='<span class="updatabletextspan" id="updatabletextspan-'+id+'">'+func()+'</span>';
  568. G.pushCallback(function(id,func,freq){return function(){
  569. G.updateTextTimer(id,func,-freq);
  570. }}(id,func,freq));
  571. G.textN++;
  572. return str;
  573. }
  574.  
  575. console.log("settihn up tool tips");
  576. G.tooltipdN=0;
  577. G.tooltipped=function(text,obj,style)
  578. {
  579. var id='tooltippedspan-'+G.tooltipdN;
  580. //var str='<span class="tooltippedspan"'+(style?' style="'+style+'"':'')+' id="'+id+'">'+text+'</span>';
  581. var div=document.createElement('div');
  582. //weird trickery here, don't even ask
  583. div.innerHTML=text;
  584. if (div.firstChild.nodeType==3) div.innerHTML='<span>'+div.innerHTML+'</span>';
  585. if (!div.firstChild.id) div.firstChild.id=id;
  586. else id=div.firstChild.id;
  587. str=div.innerHTML;
  588. G.pushCallback(function(id,obj){return function(){
  589. if (typeof obj==='string') G.addTooltip(l(id),{text:obj});
  590. else G.addTooltip(l(id),obj);
  591. }}(id,obj));
  592. G.tooltipdN++;
  593. return str;
  594. }
  595.  
  596.  
  597.  
  598. /*=====================================================================================
  599. LOGIC
  600. =======================================================================================*/
  601. console.log("building main logic");
  602. G.Logic=function(){}
  603. G._Logic=function()
  604. {
  605. G.Logic();
  606. if (G.T%5==0 && G.resizing) {G._stabilizeResize();}
  607.  
  608. if (G.mouseUp) G.mousePressed=false;
  609. G.mouseDown=false;
  610. G.mouseUp=false;
  611. if (G.mouseMoved && G.mousePressed) G.draggedFrames++; else if (!G.mousePressed) G.draggedFrames=0;
  612. G.mouseMoved=0;
  613. G.Scroll=0;
  614. G.clickL=0;
  615. G.keysD={};
  616. G.keysU={};
  617. if (document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') G.keys={};
  618.  
  619. G.T++;
  620. }
  621.  
  622. /*=====================================================================================
  623. DRAW
  624. =======================================================================================*/
  625. console.log("defining draw system");
  626. G.Draw=function(){};
  627. G._Draw=function()
  628. {
  629. G.Draw();
  630. G.drawT++;
  631. }
  632.  
  633. /*=====================================================================================
  634. MAIN LOOP
  635. =======================================================================================*/
  636. console.log("setting up maim game loop");
  637. G.Loop=function()
  638. {
  639. //update game logic !
  640. G.catchupLogic=0;
  641. G._Logic();
  642. G.catchupLogic=1;
  643.  
  644. //latency compensator
  645. G.accumulatedDelay+=((new Date().getTime()-G.time)-1000/G.fps);
  646. G.accumulatedDelay=Math.min(G.accumulatedDelay,1000*5);//don't compensate over 5 seconds; if you do, something's probably very wrong
  647. G.time=new Date().getTime();
  648. while (G.accumulatedDelay>0)
  649. {
  650. G._Logic();
  651. G.accumulatedDelay-=1000/G.fps;//as long as we're detecting latency (slower than target fps), execute logic (this makes drawing slower but makes the logic behave closer to correct target fps)
  652. }
  653. G.catchupLogic=0;
  654.  
  655. if (!document.hidden || document.hasFocus() || G.T%5==0) G._Draw();
  656.  
  657. //fps counter and graph
  658. G.previousFps=G.currentFps;
  659. G.currentFps=G.getFps();
  660. if (INFRAME && !EDITOR)
  661. {
  662. l('fpsCounter').innerHTML=G.currentFps+' fps';
  663. var ctx=G.fpsGraphCtx;
  664. ctx.drawImage(G.fpsGraph,-1,0);
  665. ctx.fillStyle='rgb('+Math.round((1-G.currentFps/G.fps)*128)+',0,0)';
  666. ctx.fillRect(128-1,0,1,64);
  667. ctx.strokeStyle='#fff';
  668. ctx.beginPath();
  669. ctx.moveTo(128-1,(1-G.previousFps/G.fps)*64);
  670. ctx.lineTo(128,(1-G.currentFps/G.fps)*64);
  671. ctx.stroke();
  672. }
  673.  
  674. setTimeout(G.Loop,1000/G.fps);
  675. }
  676. }
  677.  
  678. console.log("g defined, now launching g");
  679.  
  680. G.Launch();
  681.  
  682. console.log("launch complete!");
  683.  
  684. if (!INFRAME)
  685. {
  686. window.onload=function()
  687. {
  688. console.log("window loaded");
  689. if (!G.ready)
  690. {
  691. console.log("getting g ready");
  692. G.ready=true;
  693. var vars={};
  694. var parts=window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(m,key,value){vars[key]=value;});
  695. G.vars=vars;
  696. console.log("(NOT) loading i frames");
  697. if (G.vars.e)
  698. {
  699. l('code').innerHTML='<iframe id="editor" src="editor.html"></iframe>';
  700. l('wrap').classList.add('withCode');
  701. }
  702. if (G.vars.g)
  703. {
  704. l('game').innerHTML='<iframe id="player" src="player.html"></iframe>';
  705. }
  706. else
  707. {
  708. l('game').innerHTML='<iframe id="main" src="main.html"></iframe>';
  709. }
  710. }
  711. };
  712. G.loadedEditor=false;
  713. G.loadedPlayer=false;
  714. window.addEventListener('message',function(e)
  715. {
  716. if (e.data.playerReady && !G.loadedPlayer)
  717. {
  718. G.loadedPlayer=true;
  719. l('player').contentWindow.postMessage({launch:true,loc:window.location.origin,vars:G.vars},'*');
  720. }
  721. else if (e.data.editorReady) {}
  722. else if (e.data.code && !G.loadedEditor && l('editor'))
  723. {
  724. G.loadedEditor=true;
  725. l('editor').contentWindow.postMessage({code:e.data.code},'*');
  726. }
  727. else if (e.data.refresh)
  728. {
  729. l('game').innerHTML='<iframe id="player" src="player.html"></iframe>';
  730. setTimeout(function(){l('player').contentWindow.postMessage({launch:true,loc:window.location.origin,vars:G.vars,code:e.data.refresh},'*');},100);
  731. }
  732. });
  733. }
  734. else
  735. {
  736. console.log("inframe is "+INFRAME+", editor is "+EDITOR+", and ready is "+G.ready);
  737. if (!G.ready)
  738. {
  739. if (EDITOR)
  740. {
  741. console.log("code editor is on");
  742. G.ready=true;
  743. G.timeSinceTyped=0;
  744. l('game').innerHTML=`
  745. <div id="codeWrap">
  746. <textarea id="code"></textarea>
  747. </div>
  748. `;
  749. AddEvent(l('code'),'input',function(){G.timeSinceTyped=Math.ceil(G.fps/2);});
  750. G.Logic=function(){
  751. if (G.timeSinceTyped>0) G.timeSinceTyped--;
  752. if (G.timeSinceTyped==1) parent.postMessage({refresh:l('code').value},'*');
  753. }
  754. G._Init();
  755. parent.postMessage({editorReady:true},'*');
  756. }
  757. else
  758. {
  759. parent.postMessage({playerReady:true},'*');
  760. }
  761. }
  762. window.addEventListener('click',function(e)
  763. {if(!G.ready){
  764. e.data={};
  765. e.data.code=`
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.  
  773. `;
  774. console.log("click recieved");
  775. if (EDITOR)
  776. {
  777. console.log("code editor is on 2");
  778. if (e.data.code)
  779. {
  780. l('code').value=e.data.code;
  781. l('game').style.display='block';
  782. }
  783. }
  784. else
  785. {
  786. console.log("prepping for a game...");
  787. if (!G.ready && true)
  788. {
  789. G.ready=true;
  790. if (true)
  791. {
  792. if (e.data.code) TOPARSE=e.data.code;
  793. G.urlVars=e.data.vars;
  794. console.log("final step, loading game engine");
  795. /*=====================================================================================
  796. HELPER FUNCTIONS
  797. =======================================================================================*/
  798.  
  799. function triggerAnim(element,anim)
  800. {
  801. if (!element) return;
  802. element.classList.remove(anim);
  803. void element.offsetWidth;
  804. element.classList.add(anim);
  805. }
  806.  
  807. function isNumber(n)
  808. {return (!isNaN(parseFloat(n)) && isFinite(n));}
  809.  
  810. function parseNum(n)
  811. {
  812. if (n===true) return 1;
  813. else if (n===false) return 0;
  814. else if (isNaN(n)) return 0;
  815. else return n;
  816. }
  817.  
  818. var luck=function(x)
  819. {
  820. if (Math.random()<x/100) return true; else return false;
  821. }
  822. var rand=function(x,y)
  823. {
  824. if (!y) y=0;
  825. if (y<x) {var tmp=x;x=y;y=tmp;}
  826. return Math.round(Math.random()*(y-x)+x);
  827. }
  828. var frand=function(x,y)
  829. {
  830. if (!y) y=0;
  831. if (y<x) {var tmp=x;x=y;y=tmp;}
  832. return (Math.random()*(y-x)+x);
  833. }
  834. console.log("math and animation helper functions added");
  835.  
  836. //String2 is a pseudo-string type with some added functions that are as ambiguous in purpose as they are buggy
  837. var String2=function(str)
  838. {
  839. this.val=str;
  840. }
  841. String2.prototype.toString=function(){return this.val;}
  842.  
  843. /*
  844. String2.gulp(str) : if the string begins with str, remove str from the string and return what we gulped; else return false
  845. If str is unspecified, gulp a whole word until the next space and return it
  846. If a quote " is encountered, keep gulping until the next quote
  847. Example :
  848. var str=STR2('set value3 to 100');
  849. if (str.gulp('set'))
  850. {
  851. var val=str.gulpUntil('to');
  852. var amount=str.gulpUntil();
  853. console.log(val,amount);
  854. }
  855. */
  856. String2.prototype.gulp=function(str,isSymbol)
  857. {
  858. if (str)
  859. {
  860. if (!isSymbol) str=str+=' ';
  861. if (this.val.indexOf(str)==0)
  862. {
  863. this.val=this.val.substring(str.length);
  864. if (isSymbol && this.val.charAt(0)==' ') this.val=this.val.substring(1);//remove first space
  865. return str;
  866. }
  867. else return false;
  868. }
  869. else
  870. {
  871. if ((this.val.split('"').length-1)>=2 && (this.val.indexOf(' ')>this.val.indexOf('"')))
  872. {
  873. var str=this.val.substring(0,this.val.indexOf('"',this.val.indexOf('"')+1)+1);
  874. this.val=this.val.substring(str.length+1);
  875. return str;
  876. }
  877. else if (this.val.indexOf(' ')>0)
  878. {
  879. var str=this.val.substring(0,this.val.indexOf(' '));
  880. this.val=this.val.substring(str.length+1);
  881. return str;
  882. }
  883. else
  884. {
  885. var str=this.val;this.val='';return str;
  886. }
  887. }
  888. }
  889. String2.prototype.gulpSymbol=function(str){return this.gulp(str,true);}//like gulp, but ignores spaces
  890. String2.prototype.gulpUntil=function(str,isSymbol,toEnd)
  891. {
  892. //gulp all words until str (str is not included in the result, and removed from the String2)
  893. //if str is unspecified (or toEnd is true and str wasn't found), just return the rest of the string
  894. var bumpStr=str;
  895. var paddedBumpStr=bumpStr;
  896. if (!isSymbol) paddedBumpStr=' '+paddedBumpStr;
  897. if (!str || (toEnd && this.val.indexOf(paddedBumpStr)==-1))
  898. {
  899. var str=this.val;
  900. this.val='';
  901. return str;
  902. }
  903. else if (this.val.indexOf(paddedBumpStr)>=0)
  904. {
  905. var str=this.val.substring(0,this.val.indexOf(paddedBumpStr));
  906. if (isSymbol) this.val=this.val.substring(str.length+bumpStr.length);
  907. else this.val=this.val.substring(str.length+1+bumpStr.length);
  908. if (this.val.charAt(0)==' ') this.val=this.val.substring(1);//remove first space
  909. if (this.val.charAt(this.val.length-1)==' ') this.val=this.val.slice(0,-1);//remove last space
  910. return str;
  911. }
  912. else return false;
  913. }
  914. String2.prototype.gulpUntilSymbol=function(str,toEnd){return this.gulpUntil(str,true,toEnd);}//like gulpUntil, but ignores spaces
  915. var STR2=function(str)//shortcut
  916. {return new String2(str);}
  917.  
  918. console.log("string prototypes added");
  919. //the old Beautify function from Cookie Clicker, shortened to B(value)
  920. //initially adapted from http://cookieclicker.wikia.com/wiki/Frozen_Cookies_%28JavaScript_Add-on%29
  921. function formatEveryThirdPower(notations)
  922. {
  923. return function (value)
  924. {
  925. var base = 0,
  926. notationValue = '';
  927. if (!isFinite(value)) return 'Inf.';
  928. if (value >= 1000)
  929. {
  930. value /= 1000;
  931. while(Math.round(value) >= 1000)
  932. {
  933. value /= 1000;
  934. base++;
  935. }
  936. if (base >= notations.length) {return 'Inf.';} else {notationValue = notations[base];}
  937. }
  938. return ( Math.round(value * 10) / 10 ) + notationValue;
  939. };
  940. }
  941.  
  942. function rawFormatter(value) {return Math.round(value * 1000) / 1000;}
  943.  
  944. var formatLong=[' thousand',' million',' billion',' trillion',' quadrillion',' quintillion',' sextillion',' septillion',' octillion',' nonillion'];
  945. var prefixes=['','un','duo','tre','quattuor','quin','sex','septen','octo','novem'];
  946. var suffixes=['decillion','vigintillion','trigintillion','quadragintillion','quinquagintillion','sexagintillion','septuagintillion','octogintillion','nonagintillion'];
  947. for (var i in suffixes)
  948. {
  949. for (var ii in prefixes)
  950. {
  951. formatLong.push(' '+prefixes[ii]+suffixes[i]);
  952. }
  953. }
  954.  
  955. var formatShort=['k','M','B','T','Qa','Qi','Sx','Sp','Oc','No'];
  956. var prefixes=['','Un','Do','Tr','Qa','Qi','Sx','Sp','Oc','No'];
  957. var suffixes=['D','V','T','Qa','Qi','Sx','Sp','O','N'];
  958. for (var i in suffixes)
  959. {
  960. for (var ii in prefixes)
  961. {
  962. formatShort.push(' '+prefixes[ii]+suffixes[i]);
  963. }
  964. }
  965. formatShort[10]='Dc';
  966.  
  967.  
  968. var numberFormatters =
  969. [
  970. formatEveryThirdPower(formatShort),
  971. formatEveryThirdPower(formatLong),
  972. rawFormatter
  973. ];
  974. var numberFormatter=numberFormatters[2];
  975. console.log("beautify prepped...");
  976. function Beautify(value,floats)
  977. {
  978. var negative=(value<0);
  979. var decimal='';
  980. var fixed=value.toFixed(floats);
  981. if (Math.abs(value)<1000 && floats>0 && Math.floor(fixed)!=fixed) decimal='.'+(fixed.toString()).split('.')[1];
  982. value=Math.floor(Math.abs(value));
  983. if (floats>0 && fixed==value+1) value++;
  984. var formatter=numberFormatter;
  985. var output=formatter(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g,',');
  986. if (output=='0') negative=false;
  987. return negative?'-'+output:output+decimal;
  988. }
  989. var B=Beautify;
  990. console.log("beautify made");
  991.  
  992. function BeautifyTime(value)
  993. {
  994. //value should be in seconds
  995. value=Math.max(Math.ceil(value,0));
  996. var years=Math.floor(value/31536000);
  997. value-=years*31536000;
  998. var days=Math.floor(value/86400);
  999. value-=days*86400;
  1000. var hours=Math.floor(value/3600)%24;
  1001. value-=hours*3600;
  1002. var minutes=Math.floor(value/60)%60;
  1003. value-=minutes*60;
  1004. var seconds=Math.floor(value)%60;
  1005. var str='';
  1006. if (years) str+=B(years)+'Y';
  1007. if (days || str!='') str+=B(days)+'d';
  1008. if (hours || str!='') str+=hours+'h';
  1009. if (minutes || str!='') str+=minutes+'m';
  1010. if (seconds || str!='') str+=seconds+'s';
  1011. if (str=='') str+='0s';
  1012. return str;
  1013. }
  1014. var BT=BeautifyTime;
  1015.  
  1016.  
  1017. var sayTime=function(time,detail)
  1018. {
  1019. //time is a value where one second is equal to 1000.
  1020. //detail skips days when >1, hours when >2, minutes when >3 and seconds when >4.
  1021. //if detail is -1, output something like "3 hours, 9 minutes, 48 seconds"
  1022. if (time<=0) return '';
  1023. var str='';
  1024. var detail=detail||0;
  1025. time=Math.floor(time);
  1026. var second=1000;
  1027. if (detail==-1)
  1028. {
  1029. var days=0;
  1030. var hours=0;
  1031. var minutes=0;
  1032. var seconds=0;
  1033. if (time>=second*60*60*24) days=(Math.floor(time/(second*60*60*24)));
  1034. if (time>=second*60*60) hours=(Math.floor(time/(second*60*60)));
  1035. if (time>=second*60) minutes=(Math.floor(time/(second*60)));
  1036. if (time>=second) seconds=(Math.floor(time/(second)));
  1037. hours-=days*24;
  1038. minutes-=hours*60+days*24*60;
  1039. seconds-=minutes*60+hours*60*60+days*24*60*60;
  1040. if (days>10) {hours=0;}
  1041. if (days) {minutes=0;seconds=0;}
  1042. if (hours) {seconds=0;}
  1043. var bits=[];
  1044. if (days>0) bits.push(B(days)+' day'+(days==1?'':'s'));
  1045. if (hours>0) bits.push(B(hours)+' hour'+(hours==1?'':'s'));
  1046. if (minutes>0) bits.push(B(minutes)+' minute'+(minutes==1?'':'s'));
  1047. if (seconds>0) bits.push(B(seconds)+' second'+(seconds==1?'':'s'));
  1048. if (bits.length==0) str='less than 1 second';
  1049. else str=bits.join(', ');
  1050. }
  1051. else
  1052. {
  1053. if (time>=second*60*60*24*2 && detail<2) str=B(Math.floor(time/(second*60*60*24)))+' days';
  1054. else if (time>=second*60*60*24 && detail<2) str='1 day';
  1055. else if (time>=second*60*60*2 && detail<3) str=B(Math.floor(time/(second*60*60)))+' hours';
  1056. else if (time>=second*60*60 && detail<3) str='1 hour';
  1057. else if (time>=second*60*2 && detail<4) str=B(Math.floor(time/(second*60)))+' minutes';
  1058. else if (time>=second*60 && detail<4) str='1 minute';
  1059. else if (time>=second*2 && detail<5) str=B(Math.floor(time/(second)))+' seconds';
  1060. else if (time>=second && detail<5) str='1 second';
  1061. else str='less than 1 second';
  1062. }
  1063. return str;
  1064. }
  1065. console.log("time beautifier done");
  1066.  
  1067. function cap(str)
  1068. {return str.charAt(0).toUpperCase()+str.slice(1);}
  1069.  
  1070. console.log("building save function");
  1071. //file save function from https://github.com/eligrey/FileSaver.js
  1072. var saveAs=saveAs||function(view){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var doc=view.document,get_URL=function(){return view.URL||view.webkitURL||view},save_link=doc.createElementNS("http://www.w3.org/1999/xhtml","a"),can_use_save_link="download"in save_link,click=function(node){var event=new MouseEvent("click");node.dispatchEvent(event)},is_safari=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),webkit_req_fs=view.webkitRequestFileSystem,req_fs=view.requestFileSystem||webkit_req_fs||view.mozRequestFileSystem,throw_outside=function(ex){(view.setImmediate||view.setTimeout)(function(){throw ex},0)},force_saveable_type="application/octet-stream",fs_min_size=0,arbitrary_revoke_timeout=500,revoke=function(file){var revoker=function(){if(typeof file==="string"){get_URL().revokeObjectURL(file)}else{file.remove()}};if(view.chrome){revoker()}else{setTimeout(revoker,arbitrary_revoke_timeout)}},dispatch=function(filesaver,event_types,event){event_types=[].concat(event_types);var i=event_types.length;while(i--){var listener=filesaver["on"+event_types[i]];if(typeof listener==="function"){try{listener.call(filesaver,event||filesaver)}catch(ex){throw_outside(ex)}}}},auto_bom=function(blob){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)){return new Blob(["\ufeff",blob],{type:blob.type})}return blob},FileSaver=function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}var filesaver=this,type=blob.type,blob_changed=false,object_url,target_view,dispatch_all=function(){dispatch(filesaver,"writestart progress write writeend".split(" "))},fs_error=function(){if(target_view&&is_safari&&typeof FileReader!=="undefined"){var reader=new FileReader;reader.onloadend=function(){var base64Data=reader.result;target_view.location.href="data:attachment/file"+base64Data.slice(base64Data.search(/[,;]/));filesaver.readyState=filesaver.DONE;dispatch_all()};reader.readAsDataURL(blob);filesaver.readyState=filesaver.INIT;return}if(blob_changed||!object_url){object_url=get_URL().createObjectURL(blob)}if(target_view){target_view.location.href=object_url}else{var new_tab=view.open(object_url,"_blank");if(new_tab==undefined&&is_safari){view.location.href=object_url}}filesaver.readyState=filesaver.DONE;dispatch_all();revoke(object_url)},abortable=function(func){return function(){if(filesaver.readyState!==filesaver.DONE){return func.apply(this,arguments)}}},create_if_not_found={create:true,exclusive:false},slice;filesaver.readyState=filesaver.INIT;if(!name){name="download"}if(can_use_save_link){object_url=get_URL().createObjectURL(blob);setTimeout(function(){save_link.href=object_url;save_link.download=name;click(save_link);dispatch_all();revoke(object_url);filesaver.readyState=filesaver.DONE});return}if(view.chrome&&type&&type!==force_saveable_type){slice=blob.slice||blob.webkitSlice;blob=slice.call(blob,0,blob.size,force_saveable_type);blob_changed=true}if(webkit_req_fs&&name!=="download"){name+=".download"}if(type===force_saveable_type||webkit_req_fs){target_view=view}if(!req_fs){fs_error();return}fs_min_size+=blob.size;req_fs(view.TEMPORARY,fs_min_size,abortable(function(fs){fs.root.getDirectory("saved",create_if_not_found,abortable(function(dir){var save=function(){dir.getFile(name,create_if_not_found,abortable(function(file){file.createWriter(abortable(function(writer){writer.onwriteend=function(event){target_view.location.href=file.toURL();filesaver.readyState=filesaver.DONE;dispatch(filesaver,"writeend",event);revoke(file)};writer.onerror=function(){var error=writer.error;if(error.code!==error.ABORT_ERR){fs_error()}};"writestart progress write abort".split(" ").forEach(function(event){writer["on"+event]=filesaver["on"+event]});writer.write(blob);filesaver.abort=function(){writer.abort();filesaver.readyState=filesaver.DONE};filesaver.readyState=filesaver.WRITING}),fs_error)}),fs_error)};dir.getFile(name,{create:false},abortable(function(file){file.remove();save()}),abortable(function(ex){if(ex.code===ex.NOT_FOUND_ERR){save()}else{fs_error()}}))}),fs_error)}),fs_error)},FS_proto=FileSaver.prototype,saveAs=function(blob,name,no_auto_bom){return new FileSaver(blob,name,no_auto_bom)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(blob,name,no_auto_bom){if(!no_auto_bom){blob=auto_bom(blob)}return navigator.msSaveOrOpenBlob(blob,name||"download")}}FS_proto.abort=function(){var filesaver=this;filesaver.readyState=filesaver.DONE;dispatch(filesaver,"abort")};FS_proto.readyState=FS_proto.INIT=0;FS_proto.WRITING=1;FS_proto.DONE=2;FS_proto.error=FS_proto.onwritestart=FS_proto.onprogress=FS_proto.onwrite=FS_proto.onabort=FS_proto.onerror=FS_proto.onwriteend=null;return saveAs}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})}
  1073.  
  1074.  
  1075.  
  1076. /*=====================================================================================
  1077. ENGINE
  1078. =======================================================================================*/
  1079.  
  1080. var TOPARSE=TOPARSE||false;
  1081.  
  1082. console.log("helper functions made!");
  1083.  
  1084. G.Init=function()
  1085. {
  1086. console.log("stabilizijg...");
  1087. G.stabilizeResize=function()
  1088. {
  1089. }
  1090. console.log("intiializimg the game itself");
  1091.  
  1092. /*=====================================================================================
  1093. SETTINGS
  1094. =======================================================================================*/
  1095. G.settings={
  1096. 'vol':{val:100},
  1097. 'autosave':{val:1},
  1098. 'numdisp':{val:0,onchange:function(val){
  1099. val=parseInt(val);
  1100. if (val>=0 && val<=2) numberFormatter=numberFormatters[val];
  1101. }},
  1102. 'cssfilts':{val:1,onchange:function(val){
  1103. if (val)
  1104. {
  1105. G.l.classList.remove('filtersOff');
  1106. G.l.classList.add('filtersOn');
  1107. }
  1108. else
  1109. {
  1110. G.l.classList.remove('filtersOn');
  1111. G.l.classList.add('filtersOff');
  1112. }
  1113. }},
  1114. 'particles':{val:2},
  1115. 'showFPS':{val:G.local?1:0,onchange:function(val){
  1116. if (val)
  1117. {
  1118. G.fpsGraph.style.display='block';
  1119. G.fpsCounter.style.display='block';
  1120. }
  1121. else
  1122. {
  1123. G.fpsGraph.style.display='none';
  1124. G.fpsCounter.style.display='none';
  1125. }
  1126. }},
  1127. };
  1128. console.log("settijg preset up");
  1129. for (var i in G.settings){G.settings[i].key=i;}
  1130. G.setSetting=function(what,val)
  1131. {
  1132. G.settings[what].val=val;
  1133. if (G.settings[what].onchange) G.settings[what].onchange(G.settings[what].val);
  1134. }
  1135. G.getSetting=function(what)
  1136. {
  1137. return G.settings[what].val;
  1138. }
  1139. G.loadSettings=function()
  1140. {
  1141. for (var i in G.settings)
  1142. {
  1143. if (G.settings[i].onchange) G.settings[i].onchange(G.settings[i].val);
  1144. }
  1145. }
  1146.  
  1147. G.makeChoices=function(o)
  1148. {
  1149. var str='';
  1150. var buttonIds=[];
  1151. for (var i in o.list)
  1152. {
  1153. buttonIds.push('button-'+(G.buttonsN+parseInt(i)));
  1154. }
  1155. for (var i in o.list)
  1156. {
  1157. var id=parseInt(i);
  1158. str+=G.button({
  1159. text:o.list[i].text,
  1160. classes:'tickbox '+(o.val()==id?'on':'off'),
  1161. tooltip:o.list[i].tooltip,
  1162. onclick:function(e){
  1163. for (var i in buttonIds)
  1164. {
  1165. l(buttonIds[i]).classList.remove('on');
  1166. l(buttonIds[i]).classList.add('off');
  1167. if (e.target.id==buttonIds[i])
  1168. {
  1169. var id=parseInt(i);
  1170. o.func(id);
  1171. }
  1172. }
  1173. e.target.classList.remove('off');
  1174. e.target.classList.add('on');
  1175. triggerAnim(e.target,'glow');
  1176. },
  1177. });
  1178. }
  1179. return str;
  1180. }
  1181. console.log("setting menu functions ready for later");
  1182.  
  1183. G.makeTick=function(o)
  1184. {
  1185. if (!o.off) o.off=o.on;
  1186. return G.button({
  1187. text:(o.val()?o.on:o.off),
  1188. classes:'tickbox '+(o.val()?'on':'off'),
  1189. tooltip:o.tooltip,
  1190. onclick:function(e){
  1191. if (o.val()) o.func(0); else o.func(1);
  1192. if (o.val())
  1193. {
  1194. e.target.classList.remove('off');
  1195. e.target.classList.add('on');
  1196. e.target.innerHTML=o.on;
  1197. }
  1198. else
  1199. {
  1200. e.target.classList.remove('on');
  1201. e.target.classList.add('off');
  1202. e.target.innerHTML=o.off;
  1203. }
  1204. triggerAnim(e.target,'glow');
  1205. },
  1206. });
  1207. }
  1208. console.log("game 'tick' function made");
  1209.  
  1210. /*=====================================================================================
  1211. SAVE & LOAD
  1212. =======================================================================================*/
  1213.  
  1214. G.saveTo=0;//we save the game to the key "IGM-"+game's url
  1215. G.save=function(returnOnly)
  1216. {
  1217. if (!G.saveTo) return false;
  1218. G.doEffectsForAll('save');
  1219. G.doEffectsForAll('undo grants');
  1220. var str='';
  1221. str+='BEGIN|';
  1222. var list=[];
  1223. list.push('1');//engine version
  1224. list.push(G.startDate);
  1225. list.push(parseInt(Date.now()));//time last played
  1226. str+=list.join(';');
  1227. str+='|SET|';
  1228. var list=[];
  1229. for (var i in G.settings)
  1230. {
  1231. var me=G.settings[i];
  1232. list.push(me.key+','+parseInt(me.val));
  1233. }
  1234. str+=list.join(';');
  1235. str+='|RES|';
  1236. var list=[];
  1237. for (var i in G.res)
  1238. {
  1239. var me=G.res[i];
  1240. list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.amount+','+me.maxAmount+','+me.earned);
  1241. }
  1242. str+=list.join(';');
  1243. str+='|BTN|';
  1244. var list=[];
  1245. for (var i in G.buttons)
  1246. {
  1247. var me=G.buttons[i];
  1248. list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.clicks);
  1249. }
  1250. str+=list.join(';');
  1251. str+='|BLD|';
  1252. var list=[];
  1253. for (var i in G.buildings)
  1254. {
  1255. var me=G.buildings[i];
  1256. list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.amount+','+me.maxAmount);
  1257. }
  1258. str+=list.join(';');
  1259. str+='|UPG|';
  1260. var list=[];
  1261. for (var i in G.upgrades)
  1262. {
  1263. var me=G.upgrades[i];
  1264. list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.owned);
  1265. }
  1266. str+=list.join(';');
  1267. str+='|ACH|';
  1268. var list=[];
  1269. for (var i in G.achievs)
  1270. {
  1271. var me=G.achievs[i];
  1272. list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.owned);
  1273. }
  1274. str+=list.join(';');
  1275. str+='|ITM|';
  1276. //group up items by base key
  1277. var itemsByBase=[];
  1278. for (var i in G.items)
  1279. {
  1280. if (!itemsByBase[G.items[i].base.key]) itemsByBase[G.items[i].base.key]=[];
  1281. itemsByBase[G.items[i].base.key].push(G.items[i]);
  1282. }
  1283. var list=[];
  1284. for (var i in itemsByBase)
  1285. {
  1286. var list2=[];
  1287. for (var ii in itemsByBase[i])
  1288. {
  1289. var me=itemsByBase[i][ii];
  1290. list2.push(((me.show<<1)|(me.lit)));
  1291. }
  1292. list.push(i+'/'+list2.join('/'));
  1293. }
  1294. str+=list.join(';');
  1295. str+='|SHN|';
  1296. var list=[];
  1297. for (var i in G.shinies)
  1298. {
  1299. var me=G.shinies[i];
  1300. list.push(me.key+','+me.clicks);
  1301. }
  1302. str+=list.join(';');
  1303. str+='|END';
  1304. G.doEffectsForAll('do grants');
  1305. str=window.btoa(str);
  1306. if (returnOnly) return str;
  1307. localStorage[G.saveTo]=str;
  1308. if (localStorage[G.saveTo]!=str) return false;
  1309. G.toast({text:'Game saved',classes:'center',dur:3});
  1310. return true;
  1311. }
  1312. console.log("save to function made");
  1313. G.saveData=0;
  1314. G.applyLoad=function(data)
  1315. {
  1316. if (!G.saveData) return false;
  1317. var str=data||G.saveData;
  1318. try{str=window.atob(str);}catch(err){return false;}
  1319. G.saveData=0;
  1320. str=str.split('|');
  1321. if (str[0]!='BEGIN' || str[str.length-1]!='END') return false;
  1322. var blocks=[];
  1323. var blockNames=['BEGIN','SET','RES','BTN','BLD','UPG','ACH','ITM'];
  1324. for (var i in str)
  1325. {
  1326. if (i>0)
  1327. {
  1328. if (blockNames.indexOf(str[i])==-1 && blockNames.indexOf(str[i-1])!=-1) blocks[str[i-1]]=str[i].split(';');
  1329. }
  1330. }
  1331. for (var block in blocks)
  1332. {
  1333. if (block=='ITM')
  1334. {
  1335. for (var subblock in blocks[block])
  1336. {
  1337. var things=blocks[block][subblock].split('/');
  1338. var key=things[0];
  1339. if (G.thingsByName[key])
  1340. {
  1341. var base=G.thingsByName[key];
  1342. things.shift();
  1343. for (var thing in things)
  1344. {
  1345. var bits=things[thing].split(',');
  1346. var i=0;
  1347. var me={};
  1348. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1349. G.gainItem(base,me);
  1350. }
  1351. }
  1352. }
  1353. }
  1354. else if (block=='BEGIN')
  1355. {
  1356. var i=0;
  1357. var bits=blocks[block];
  1358. G.saveUsedEngineVersion=parseInt(bits[i++]);
  1359. G.startDate=parseInt(bits[i++]);
  1360. G.lastDate=parseInt(bits[i++]);
  1361. }
  1362. else
  1363. {
  1364. for (var thing in blocks[block])
  1365. {
  1366. var bits=blocks[block][thing].split(',');
  1367. var i=0;
  1368. var key=bits[i++];
  1369. if (block=='SET' && G.settings[key])
  1370. {
  1371. var me=G.settings[key];
  1372. G.settings[key].val=parseInt(bits[i++]);
  1373. }
  1374. else if (G.thingsByName[key])
  1375. {
  1376. var me=G.thingsByName[key];
  1377. if (block=='RES')
  1378. {
  1379. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1380. me.amount=parseFloat(bits[i++]);
  1381. me.maxAmount=parseFloat(bits[i++]);
  1382. me.earned=parseFloat(bits[i++]);
  1383. }
  1384. else if (block=='BTN')
  1385. {
  1386. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1387. me.clicks=parseInt(bits[i++]);
  1388. }
  1389. else if (block=='BLD')
  1390. {
  1391. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1392. me.amount=parseFloat(bits[i++]);
  1393. me.maxAmount=parseFloat(bits[i++]);
  1394. }
  1395. else if (block=='UPG')
  1396. {
  1397. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1398. me.owned=parseInt(bits[i++]);
  1399. }
  1400. else if (block=='ACH')
  1401. {
  1402. var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
  1403. me.owned=parseInt(bits[i++]);
  1404. }
  1405. else if (block=='SHN')
  1406. {
  1407. me.clicks=parseInt(bits[i++]);
  1408. }
  1409. }
  1410. }
  1411. }
  1412. }
  1413. G.toast({text:'Game loaded',classes:'center',dur:3});
  1414. return true;
  1415. }
  1416. console.log("1st save loading function made");
  1417. G.load=function(data)
  1418. {
  1419. //reset the game and get the save data
  1420. //(the save data is parsed inside G.parse using G.applyLoad)
  1421. //the data parameter lets us load arbitrary save data; if none is specified, use localStorage instead
  1422. G.turnOff();
  1423. G.saveData=0;
  1424. if (!data && G.saveTo) var data=localStorage[G.saveTo];
  1425. if (!data) data=0;
  1426. G.saveData=data;
  1427. try {
  1428. G.Reset(-1);
  1429. var done=G.parse(G.data);
  1430. }
  1431. catch(e)
  1432. {
  1433. G.context='(javascript)';
  1434. console.log(e);
  1435. G.parseError(e.message);
  1436. return false;
  1437. }
  1438. if (done && !G.parsedOnce)
  1439. {
  1440. //TODO : remove stylesheets first
  1441. //load stylesheets
  1442. /*
  1443. G.stylesheetUrls=G.stylesheets.slice(0);
  1444. G.stylesheets=[];
  1445. G.stylesheetsToLoad=0;
  1446. var onCompletion=function()
  1447. {
  1448. G.stylesheetsToLoad--;
  1449. if (G.stylesheetsToLoad<=0)
  1450. {
  1451. G.turnOn();
  1452. }
  1453. }
  1454. var onBadCompletion=function()
  1455. {
  1456. G.stylesheetsToLoad--;
  1457. if (G.stylesheetsToLoad<=0)
  1458. {
  1459. //TODO : display a warning message; perhaps a confirm prompt
  1460. setTimeout(G.turnOn,1000);
  1461. }
  1462. }
  1463. if (G.stylesheetUrls.length==0) G.stylesheetUrls.push('stuff/basic.css');
  1464. for (var i in G.stylesheetUrls)
  1465. {
  1466. var me={url:G.stylesheetUrls[i],loaded:false};
  1467. G.stylesheets.push(me);
  1468. G.stylesheetsToLoad++;
  1469. }
  1470. for (var i in G.stylesheets)
  1471. {
  1472. var me=G.stylesheets[i];
  1473. if (me.url.endsWith('.css'))
  1474. {
  1475. var link=document.createElement('link');
  1476. link.type='text/css';
  1477. link.rel='stylesheet';
  1478. link.href=me.url;
  1479. link.onload=function(me){return function(){me.loaded=true;onCompletion();}}(me);
  1480. link.onerror=function(me){return function(){console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');onBadCompletion();}}(me);
  1481. document.head.appendChild(link);
  1482. }
  1483. else if (!G.local)
  1484. {
  1485. ajax('server.php?q=fetch|'+me.url,function(me){return function(out){
  1486. if (out=='[NONE]')
  1487. {
  1488. console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');
  1489. onBadCompletion();
  1490. return false;
  1491. }
  1492. var css=document.createElement('style');
  1493. css.type='text/css';
  1494. css.rel='stylesheet';
  1495. css.innerHTML=out;
  1496. document.head.appendChild(css);
  1497. me.loaded=true;
  1498. onCompletion();
  1499. }}(me));
  1500. }
  1501. else
  1502. {
  1503. console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');
  1504. onBadCompletion();
  1505. }
  1506. }
  1507.  
  1508. if (G.css!='')
  1509. {
  1510. var css=document.createElement('style');
  1511. css.type='text/css';
  1512. css.innerHTML=G.css;
  1513. document.getElementsByTagName('head')[0].appendChild(css);
  1514. }
  1515. */
  1516. if (G.bloomFilter>0)
  1517. {
  1518. //disabled (problems in Firefox, slowdowns on complex games, all-around just kinda gaudy)
  1519. /*var bloom=document.createElementNS('http://www.w3.org/2000/svg','svg');
  1520. bloom.innerHTML=`
  1521. <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  1522. <defs>
  1523. <filter id="bloom" x="0" y="0" width="100%" height="100%">
  1524. <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
  1525. <feColorMatrix in="blur" type="matrix" values="
  1526. 1 0 0 0 0
  1527. 0 1 0 0 0
  1528. 0 0 1 0 0
  1529. 0 0 0 `+G.bloomFilter+` 0
  1530. " result="blur2" />
  1531. <feComposite result="mix" operator="atop" in2="SourceGraphic" in="blur2" />
  1532. <feBlend in2="blur2" in="mix" result="out" mode="overlay" />
  1533. </filter>
  1534. </defs>
  1535. </svg>
  1536. `;
  1537. document.head.appendChild(bloom);*/
  1538. }
  1539. G.parsedOnce=true;
  1540. }
  1541. else if (done)
  1542. {
  1543. setTimeout(G.turnOn,10);
  1544. }
  1545. G.loadSettings();
  1546. }
  1547. console.log("2nd save loading function made");
  1548. G.clear=function(data)
  1549. {
  1550. //completely get rid of the save data
  1551. if (G.saveTo) localStorage.removeItem(G.saveTo);
  1552. G.load();
  1553. G.loadSettings();
  1554. }
  1555.  
  1556. G.getSaveData=function(){return G.save(true);}
  1557. G.loadFromData=function(data){return G.load(data);}
  1558.  
  1559.  
  1560. G.fileSave=function()
  1561. {
  1562. var filename='IGM-'+(G.name.replace(/[^a-zA-Z0-9]+/g,'')||'game');
  1563. var text=G.getSaveData();
  1564. var blob=new Blob([text],{type:'text/plain;charset=utf-8'});
  1565. saveAs(blob,filename+'.txt');
  1566. G.toast({text:'File saved to '+filename+'.txt',classes:'center',dur:3});
  1567. }
  1568. G.fileLoad=function(e)
  1569. {
  1570. if (e.target.files.length==0) return false;
  1571. var file=e.target.files[0];
  1572. var reader=new FileReader();
  1573. reader.onload=function(e)
  1574. {
  1575. G.loadFromData(e.target.result);
  1576. }
  1577. reader.readAsText(file);
  1578. }
  1579. console.log("other save functions made");
  1580.  
  1581. /*=====================================================================================
  1582. GET GAME SOURCE
  1583. =======================================================================================*/
  1584.  
  1585. G.parsedOnce=false;//we've parsed the data at least once; this helps prevent declaring stylesheets more than once etc
  1586. G.playing=false;
  1587.  
  1588. G.dataLoaded=function(data)
  1589. {
  1590. console.log(TOPARSE);
  1591. //if (data=='[NONE]') {G.noData();return false;}
  1592. if (true) G.data=TOPARSE;
  1593. else if (data) G.data=data;
  1594. else {G.noData();return false;}
  1595. console.log('Got '+B(G.data.length)+' characters of data.');
  1596. parent.postMessage({code:G.data},'*');
  1597. G.load();
  1598. }
  1599.  
  1600. G.noData=function()
  1601. {
  1602. var str=G.url||'';
  1603. str=str.replaceAll('&','&amp;');
  1604. str=str.replaceAll('<','&lt;');
  1605. str=str.replaceAll('>','&gt;');
  1606. str=str.replaceAll('"','&quot;');
  1607. str=str.replaceAll('\'','&apos;');
  1608.  
  1609. G.l.innerHTML=`
  1610. <div id="errorWrap">
  1611. <div id="noGameData">
  1612. <b>Woops!</b><br>
  1613. The specified game data<br>
  1614. <small>(`+str+`)</small><br>
  1615. could not be found.<br>
  1616. <a href="./index.html" target="_top">Go back to homepage</a>
  1617. </div>
  1618. </div>
  1619. `;
  1620. G.l.classList.add('on');
  1621. }
  1622. console.log("basic data loaders set up");
  1623.  
  1624. if (true/*G.urlVars && G.urlVars.g*/)
  1625. {
  1626. //load data file
  1627. G.url="here";//G.urlVars.g;
  1628.  
  1629. //if (G.url.indexOf('/')==0) G.url='./games'+G.url+'.txt';
  1630. //else if (!G.local && G.url.indexOf('www.')!=0 && G.url.indexOf('http://')!=0 && G.url.indexOf('https://')!=0) G.url='http://pastebin.com/raw.php?i='+G.url;
  1631.  
  1632. console.log('Fetching game at '+G.url+'...');
  1633. G.saveTo=G.url;
  1634. if (TOPARSE) setTimeout(G.dataLoaded,100);
  1635. else if (G.local) LoadScript(G.url,G.dataLoaded,function(){G.noData();});
  1636. else ajax('server.php?q=fetch|'+G.url,G.dataLoaded);
  1637. }
  1638.  
  1639. else G.noData();
  1640.  
  1641.  
  1642. console.log("data fetched (or no?)");
  1643.  
  1644. /*=====================================================================================
  1645. RESET
  1646. =======================================================================================*/
  1647. G.Reset=function()
  1648. {
  1649. //G.Reset is called on save load.
  1650. //Most of the engine's code (perhaps more than necessary) is in here as we want to start as fresh as possible on each data load.
  1651.  
  1652. G.T=0;
  1653. G.domReady=false;
  1654. G.playing=false;
  1655.  
  1656. G.l.innerHTML=`
  1657. <div id="content"></div>
  1658. <div id="meta-buttons">
  1659. <div class="meta-button" id="meta-button-settings"></div>
  1660. <div class="meta-button" id="meta-button-info"></div>
  1661. </div>
  1662. <div id="shinies" class="shadowed"></div>
  1663. <div id="particles" class="shadowed"></div>
  1664. <div id="toasts" class="shadowed"></div>
  1665. <div id="popups"><div id="darken" class="off"></div></div>
  1666. <div id="tooltip">
  1667. <div id="tooltipPU" class="tooltipPoint"></div>
  1668. <div id="tooltipPD" class="tooltipPoint"></div>
  1669. <div id="tooltipPL" class="tooltipPoint"></div>
  1670. <div id="tooltipPR" class="tooltipPoint"></div>
  1671. <div id="tooltipContent"></div>
  1672. </div>
  1673. `;
  1674.  
  1675.  
  1676. /*=====================================================================================
  1677. LOG
  1678. =======================================================================================*/
  1679. G.initLog=function()
  1680. {
  1681. G.logL=l('log')||0;
  1682. G.logLin=0;
  1683. G.logs=[];
  1684. G.maxLogs=50;
  1685. if (G.logL)
  1686. {
  1687. G.logLin=l('logInner');
  1688. }
  1689. }
  1690. G.log=function(text,classes)
  1691. {
  1692. if (!G.logL) return false;
  1693. var scrolled=!(Math.abs(G.logL.scrollTop-(G.logL.scrollHeight-G.logL.offsetHeight))<3);
  1694. var me={};
  1695. var str='<div class="messageContent"><div>'+text+'</div></div>';
  1696. me.text=str;
  1697. var div=document.createElement('div');
  1698. div.innerHTML=str;
  1699. div.className='message popInVertical'+(classes?(' '+classes):'');
  1700. me.l=div;
  1701. G.logLin.appendChild(div);
  1702. G.logs.push(me);
  1703. if (G.logs.length>G.maxLogs)
  1704. {
  1705. var el=G.logLin.firstChild;
  1706. for (var i in G.logs)
  1707. {
  1708. if (G.logs[i].l==el)
  1709. {
  1710. G.logs.splice(i,1);
  1711. break;
  1712. }
  1713. }
  1714. G.logLin.removeChild(el);
  1715. }
  1716. if (!scrolled) G.logL.scrollTop=G.logL.scrollHeight-G.logL.offsetHeight;
  1717. G.addCallbacks();
  1718. }
  1719. G.clearLog=function()
  1720. {
  1721. G.logs=[];
  1722. G.logLin.innerHTML='';
  1723. }
  1724. G.initLog();
  1725.  
  1726.  
  1727. /*=====================================================================================
  1728. PARTICLES
  1729. =======================================================================================*/
  1730. G.particlesL=l('particles');
  1731. G.particlesN=50;
  1732. G.particlesI=0;
  1733. G.particles=[];
  1734. G.particlesReset=function()
  1735. {
  1736. var str='';
  1737. for (var i=0;i<G.particlesN;i++)
  1738. {
  1739. str+='<div id="particle-'+i+'" class="particle"><div id="particleText-'+i+'" class="particleText"></div></div>';
  1740. }
  1741. G.particlesL.innerHTML=str;
  1742.  
  1743. for (var i=0;i<G.particlesN;i++)
  1744. {
  1745. G.particles[i]={id:i,low:false,t:-1,x:0,y:0,xd:0,yd:0,l:l('particle-'+i),lt:l('particleText-'+i)};
  1746. }
  1747. }
  1748. G.particlesReset();
  1749.  
  1750. G.particleAt=function(el,icon,text)
  1751. {
  1752. if (G.getSetting('particles')==0) return false;
  1753. var me=G.particles[G.particlesI];
  1754. /*var box=el.getBoundingClientRect();
  1755. var x=box.left;
  1756. var y=box.top;
  1757. var w=box.right-x;
  1758. var h=box.bottom-y;
  1759. me.x=x+w*0.2+Math.random()*w*0.6-24;
  1760. me.y=y+h*0.2+Math.random()*h*0.6-24-48;*/
  1761. me.x=G.mouseX-24+(Math.random()*20-10);
  1762. me.y=G.mouseY-24-48+(Math.random()*20-10);
  1763. me.xd=Math.random()*8-4;
  1764. me.yd=-Math.random()*8-4;
  1765. me.r=Math.random()*90-45;
  1766. me.t=0;
  1767. if (text) me.lt.innerHTML=text;
  1768. me.baseCSS='display:block;'+G.resolveIcon(icon,true);
  1769. if (G.getSetting('particles')<3 && (G.currentFps<20 || G.getSetting('particles')==1))
  1770. {
  1771. //if low fps, trigger simple CSS animation instead
  1772. me.low=true;
  1773. me.l.style.cssText=me.baseCSS+'opacity:0;left:'+Math.floor(me.x)+'px;top:'+Math.floor(me.y)+'px;';
  1774. triggerAnim(me.l,'particlePop');
  1775. }
  1776. else {me.low=false;me.l.classList.remove('particlePop');}
  1777. G.particlesI++;
  1778. if (G.particlesI>=G.particlesN) G.particlesI=0;
  1779. }
  1780. G.particlesLogic=function()
  1781. {
  1782. for (var i=0;i<G.particlesN;i++)
  1783. {
  1784. var me=G.particles[i];
  1785. if (!me.low && me.t>=0)
  1786. {
  1787. var r=Math.pow(me.t/20,0.15);
  1788. var r2=Math.pow(me.t/20,5);
  1789. me.l.style.cssText=me.baseCSS+'opacity:'+(1-r2)+';transform:translate('+me.x+'px,'+me.y+'px) rotate('+(me.r*(1-r))+'deg) scale('+(0.5+0.5*r)+','+(1.5-0.5*r)+');transform-origin:50% 100%;';
  1790. me.x+=me.xd;
  1791. me.y+=me.yd;
  1792. me.xd*=0.95;
  1793. me.yd+=1;
  1794. me.yd=Math.min(me.yd,6);
  1795. me.t++;
  1796. if (me.t>20)
  1797. {
  1798. me.t=-1;
  1799. me.l.style.cssText='display:none;';
  1800. me.lt.innerHTML='';
  1801. }
  1802. }
  1803. }
  1804. }
  1805.  
  1806. /*=====================================================================================
  1807. POPUPS
  1808. =======================================================================================*/
  1809. G.popupsL=l('popups');
  1810. G.popups=[];
  1811. G.popup=function(el,o)
  1812. {
  1813. //TODO : handle el
  1814. var me={};
  1815. for (var i in o){me[i]=o[i];}
  1816. me.l=document.createElement('div');
  1817. var classes='popup';
  1818. if (me.classes) classes+=' '+me.classes;
  1819. me.l.className=classes;
  1820. me.l.innerHTML=(me.text||'');
  1821. if (me.init) me.init(me);
  1822. var buttonl=document.createElement('div');
  1823. buttonl.innerHTML='x';
  1824. buttonl.className='closeButton closesThePopup';
  1825. me.l.insertBefore(buttonl,me.l.firstChild);
  1826. var closers=me.l.getElementsByClassName('closesThePopup');
  1827. for (var i in closers)
  1828. {AddEvent(closers[i],'click',function(me){return function(){G.closePopup(me);}}(me));}
  1829. G.popupsL.appendChild(me.l);
  1830. G.popups.push(me);
  1831. G.addCallbacks();
  1832. return me;
  1833. }
  1834. G.closePopup=function(me)
  1835. {
  1836. if (!me) var me=G.popups[G.popups.length-1];
  1837. if (me.onClose) me.onClose(me);
  1838. G.popups.splice(G.popups.indexOf(me),1);
  1839. me.l.parentNode.removeChild(me.l);
  1840. }
  1841. G.popupDraw=function()
  1842. {
  1843. var topb=0;
  1844. var bottomb=G.h;
  1845. var leftb=0;
  1846. var rightb=G.w;
  1847. for (var i in G.popups)
  1848. {
  1849. var me=G.popups[i];
  1850. if (me.func) me.func(me);
  1851. me.l.style.left=Math.floor((rightb-leftb)/2-me.l.clientWidth/2)+'px';
  1852. me.l.style.top=Math.floor((bottomb-topb)/2-me.l.clientHeight/2)+'px';
  1853. me.l.style.opacity=1;
  1854. }
  1855. }
  1856.  
  1857. /*=====================================================================================
  1858. TOASTS
  1859. =======================================================================================*/
  1860. G.toastsL=l('toasts');
  1861. G.toasts=[];
  1862. G.toast=function(o)
  1863. {
  1864. var me={};
  1865. for (var i in o){me[i]=o[i];}
  1866. me.l=document.createElement('div');
  1867. var classes='toast popInVertical';
  1868. if (me.classes) classes+=' '+me.classes;
  1869. me.l.className=classes;
  1870. me.l.innerHTML=(me.text||'');
  1871. me.t=0;
  1872. me.toDie=0;
  1873. if (me.init) me.init(me);
  1874. var buttonl=document.createElement('div');
  1875. buttonl.innerHTML='x';
  1876. buttonl.className='closeButton closesThePopup';
  1877. me.l.insertBefore(buttonl,me.l.firstChild);
  1878. var closers=me.l.getElementsByClassName('closesThePopup');
  1879. for (var i in closers)
  1880. {AddEvent(closers[i],'click',function(me){return function(){G.closeToast(me);}}(me));}
  1881. G.toastsL.appendChild(me.l);
  1882. G.toasts.push(me);
  1883. G.addCallbacks();
  1884. return me;
  1885. }
  1886. G.closeToast=function(me)
  1887. {
  1888. if (!me) var me=G.toasts[G.toasts.length-1];
  1889. if (me.toDie) return false;
  1890. me.toDie=1;
  1891. me.l.classList.remove('popInVertical');
  1892. me.l.classList.add('popOutVertical');
  1893. if (me.onClose) me.onClose(me);
  1894. }
  1895. G.killToast=function(me)
  1896. {
  1897. if (!me) var me=G.toasts[G.toasts.length-1];
  1898. G.toasts.splice(G.toasts.indexOf(me),1);
  1899. me.l.parentNode.removeChild(me.l);
  1900. }
  1901. G.toastLogic=function()
  1902. {
  1903. for (var i in G.toasts)
  1904. {
  1905. var me=G.toasts[i];
  1906. if (me.toDie)
  1907. {
  1908. me.toDie++;
  1909. if (me.toDie>=G.fps*0.3) G.killToast(me);
  1910. }
  1911. else
  1912. {
  1913. me.t++;
  1914. if (me.dur>0 && me.t>=me.dur*G.fps) G.closeToast(me);
  1915. }
  1916. }
  1917. }
  1918.  
  1919. /*=====================================================================================
  1920. TOOLTIP
  1921. =======================================================================================*/
  1922. G.tooltipL=l('tooltip');
  1923. G.tooltipContentL=l('tooltipContent');
  1924. G.tooltipPU=l('tooltipPU');
  1925. G.tooltipPD=l('tooltipPD');
  1926. G.tooltipPL=l('tooltipPL');
  1927. G.tooltipPR=l('tooltipPR');
  1928. G.pseudoHover=new Event('pseudoHover');
  1929. G.tooltipReset=function()
  1930. {
  1931. G.tooltip={
  1932. parent:0,origin:0,classes:'',text:'',on:false,settled:false,t:0,
  1933. };
  1934. }
  1935. G.tooltipReset();
  1936. G.addTooltip=function(el,o)
  1937. {
  1938. AddEvent(el,'mouseover',function(el,o){return function(){
  1939. var settled=(el==G.tooltip.parent);
  1940. G.showTooltip(el,o);
  1941. if (settled) G.tooltip.settled=true;
  1942. }}(el,o));
  1943. AddEvent(el,'pseudoHover',function(el,o){return function(){
  1944. G.showTooltip(el,o);
  1945. }}(el,o));
  1946. AddEvent(el,'mouseout',function(el,o){return function(){
  1947. G.hideTooltip(el);
  1948. }}(el,o));
  1949. }
  1950. G.showTooltip=function(el,o)
  1951. {
  1952. G.tooltipReset();
  1953. var me=G.tooltip;
  1954. me.on=true;
  1955. for (var i in o){me[i]=o[i];}
  1956. me.parent=el;
  1957. if (!me.origin) me.origin='top';
  1958. }
  1959. G.hideTooltip=function(el)
  1960. {
  1961. var prev=G.tooltip.parent;
  1962. if (el==-1) G.tooltipReset();
  1963. else if (!el || el==prev)
  1964. {
  1965. G.tooltipReset();
  1966. if (!prev) G.tooltip.settled=true;
  1967. var underneath=document.elementFromPoint(G.mouseX,G.mouseY);
  1968. if (underneath && prev && underneath!=prev)
  1969. {
  1970. underneath.dispatchEvent(G.pseudoHover);
  1971. G.tooltip.settled=true;
  1972. }
  1973. }
  1974. }
  1975. G.tooltipDraw=function()
  1976. {
  1977. var me=G.tooltip;
  1978. if (me.on)
  1979. {
  1980. if (!me.parent || !document.body.contains(me.parent)) {G.hideTooltip();}
  1981. else
  1982. {
  1983. if (!me.settled)
  1984. {
  1985. if (me.classes) G.tooltipL.className=me.classes;
  1986. if (me.text) G.tooltipContentL.innerHTML=me.text;
  1987. G.tooltipL.style.opacity='0';
  1988. G.tooltipL.style.display='block';
  1989. G.tooltipL.classList.remove('stretchIn');
  1990. G.tooltipL.classList.remove('stretchInV');
  1991. }
  1992. if (me.func && me.t%10==0) G.tooltipContentL.innerHTML=me.func();
  1993.  
  1994. var div=me.parent;
  1995. var box=div.getBoundingClientRect();
  1996.  
  1997. var topb=0;
  1998. var bottomb=G.h;
  1999. var leftb=0;
  2000. var rightb=G.w;
  2001. var margin=8;
  2002. var tx=G.tooltipL.offsetLeft;
  2003. var ty=G.tooltipL.offsetTop;
  2004. var tw=G.tooltipL.clientWidth;
  2005. var th=G.tooltipL.clientHeight;
  2006. var x=0;
  2007. var y=0;
  2008. var i=0;
  2009. var origin=me.origin;
  2010.  
  2011. //try to fit within the screen
  2012. var spins=[];
  2013. if (origin=='top') spins=['top','bottom','right','left'];
  2014. else if (origin=='bottom') spins=['bottom','top','right','left'];
  2015. else if (origin=='left') spins=['left','right','top','bottom'];
  2016. else if (origin=='right') spins=['right','left','top','bottom'];
  2017.  
  2018. for (var i=0;i<4;i++)
  2019. {
  2020. var spin=spins[i];
  2021. origin=spin;
  2022. if (spin=='top')
  2023. {
  2024. x=(box.left+box.right)/2;
  2025. y=box.top;
  2026. x=x-tw/2;
  2027. y=y-th-margin;
  2028. x=Math.max(0,Math.min(G.w-tw,x));
  2029. }
  2030. else if (spin=='bottom')
  2031. {
  2032. x=(box.left+box.right)/2;
  2033. y=box.bottom;
  2034. x=x-tw/2;
  2035. y=y+margin;
  2036. x=Math.max(0,Math.min(G.w-tw,x));
  2037. }
  2038. else if (spin=='left')
  2039. {
  2040. x=box.left;
  2041. y=(box.top+box.bottom)/2;
  2042. x=x-tw-margin;
  2043. y=y-th/2;
  2044. y=Math.max(0,Math.min(G.h-th,y));
  2045. }
  2046. else if (spin=='right')
  2047. {
  2048. x=box.right;
  2049. y=(box.top+box.bottom)/2;
  2050. x=x+margin;
  2051. y=y-th/2;
  2052. y=Math.max(0,Math.min(G.h-th,y));
  2053. }
  2054. if (y>=topb && y+th<=bottomb && x>=leftb && x+tw<=rightb) break;
  2055. }
  2056.  
  2057. G.tooltipPU.style.display='none';
  2058. G.tooltipPD.style.display='none';
  2059. G.tooltipPL.style.display='none';
  2060. G.tooltipPR.style.display='none';
  2061. if (origin=='top')
  2062. {
  2063. G.tooltipPD.style.display='block';
  2064. G.tooltipPD.style.left=Math.floor((box.left+box.right)/2-x-6)+'px';
  2065. G.tooltipPD.style.bottom=Math.floor(-6)+'px';
  2066. }
  2067. else if (origin=='bottom')
  2068. {
  2069. G.tooltipPU.style.display='block';
  2070. G.tooltipPU.style.left=Math.floor((box.left+box.right)/2-x-6)+'px';
  2071. G.tooltipPU.style.top=Math.floor(-6)+'px';
  2072. }
  2073. else if (origin=='left')
  2074. {
  2075. G.tooltipPR.style.display='block';
  2076. G.tooltipPR.style.right=Math.floor(-6)+'px';
  2077. G.tooltipPR.style.top=Math.floor((box.top+box.bottom)/2-y-6)+'px';
  2078. }
  2079. else if (origin=='right')
  2080. {
  2081. G.tooltipPL.style.display='block';
  2082. G.tooltipPL.style.left=Math.floor(-6)+'px';
  2083. G.tooltipPL.style.top=Math.floor((box.top+box.bottom)/2-y-6)+'px';
  2084. }
  2085.  
  2086. if (!me.settled) triggerAnim(G.tooltipL,(origin=='top' || origin=='bottom')?'stretchIn':'stretchInV');
  2087. me.settled=true;
  2088.  
  2089. G.tooltipL.style.left=Math.floor(x)+'px';
  2090. G.tooltipL.style.top=Math.floor(y)+'px';
  2091. G.tooltipL.style.opacity='1';
  2092. me.t++;
  2093. }
  2094. }
  2095. else
  2096. {
  2097. if (!me.settled)
  2098. {
  2099. me.settled=true;
  2100. G.tooltipL.classList.remove('stretchIn');
  2101. G.tooltipL.classList.remove('stretchInV');
  2102. G.tooltipL.style.opacity='0';
  2103. triggerAnim(G.tooltipL,'fadeOutQuick');
  2104. //G.tooltipL.style.display='none';
  2105. //G.tooltipL.className='';
  2106. }
  2107. }
  2108. }
  2109.  
  2110. /*=====================================================================================
  2111. THINGS
  2112. =======================================================================================*/
  2113. G.thingsN=0;
  2114. G.things=[];//by id
  2115. G.thingsByName=[];//by name id
  2116. G.thingsByTag={};
  2117. G.res=[];//by id
  2118. G.buttons=[];//by id
  2119. G.buildings=[];//by id
  2120. G.upgrades=[];//by id
  2121. G.itemTypes=[];//by id
  2122. G.items=[];//by id
  2123. G.itemsN=0;
  2124. G.achievs=[];//by id
  2125. G.shinies=[];//by id
  2126. G.boxes=[];//by id
  2127.  
  2128. G.Thing=function(o)
  2129. {
  2130. if (o.ids) var ids=o.ids.substring(1,o.ids.length).split('|');
  2131. for (var i in ids)
  2132. {
  2133. ids[i]=G.makeSafe(ids[i]);
  2134. }
  2135. this.key='-';
  2136. if (ids) this.key=ids[0];
  2137. if (ids) this.name=ids[0]; else this.name='???';
  2138.  
  2139. this.tags=0;
  2140. this.classes='';
  2141. this.show=1;
  2142. this.lit=0;
  2143. this.alwaysHidden=0;//overrides .show
  2144.  
  2145. this.refreshText=true;//if true, change the text during Draw
  2146. this.toRefresh=1;//if 1, update visibility
  2147.  
  2148. if (G.baseThing)
  2149. {
  2150. for (var i in G.baseThing)
  2151. {
  2152. if (i=='effects')
  2153. {
  2154. if (!this[i]) this[i]={};
  2155. for (var ii in G.baseThing[i])
  2156. {
  2157. if (!this[i][ii]) this[i][ii]=[];
  2158. this[i][ii]=G.baseThing[i][ii];
  2159. }
  2160. }
  2161. else this[i]=G.baseThing[i];
  2162. }
  2163. }
  2164. for (var i in o)
  2165. {
  2166. if (i=='effects')
  2167. {
  2168. if (!this[i]) this[i]={};
  2169. for (var ii in o[i])
  2170. {
  2171. if (!this[i][ii]) this[i][ii]=[];
  2172. this[i][ii]=this[i][ii].concat(o[i][ii]);
  2173. }
  2174. }
  2175. else if (Array.isArray(o[i]) && this[i]) this[i]=this[i].concat(o[i]);
  2176. else this[i]=o[i];
  2177. }
  2178. if (!this.plural) this.plural=this.name;
  2179. if (this.type=='item') this.id='ITEM'+G.itemsN;
  2180. else this.id=G.thingsN;
  2181. this.ids=[];
  2182. if (ids) this.ids=ids;
  2183. if (this.tags) this.tags=this.tags.split(' '); else this.tags=[];
  2184. for (var i in this.tags)
  2185. {
  2186. this.tags[i]=G.makeSafe(this.tags[i]);
  2187. if (!G.thingsByTag[this.tags[i]]) G.thingsByTag[this.tags[i]]=[];
  2188. G.thingsByTag[this.tags[i]].push(this);
  2189. }
  2190.  
  2191. if (this.type=='res') G.res.push(this);
  2192. else if (this.type=='button') G.buttons.push(this);
  2193. else if (this.type=='building') G.buildings.push(this);
  2194. else if (this.type=='upgrade') G.upgrades.push(this);
  2195. else if (this.type=='itemType') G.itemTypes.push(this);
  2196. else if (this.type=='item') G.items.push(this);
  2197. else if (this.type=='achiev') G.achievs.push(this);
  2198. else if (this.type=='shiny') G.shinies.push(this);
  2199. else if (this.type=='box') G.boxes.push(this);
  2200. else {G.parseError('The type "'+this.type+'" is not recognized.');return {type:0};}
  2201.  
  2202. if (this.type!='item' && this.type!='box') G.things.push(this);
  2203. if (ids) {for (var i in ids){G.thingsByName[ids[i]]=this;}}
  2204. //console.log(this);
  2205. if (this.type=='item') G.itemsN++; else if (this.type!='box') G.thingsN++;
  2206. return this;
  2207. }
  2208. G.createThing=function(thing)
  2209. {
  2210. if (thing)
  2211. {
  2212. if (thing.ids=='*TEMPLATE') {G.baseThing=thing;}
  2213. else new G.Thing(thing);
  2214. }
  2215. }
  2216. G.copyEffects=function(effects)
  2217. {
  2218. //returns new effects by type cloned from the original; this lets us do have a Thing using the same effects as another
  2219. var out={};
  2220. for (var effectType in effects)
  2221. {
  2222. out[effectType]=[];
  2223. for (var i in effects[effectType])
  2224. {
  2225. var eff={};
  2226. for (var ii in effects[effectType][i])
  2227. {
  2228. eff[ii]=effects[effectType][i][ii];
  2229. }
  2230. if (eff.type=='if')
  2231. {
  2232. eff.effs=G.copyEffects(eff.effs);
  2233. }
  2234. out[effectType].push(eff);
  2235. }
  2236. }
  2237. return out;
  2238. }
  2239.  
  2240. G.shiniesE=[];
  2241. G.shiniesL=l('shinies');
  2242. G.shiniesN=0;
  2243. G.spawnShiny=function(type)
  2244. {
  2245. var fail=false;
  2246. var me={
  2247. type:type,
  2248. x:0,
  2249. y:0,
  2250. t:0,
  2251. tm:Math.max(0,G.fps*type.dur*type.durMult),
  2252. };
  2253. me.l=document.createElement('div');
  2254. var moves=me.type.moves;
  2255. if ('anywhere' in moves)
  2256. {
  2257. me.x=Math.random()*G.w;
  2258. me.y=Math.random()*(G.h);
  2259. }
  2260. else if ('onRight' in moves)
  2261. {
  2262. me.x=G.w;
  2263. me.y=Math.random()*(G.h);
  2264. }
  2265. else if ('onLeft' in moves)
  2266. {
  2267. me.x=0;
  2268. me.y=Math.random()*(G.h);
  2269. }
  2270. else if ('onBottom' in moves)
  2271. {
  2272. me.x=Math.random()*G.w;
  2273. me.y=(G.h);
  2274. }
  2275. else if ('onTop' in moves)
  2276. {
  2277. me.x=Math.random()*G.w;
  2278. me.y=0;
  2279. }
  2280. else if ('onMouse' in moves)
  2281. {
  2282. me.x=G.mouseX;
  2283. me.y=G.mouseY;
  2284. }
  2285. else if ('onThing' in moves || 'onBox' in moves)
  2286. {
  2287. if ('onThing' in moves) var it=moves['onThing'];
  2288. else var it=moves['onBox'];
  2289. if (it && it.l)
  2290. {
  2291. var box=it.l.getBoundingClientRect();
  2292. me.x=box.left+Math.random()*(box.right-box.left);
  2293. me.y=box.top+Math.random()*(box.bottom-box.top);
  2294. }
  2295. else fail=true;
  2296. }
  2297.  
  2298. me.a=0;
  2299. if ('randomAngle' in moves)
  2300. {
  2301. me.a=Math.random()*360;
  2302. }
  2303. me.d=0;
  2304. if ('moveRandom' in moves)
  2305. {
  2306. me.d=Math.random();
  2307. }
  2308.  
  2309. if (fail) return false;
  2310.  
  2311. var classes='thing shiny';
  2312. if (me.type.classes) classes+=' '+me.type.classes;
  2313. if (!me.type.noClick)
  2314. {
  2315. AddEvent(me.l,'click',function(me){return function(){
  2316. if (me.t<me.tm) {me.type.click();me.t=me.tm;}
  2317. }}(me));
  2318. }
  2319. var str='';
  2320. if (!me.type.icon) classes+=' noIcon';
  2321. else
  2322. {
  2323. var icon=G.resolveIcon(me.type.icon);
  2324. str+='<div class="thing-icon shiny-icon" style="'+icon+'"></div>';
  2325. }
  2326. if (me.type.noText || !me.type.customName) classes+=' noText';
  2327. else str+='<div class="thing-text shiny-text">'+me.type.name+'</div>';
  2328. me.l.innerHTML=str;
  2329. me.l.className=classes;
  2330. G.shiniesL.appendChild(me.l);
  2331. me.offx=-me.l.clientWidth/2;
  2332. me.offy=-me.l.clientHeight/2;
  2333. me.id=G.shiniesN;
  2334. G.shiniesN++;
  2335. G.shiniesE.push(me);
  2336. }
  2337. G.shiniesLogic=function()
  2338. {
  2339. var shinies=[];
  2340. for (var i in G.shiniesE)
  2341. {
  2342. var me=G.shiniesE[i];
  2343. me.t++;
  2344. if (me.t>=me.tm)
  2345. {
  2346. me.l.parentNode.removeChild(me.l);
  2347. }
  2348. else shinies.push(me);
  2349. }
  2350. G.shiniesE=shinies;
  2351. }
  2352. G.shiniesDraw=function()
  2353. {
  2354. for (var i in G.shiniesE)
  2355. {
  2356. var me=G.shiniesE[i];
  2357. var moves=me.type.moves;
  2358. var r=me.t/me.tm;
  2359. var x=me.x;
  2360. var y=me.y;
  2361. var o=1;
  2362. var a=me.a;
  2363. var s=1;
  2364. if ('fade' in moves)
  2365. {
  2366. if (r<0.15) o=r/0.15;
  2367. else if (r<0.85) o=1;
  2368. else o=1-(r-0.85)/0.15;
  2369. }
  2370. if ('grow' in moves) s*=r;
  2371. else if ('shrink' in moves) s*=1-r;
  2372. else if ('growShrink' in moves) s*=Math.sqrt(1-(1-r*2)*(1-r*2));
  2373. if ('wiggle' in moves)
  2374. {
  2375. a+=Math.sin(me.t*(moves['wiggle']||0.25)+me.id)*18;
  2376. }
  2377. if ('spinCW' in moves) a+=me.t*(moves['spinCW']||1);
  2378. else if ('spinCCW' in moves) a-=me.t*(moves['spinCCW']||1);
  2379. else if ('spinRandom' in moves) a+=me.t*(me.id%2==0?1:-1)*(moves['spinRandom']||1);
  2380. if ('pulse' in moves)
  2381. {
  2382. s*=1+0.05*Math.sin(me.t*(moves['pulse']||0.35)+me.id);
  2383. }
  2384. if ('followMouse' in moves)
  2385. {
  2386. x=G.mouseX;
  2387. y=G.mouseY;
  2388. }
  2389. else if ('followMouseSlow' in moves)
  2390. {
  2391. x+=(G.mouseX-x)*(moves['followMouseSlow']||0.1);
  2392. y+=((G.mouseY)-y)*(moves['followMouseSlow']||0.1);
  2393. me.x=x;
  2394. me.y=y;
  2395. }
  2396. else if ('moveRandom' in moves)
  2397. {
  2398. x+=Math.sin(me.d*Math.PI*2)*(moves['moveRandom']||3);
  2399. y+=Math.cos(me.d*Math.PI*2)*(moves['moveRandom']||3);
  2400. me.x=x;
  2401. me.y=y;
  2402. }
  2403. else if ('moveLeft' in moves) x=moves['moveLeft']||(me.x*(1-r));
  2404. else if ('moveRight' in moves) x=moves['moveRight']||(me.x+(G.w-me.x)*(r));
  2405. else if ('moveTop' in moves) y=moves['moveTop']||(me.y*(1-r));
  2406. else if ('moveBottom' in moves) y=moves['moveBottom']||(me.y+(G.h-me.y)*(r));
  2407. if ('bobVertical' in moves)
  2408. {
  2409. y+=Math.sin(me.t*(moves['bobVertical']||0.2)+me.id)*8;
  2410. }
  2411. if ('bobHorizontal' in moves)
  2412. {
  2413. x+=Math.cos(me.t*(moves['bobHorizontal']||0.2)+me.id)*8;
  2414. }
  2415. var sx=s;
  2416. var sy=s;
  2417. if ('bounce' in moves)
  2418. {
  2419. var bounce=me.t*(moves['bounce']||0.05)+me.id;
  2420. y-=Math.abs(Math.cos(bounce)*128)-64;
  2421. /*sx=1+Math.sin(bounce*2+0.3*(Math.PI*2))*0.2;
  2422. sy=1+Math.sin(bounce*2-0.2*(Math.PI*2))*0.2;
  2423. a+=Math.sin(bounce*2-0.15*(Math.PI*2))*18+12;*/
  2424. }
  2425. x+=me.offx;
  2426. y+=me.offy;
  2427. me.l.style.transform='translate('+(x)+'px,'+(y)+'px) rotate('+(a)+'deg) scale('+(sx)+','+(sy)+')';
  2428. me.l.style.opacity=o;
  2429. }
  2430. }
  2431.  
  2432. G.A=0;
  2433.  
  2434. G.itemsMax=100;//how many items can be owned maximum
  2435. G.itemsTotal=0;//how many items we currently have
  2436. G.gainItem=function(thing,o)
  2437. {
  2438. if (G.itemsTotal>=G.itemsMax) return false;
  2439. if (thing && thing.type=='itemType')
  2440. {
  2441. var inherit=['name','classes','costs','effects'];
  2442. var data={type:'item',base:thing};
  2443. for (var i in inherit)
  2444. {
  2445. data[inherit[i]]=thing[inherit[i]];
  2446. }
  2447. data.effects=G.copyEffects(data.effects);
  2448. for (var i in o)
  2449. {
  2450. data[i]=o[i];
  2451. }
  2452. var item=new G.Thing(data);
  2453. item.tags=thing.tags;
  2454. if (G.domReady) item.createDom();
  2455. G.itemsTotal++;
  2456. return item;
  2457. }
  2458. else console.log('Couldn\'t create the item : ',thing,o||'no extra parameters');
  2459. return false;
  2460. }
  2461. G.loseItem=function(thing)
  2462. {
  2463. if (thing && thing.type=='itemType')
  2464. {
  2465. var ret=false;
  2466. for (var i in G.items)
  2467. {
  2468. var item=G.items[i];
  2469. if (item.base==thing && !item.removed)
  2470. {
  2471. item.remove();
  2472. ret=item;
  2473. break;
  2474. }
  2475. }
  2476. return ret;
  2477. }
  2478. else if (thing && thing.type=='item')
  2479. {
  2480. thing.remove();
  2481. return thing;
  2482. }
  2483. else console.log('Couldn\'t lose the item : ',thing);
  2484. return false;
  2485. }
  2486. G.Thing.prototype.remove=function()
  2487. {
  2488. if (this.type=='item' && !this.removed)
  2489. {
  2490. this.removed=true;
  2491. this.doEffects('undo grants');
  2492. if (G.domReady) this.removeDom();
  2493. G.itemsTotal--;
  2494. G.itemsToRefresh=true;
  2495. }
  2496. }
  2497. G.itemsToRefresh=true;
  2498. G.refreshItems=function()
  2499. {
  2500. G.itemsToRefresh=false;
  2501. var arr=[];
  2502. for (var i in G.items)
  2503. {
  2504. var me=G.items[i];
  2505. if (!me.removed)
  2506. {
  2507. arr.push(me);
  2508. }
  2509. else
  2510. {
  2511. if (G.things.indexOf(me)!=-1) G.things.splice(G.things.indexOf(me),1);
  2512. }
  2513. }
  2514. G.items=arr;
  2515. }
  2516.  
  2517. G.Thing.prototype.tooltip=function()
  2518. {
  2519. var me=this;
  2520. var str='';
  2521. if (!me.noBuy && me.costs.length>0)//me.type=='res' || me.type=='building')
  2522. {
  2523. str+='<div class="costs">'+G.getCostsStr(me.getCosts())+'</div>';
  2524. }
  2525. if (me.icon) str+='<div class="thing-icon" style="'+G.resolveIcon(me.icon,true)+'"></div>';
  2526. if (me.name) str+='<div class="title">'+me.name+'</div>';
  2527. if ((me.type=='upgrade' || me.type=='achiev') && me.owned) str+='<div class="subtitle">(owned)</div>';
  2528. if (me.type=='res' || me.type=='building') str+='<div class="subtitle">(amount : '+B(me.amount)+')</div>';
  2529. if (me.showEarned) str+='<div class="subtitle">(total earned : '+B(me.earned)+')</div>';
  2530. if (me.showMax) str+='<div class="subtitle">(max : '+B(me.maxAmount)+')</div>';
  2531. if (me.showClicks) str+='<div class="subtitle">(clicks : '+B(me.clicks)+')</div>';
  2532. if (me.desc) str+='<div class="desc"><div>'+G.getTextValue(me.desc,me)+'</div></div>';
  2533. return str;
  2534. }
  2535.  
  2536. G.Thing.prototype.updateDisplayed=function()
  2537. {
  2538. var me=this;
  2539. var hide=false;
  2540. if (me.alwaysHidden || !me.show) hide=true;
  2541. else
  2542. {
  2543. if (me.hiddenWhen0)
  2544. {
  2545. if ((me.type=='building' || me.type=='res') && me.amount<=0) hide=true;
  2546. else if ((me.type=='upgrade' || me.type=='achiev') && me.owned<=0) hide=true;
  2547. }
  2548. if (!hide && me.reqFunc)
  2549. {
  2550. if ((me.type=='building' || me.type=='res') && !me.checkReqs()) hide=true;
  2551. else if ((me.type=='upgrade' || me.type=='achiev') && me.owned<=0 && !me.checkReqs()) hide=true;
  2552. }
  2553. }
  2554. me.displayed=hide?0:1;
  2555. }
  2556.  
  2557. G.Thing.prototype.createDom=function()
  2558. {
  2559. var me=this;
  2560. if (!me.alwaysHidden && me.type!='itemType')
  2561. {
  2562. //select which box we should put this in; first by tag, then by type
  2563. var box=0;
  2564. var category=0;
  2565. for (var i in me.tags)
  2566. {
  2567. if (G.thingPlacement[me.tags[i]]) {category=me.tags[i];box=G.thingPlacement[category];break;}
  2568. }
  2569. if (!box)
  2570. {
  2571. if (me.type=='res') category='Resources';
  2572. else if (me.type=='button') category='Buttons';
  2573. else if (me.type=='building') category='Buildings';
  2574. else if (me.type=='upgrade') category='Upgrades';
  2575. else if (me.type=='achiev') category='Achievements';
  2576. else if (me.type=='item') category='Items';
  2577. if (category && G.thingPlacement[category])
  2578. {
  2579. box=G.thingPlacement[category];
  2580. }
  2581. }
  2582. if (box && box.type && box.type=='box')
  2583. {
  2584. me.box=box;
  2585. var classes='thing '+me.type;
  2586. if (me.classes) classes+=' '+me.classes;
  2587. if (false && me.show) classes+=' visible'; else classes+=' hidden';
  2588. if (me.lit) classes+=' lit'; else classes+=' dim';
  2589. if (!me.icon) classes+=' noIcon';
  2590. if (me.noText) classes+=' noText';
  2591. if (me.tags) classes+=' tag-'+me.tags.join(' tag-');
  2592. var iconClasses='thing-icon';
  2593. if (me.iconClasses) iconClasses+=' '+me.iconClasses;
  2594. var icon=G.resolveIcon(me.icon);
  2595. var div=me.box.childrenl[category];
  2596. var str='';
  2597. str+='<div id="thing-'+me.id+'" class="'+classes+'">';
  2598. if (me.box.showIcons && me.icon) str+='<div id="thing-icon-'+me.id+'" class="'+iconClasses+'" style="'+icon+'"></div>';
  2599. if (me.text && !me.noText) str+='<div id="thing-text-'+me.id+'" class="thing-text">'+G.getTextValue(me.text,me)+'</div>';
  2600. else if ((me.box.showNames && !me.noText) || !me.icon) str+='<div id="thing-text-'+me.id+'" class="thing-text">'+me.name+'</div>';
  2601. if (me.box.showCosts && !me.noBuy && me.costs.length>0 && !me.noText) str+='<div id="thing-costs-'+me.id+'" class="thing-costs"></div>';
  2602. str+='</div>';
  2603. div.appendChild(document.createRange().createContextualFragment(str));
  2604.  
  2605. me.l=l('thing-'+me.id)||0;
  2606. me.iconl=l('thing-icon-'+me.id)||0;
  2607. me.textl=l('thing-text-'+me.id)||0;
  2608. me.costsl=l('thing-costs-'+me.id)||0;
  2609. if (me.l)
  2610. {
  2611. AddEvent(me.l,'click',function(me){return function(){me.click();}}(me));
  2612. if (me.tooltip && !me.noTooltip && !me.box.noTooltip)
  2613. {
  2614. var obj={func:function(me){return function(){return me.tooltip();}}(me)};
  2615. if (me.tooltipOrigin) obj.origin=me.tooltipOrigin;
  2616. else if (me.box.tooltipOrigin) obj.origin=me.box.tooltipOrigin;
  2617. if (me.tooltipClasses) obj.classes=me.tooltipClasses;
  2618. else if (me.box.tooltipClasses) obj.classes=me.box.tooltipClasses;
  2619. G.addTooltip(me.l,obj);
  2620. }
  2621. }
  2622. }
  2623. }
  2624. }
  2625. G.Thing.prototype.getQuickDom=function(id)
  2626. {
  2627. //returns simplified non-gameplay DOM with no bindings save for tooltip, such as something you'd see in the stats page
  2628. var me=this;
  2629. var classes='thing '+me.type;
  2630. if (me.classes) classes+=' '+me.classes;
  2631. if (!me.icon) classes+=' noIcon';
  2632. if (me.noText) classes+=' noText';
  2633. if (me.tags) classes+=' tag-'+me.tags.join(' tag-');
  2634. var iconClasses='thing-icon';
  2635. if (me.iconClasses) iconClasses+=' '+me.iconClasses;
  2636. var icon=G.resolveIcon(me.icon);
  2637. var str='';
  2638. str+='<div '+(id?'id="'+id+'" ':'')+'class="'+classes+'">';
  2639. str+='<div class="'+iconClasses+'" style="'+icon+'"></div>';
  2640. if (!me.icon) str+='<div class="thing-text">'+me.name+'</div>';
  2641. str+='</div>';
  2642. if (me.tooltip && !me.noTooltip)
  2643. {
  2644. var obj={func:function(me){return function(){return me.tooltip();}}(me)};
  2645. if (me.tooltipClasses) obj.classes=me.tooltipClasses;
  2646. str=G.tooltipped(str,obj,'display:inline-block;');
  2647. }
  2648.  
  2649. return str;
  2650. }
  2651. G.Thing.prototype.removeDom=function()
  2652. {
  2653. if (G.tooltip.parent==this.l) G.hideTooltip();
  2654. this.l.parentNode.removeChild(this.l);
  2655. delete this.l;
  2656. delete this.iconl;
  2657. delete this.textl;
  2658. delete this.costsl;
  2659. }
  2660.  
  2661. G.Thing.prototype.getCosts=function(mult)
  2662. {
  2663. //also see : G.getCostsStr, G.spendCosts
  2664. var costs=this.costs;
  2665. var costsByThing={};
  2666. var mult=mult||1;
  2667. var refund=false;
  2668. if (mult<0) refund=true;
  2669. var add=this.costAdd;
  2670. mult*=this.costMult;
  2671. if (refund) mult*=this.refundMult;
  2672. for (var i in costs)
  2673. {
  2674. for (var ii in costs[i])
  2675. {
  2676. var me=costs[i][ii];
  2677. var w=me.w;
  2678. var v=(me.v);
  2679. if (this.type=='building') v=(me.v*Math.pow(this.costRate,Math.max(0,this.amount)));
  2680. v+=add;
  2681. v+=w.costAdd;
  2682. if (this.costAddFor[w.id]) v+=this.costAddFor[w.id];
  2683. v*=mult;
  2684. v*=w.costMult;
  2685. if (refund) v*=w.refundMult;
  2686. if (this.costMultFor[w.id]) v*=this.costMultFor[w.id];
  2687. if (refund && this.refundMultFor[w.id]) v*=this.refundMultFor[w.id];
  2688. if (refund && this.type=='building' && this.amount==0) v=0;
  2689. if (!costsByThing[w.id]) costsByThing[w.id]=0;
  2690. costsByThing[w.id]+=v;
  2691. }
  2692. }
  2693. return costsByThing;
  2694. }
  2695.  
  2696. G.Thing.prototype.checkReqs=function()
  2697. {
  2698. if (this.reqFunc && this.reqFunc()) return true;
  2699. else return false;
  2700. }
  2701.  
  2702. G.Thing.prototype.logic=function()
  2703. {
  2704. //every logic tick
  2705. if (true)
  2706. {
  2707. var me=this;
  2708. if (me.type=='res')
  2709. {
  2710. if (me.amountD!=me.amount) me.refreshText=true;
  2711. if (Math.abs(me.amount-me.amountD)<0.01) {me.amountD=me.amount;}
  2712. else {me.amountD+=(me.amount-me.amountD)*(Math.abs(me.amount-me.amountD)<10?0.5:0.1);}
  2713. }
  2714. else if (me.type=='shiny')
  2715. {
  2716. if (me.freq>0)
  2717. {
  2718. if (me.timeLeft>0)
  2719. {
  2720. me.timeLeft--;
  2721. if (me.timeLeft==0)
  2722. {
  2723. G.spawnShiny(me);
  2724. me.timeLeft=-1;
  2725. }
  2726. }
  2727. if (me.timeLeft==-1)//init time
  2728. {
  2729. me.timeLeft=Math.ceil(Math.max(0,(me.freq+Math.random()*me.freqV)*me.freqMult)*G.fps);
  2730. }
  2731. }
  2732. }
  2733. }
  2734. }
  2735.  
  2736. G.Thing.prototype.draw=function()
  2737. {
  2738. //every draw tick
  2739. var me=this;
  2740. if (me.l && !me.alwaysHidden)
  2741. {
  2742. if (me.toRefresh)
  2743. {
  2744. if (me.toRefresh!=2) me.updateDisplayed();
  2745. if (!me.displayed)
  2746. {
  2747. if (G.tooltip.parent==me.l) G.hideTooltip(-1);
  2748. me.l.classList.add('hidden');
  2749. me.l.classList.remove('visible');
  2750. }
  2751. else
  2752. {
  2753. me.l.classList.add('visible');
  2754. me.l.classList.remove('hidden');
  2755.  
  2756. var lit=me.lit;
  2757. if (lit)
  2758. {
  2759. this.l.classList.add('lit');
  2760. this.l.classList.remove('dim');
  2761. }
  2762. else
  2763. {
  2764. this.l.classList.add('dim');
  2765. this.l.classList.remove('lit');
  2766. }
  2767. }
  2768. }
  2769. if (me.text && (me.toRefresh || G.drawT%10==0))
  2770. {
  2771. me.textl.innerHTML=G.getTextValue(me.text,me);
  2772. }
  2773. me.toRefresh=0;
  2774. if (me.displayed)
  2775. {
  2776. if (me.text)
  2777. {
  2778. }
  2779. else if (me.refreshText)
  2780. {
  2781. me.refreshText=false;
  2782. if (me.type=='res')
  2783. {
  2784. var str=me.plural+' : ';
  2785. str+=B(Math.floor(me.amountD));
  2786. if (me.box.showPs)
  2787. {
  2788. var ps=me.ps;
  2789. if (ps>0) str+=' (+'+B(ps,1)+'/s)';
  2790. else if (ps<0) str+=' ('+B(ps,1)+'/s)';
  2791. //else str+=' (0/s)';
  2792. }
  2793. me.textl.innerHTML=str;
  2794. }
  2795. else if (me.type=='building')
  2796. {
  2797. var str=me.plural+' : ';
  2798. str+=B(me.amount);
  2799. me.textl.innerHTML=str;
  2800. }
  2801. }
  2802. if (me.type=='res')
  2803. {
  2804. if (me.ps>0) me.l.classList.add('earning');
  2805. else me.l.classList.remove('earning');
  2806. if (me.ps<0) me.l.classList.add('losing');
  2807. else me.l.classList.remove('losing');
  2808. }
  2809. if (G.drawT%10==0)
  2810. {
  2811. var owned=0;
  2812. if (me.owned && (me.type=='upgrade' || me.type=='achiev')) owned=1;
  2813. else if (me.amount>0 && (me.type=='res' || me.type=='building')) owned=1;
  2814. if (me.costs.length>0)
  2815. {
  2816. var amount=1;
  2817. if (me.type=='building') {amount=(G.bulk<0?G.bulk*0.5:G.bulk);}
  2818. var costs=G.getCostsStr(me.getCosts(amount),(me.type=='upgrade'?(owned>0):0),true);
  2819. if (!me.noText && me.costsl) me.costsl.innerHTML=costs.str;
  2820. if (costs.lacking>0 && (!owned || me.type=='building')) me.l.classList.add('cantAfford');
  2821. else me.l.classList.remove('cantAfford');
  2822. }
  2823. me.updateOwned(owned>0);
  2824. }
  2825. }
  2826. }
  2827. }
  2828.  
  2829. G.bulk=1;//how much of buildings we buy at once (or sell, if negative)
  2830.  
  2831. G.Thing.prototype.click=function()
  2832. {
  2833. if (true)
  2834. {
  2835. var win=false;
  2836. var out=0;
  2837. if (this.type=='button' || this.type=='shiny')
  2838. {
  2839. this.clicks++;
  2840. win=true;
  2841. }
  2842. if (this.type=='building' && !this.noBuy)
  2843. {
  2844. var amount=G.bulk;
  2845. if (amount<0)//selling
  2846. {
  2847. if (this.amount+amount>=0) out=G.refundCosts(this.getCosts(-amount*this.refundRate));
  2848. }
  2849. else if (!this.limit || this.limit()>=this.amount+amount)//buying
  2850. {
  2851. out=G.spendCosts(this.getCosts(amount));
  2852. }
  2853. if (out)
  2854. {
  2855. this.doEffects('undo grants');
  2856. this.earn(amount);
  2857. win=true;
  2858. }
  2859. }
  2860. else if (this.type=='upgrade' && !this.owned && !this.noBuy)
  2861. {
  2862. out=G.spendCosts(this.getCosts());
  2863. if (out)
  2864. {
  2865. this.doEffects('undo grants');
  2866. this.earn(1);
  2867. win=true;
  2868. }
  2869. }
  2870.  
  2871. var out2=this.doEffects('click',true);
  2872. if (out2.produced && out.produced)
  2873. {
  2874. for (var i in out2.produced)
  2875. {
  2876. if (!out.produced[i]) out.produced[i]=0;
  2877. out.produced[i]+=out2.produced[i];
  2878. }
  2879. }
  2880. else out=out2;
  2881.  
  2882. if (win && this.l)
  2883. {
  2884. if (this.type=='button') G.hideTooltip();
  2885. if (out && !G.noParticles)
  2886. {
  2887. for (var i in out.produced)
  2888. {G.particleAt(this.l,G.things[i].icon,(G.things[i].icon?'':(G.things[i].name))+(out.produced[i]>0?'+':'')+B(out.produced[i]));}
  2889. }
  2890. }
  2891. }
  2892. }
  2893.  
  2894. G.Thing.prototype.updateOwned=function(val)
  2895. {
  2896. if (val) {this.l.classList.add('owned');this.l.classList.remove('notOwned');}
  2897. else {this.l.classList.add('notOwned');this.l.classList.remove('owned');}
  2898. }
  2899. G.Thing.prototype.set=function(v)
  2900. {
  2901. var w=this;
  2902. if (w.type=='res' || w.type=='building')
  2903. {
  2904. if (w.type=='building') v=Math.round(v);
  2905. w.amount=v;
  2906. if (w.type=='res' && v>0) w.earned=Math.max(v,w.earned);
  2907. if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
  2908. w.maxAmount=Math.max(w.amount,w.maxAmount);
  2909. this.updateOwned(this.amount>0);
  2910. this.refreshText=true;
  2911. }
  2912. else if (w.type=='upgrade' || w.type=='achiev')
  2913. {
  2914. var v2=v?1:0;
  2915. if (w.owned!=v2 && w.l)
  2916. {
  2917. w.updateOwned(v2>0);
  2918. }
  2919. w.owned=v2;
  2920. }
  2921. }
  2922. G.Thing.prototype.earn=function(v)
  2923. {
  2924. var w=this;
  2925. if (w.type=='res' || w.type=='building')
  2926. {
  2927. if (w.type=='building') v=Math.round(v);
  2928. var old=w.amount;
  2929. w.amount+=v;
  2930. if (w.type=='res' && v>0) w.earned+=v;
  2931. if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
  2932. if (w.amount-old>0) w.doEffects('earn');
  2933. else if (w.amount-old<0) w.doEffects('lose');
  2934. w.maxAmount=Math.max(w.amount,w.maxAmount);
  2935. if (w.l)
  2936. {
  2937. w.updateOwned(w.amount>0);
  2938. }
  2939. w.refreshText=true;
  2940. }
  2941. else if (w.type=='upgrade' || w.type=='achiev')
  2942. {
  2943. var v2=v?1:0;
  2944. if (w.owned!=v2 && w.l)
  2945. {
  2946. w.updateOwned(v2>0);
  2947. }
  2948. w.owned=v2;
  2949. if (w.owned) w.doEffects('earn');
  2950. else w.doEffects('lose');
  2951.  
  2952. if (w.owned && w.type=='achiev')
  2953. {
  2954. var str='';
  2955. if (w.icon) str+='<div class="thing-icon" style="'+G.resolveIcon(w.icon,true)+'"></div>';
  2956. if (w.name) str+='Got achievement :<div class="title">'+w.name+'</div>'; else str+='Got achievement!';
  2957. G.toast({text:str,dur:10});
  2958. }
  2959. }
  2960. }
  2961. G.Thing.prototype.grant=function(v)
  2962. {
  2963. var w=this;
  2964. if (w.type=='res' || w.type=='building')
  2965. {
  2966. if (w.type=='building') v=Math.round(v);
  2967. w.amount+=v;
  2968. if (w.type=='res' && v>0) w.earned=Math.max(w.earned,w.amount);
  2969. if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
  2970. w.maxAmount=Math.max(w.amount,w.maxAmount);
  2971. if (w.l)
  2972. {
  2973. w.updateOwned(w.amount>0);
  2974. }
  2975. w.refreshText=true;
  2976. }
  2977. }
  2978. G.Thing.prototype.light=function()
  2979. {
  2980. if (this.lit || this.alwaysHidden) return false;
  2981. this.lit=1;
  2982. this.toRefresh=1;
  2983. }
  2984. G.Thing.prototype.dim=function()
  2985. {
  2986. if (!this.lit || this.alwaysHidden) return false;
  2987. this.lit=0;
  2988. this.toRefresh=1;
  2989. }
  2990. G.Thing.prototype.display=function()
  2991. {
  2992. if (this.show || this.alwaysHidden) return false;
  2993. this.show=1;
  2994. this.toRefresh=1;
  2995. }
  2996. G.Thing.prototype.hide=function()
  2997. {
  2998. if (!this.show || this.alwaysHidden) return false;
  2999. this.show=0;
  3000. this.toRefresh=1;
  3001. }
  3002.  
  3003. G.doEffectsForAll=function(type)
  3004. {
  3005. var things=[];
  3006. for (var i in G.things)
  3007. {
  3008. var me=G.things[i];
  3009. if (me.type=='button' || me.type=='res' || (me.type=='building' && me.amount>0) || (me.type=='upgrade' && me.owned) || (me.type=='achiev' && me.owned)) things.push(me);
  3010. }
  3011. for (var i in G.items)
  3012. {things.push(G.items[i]);}
  3013.  
  3014. for (var i in things)
  3015. {
  3016. var me=things[i];
  3017. me.doEffects(type);
  3018. }
  3019. }
  3020.  
  3021. /*=====================================================================================
  3022. TICK
  3023. =======================================================================================*/
  3024. G.tick=function()
  3025. {
  3026. //every second :
  3027.  
  3028. var things=[];
  3029. for (var i in G.things)
  3030. {
  3031. var me=G.things[i];
  3032. if (me.type=='button' || me.type=='res' || (me.type=='building' && me.amount>0) || (me.type=='upgrade' && me.owned) || (me.type=='achiev' && me.owned)) things.push(me);
  3033. }
  3034. for (var i in G.items)
  3035. {things.push(G.items[i]);}
  3036.  
  3037. //reset boosts and production
  3038. for (var i in G.things)
  3039. {
  3040. var me=G.things[i];
  3041. me.ps=0;
  3042. me.boostAdd=0;
  3043. me.boostMult=1;
  3044. me.boostAddFor=[];
  3045. me.boostMultFor=[];
  3046. me.costAdd=0;
  3047. me.costMult=1;
  3048. me.refundMult=1;
  3049. me.costAddFor=[];
  3050. me.costMultFor=[];
  3051. me.refundMultFor=[];
  3052. if (me.type=='shiny')
  3053. {
  3054. me.durMult=1;
  3055. me.freqMult=1;
  3056. }
  3057. }
  3058.  
  3059. //cache boosts
  3060. for (var i in things)
  3061. {
  3062. var me=things[i];
  3063. me.doEffects('cache boosts');
  3064. }
  3065.  
  3066. //apply grants
  3067. for (var i in things)
  3068. {
  3069. var me=things[i];
  3070. me.doEffects('do grants');
  3071. }
  3072.  
  3073. //cache production
  3074. for (var i in things)
  3075. {
  3076. var me=things[i];
  3077. me.doEffects('cache ps');
  3078. }
  3079.  
  3080. //tick
  3081. for (var i in things)
  3082. {
  3083. var me=things[i];
  3084. me.doEffects('tick');
  3085. }
  3086.  
  3087. //apply production
  3088. for (var i in things)
  3089. {
  3090. var me=things[i];
  3091. if (me.type=='res' || me.type=='building')
  3092. {
  3093. if (me.ps!=0)
  3094. {
  3095. me.earn(me.ps);
  3096. }
  3097. }
  3098. }
  3099.  
  3100. for (var i in G.things)
  3101. {
  3102. var me=G.things[i];
  3103. if (me.isAlways) me.amount=me.isAlways();
  3104. if (me.limit) me.amount=Math.min(me.limit(),me.amount);
  3105. if (me.type=='achiev' && !me.owned)
  3106. {
  3107. if (me.checkReqs())
  3108. {
  3109. me.earn(1);
  3110. }
  3111. }
  3112. var old=me.displayed||0;
  3113. me.updateDisplayed();
  3114. if (me.displayed!=old) me.toRefresh=2;//triggers a refresh without re-running updateDisplayed()
  3115. }
  3116. }
  3117.  
  3118. /*=====================================================================================
  3119. EFFECTS
  3120. =======================================================================================*/
  3121. G.effectNestLevel=0;
  3122. G.localVars=[];
  3123. G.doEffect=function(effect,owner,context,amount,out)
  3124. {
  3125. G.effectNestLevel++;
  3126. if (G.effectNestLevel>10) {G.effectNestLevel--;return false;}//possible endless loop
  3127. var me=effect;
  3128. var type=me.type;
  3129. if (me.w)
  3130. {
  3131. var w=me.w;
  3132. if (w==='this') w=owner;
  3133. else if (w[0]==='this') w=[owner];
  3134. }
  3135. if (type=='if' || type=='else')
  3136. {
  3137. var pass=false;
  3138. if (me.cond && context!='undo grants')//when undoing grants, disregard conditions
  3139. {
  3140. var amount2=0;
  3141. if (!owner) amount2=0;
  3142. else
  3143. {
  3144. if (owner.type=='building' || owner.type=='res') amount2=owner.amount;
  3145. else if (owner.type=='upgrade' || owner.type=='achiev') amount2=(owner.owned?1:0);
  3146. else if (owner.type=='button' || owner.type=='shiny') amount2=owner.clicks;
  3147. }
  3148. if (me.cond(owner,amount2)) pass=true;
  3149. }
  3150. else pass=true;
  3151.  
  3152. if (pass)
  3153. {
  3154. for (var i in me.effs)
  3155. {
  3156. G.doEffect(me.effs[i],owner,context,amount,out);
  3157. }
  3158. }
  3159. else if (me.or)
  3160. {
  3161. for (var i in me.or)
  3162. {
  3163. G.doEffect(me.or[i],owner,context,amount,out);
  3164. }
  3165. }
  3166. }
  3167. else if (type=='grant' && context=='do grants' && !me.done)
  3168. {
  3169. me.done=true;
  3170. w.grant(amount*G.getVarValue(me.v,owner));
  3171. }
  3172. else if (type=='grant' && context=='undo grants' && me.done)
  3173. {
  3174. me.done=false;
  3175. w.grant(-amount*G.getVarValue(me.v,owner));
  3176. }
  3177. else if (context=='do grants' || context=='undo grants') {}
  3178. else if (context=='cache boosts')
  3179. {
  3180. if (type=='increase yield' || type=='lower yield')
  3181. {
  3182. var w=G.getThings(me.w,owner);
  3183. var gain=amount*G.getVarValue(me.v,owner);
  3184. if (type=='lower yield') gain*=-1;
  3185. for (var i=0;i<w.length;i++)
  3186. {
  3187. w[i].boostAdd+=gain;
  3188. }
  3189. }
  3190. else if (type=='multiply yield')
  3191. {
  3192. var w=G.getThings(me.w,owner);
  3193. var gain=amount*G.getVarValue(me.v,owner);
  3194. for (var i=0;i<w.length;i++)
  3195. {
  3196. w[i].boostMult*=gain;
  3197. }
  3198. }
  3199. else if (type=='increase yield for' || type=='lower yield for')
  3200. {
  3201. var w=G.getThings(me.w,owner);
  3202. var z=G.getThings(me.z,owner);
  3203. var gain=amount*G.getVarValue(me.v,owner);
  3204. if (type=='lower yield for') gain*=-1;
  3205. for (var i=0;i<w.length;i++)
  3206. {
  3207. for (var ii=0;ii<z.length;ii++)
  3208. {
  3209. if (!w[i].boostAddFor[z[ii].id]) w[i].boostAddFor[z[ii].id]=0;
  3210. w[i].boostAddFor[z[ii].id]+=gain;
  3211. }
  3212. }
  3213. }
  3214. else if (type=='multiply yield for')
  3215. {
  3216. var w=G.getThings(me.w,owner);
  3217. var z=G.getThings(me.z,owner);
  3218. var gain=amount*G.getVarValue(me.v,owner);
  3219. for (var i=0;i<w.length;i++)
  3220. {
  3221. for (var ii=0;ii<z.length;ii++)
  3222. {
  3223. if (!w[i].boostMultFor[z[ii].id]) w[i].boostMultFor[z[ii].id]=1;
  3224. w[i].boostMultFor[z[ii].id]*=gain;
  3225. }
  3226. }
  3227. }
  3228. else if (type=='increase cost' || type=='lower cost')
  3229. {
  3230. var w=G.getThings(me.w,owner);
  3231. var gain=amount*G.getVarValue(me.v,owner);
  3232. if (type=='lower cost') gain*=-1;
  3233. for (var i=0;i<w.length;i++)
  3234. {
  3235. w[i].costAdd+=gain;
  3236. }
  3237. }
  3238. else if (type=='multiply cost')
  3239. {
  3240. var w=G.getThings(me.w,owner);
  3241. var gain=amount*G.getVarValue(me.v,owner);
  3242. for (var i=0;i<w.length;i++)
  3243. {
  3244. w[i].costMult*=gain;
  3245. }
  3246. }
  3247. else if (type=='multiply refund')
  3248. {
  3249. var w=G.getThings(me.w,owner);
  3250. var gain=amount*G.getVarValue(me.v,owner);
  3251. for (var i=0;i<w.length;i++)
  3252. {
  3253. w[i].refundMult*=gain;
  3254. }
  3255. }
  3256. else if (type=='multiply duration')
  3257. {
  3258. var w=G.getThings(me.w,owner);
  3259. var gain=amount*G.getVarValue(me.v,owner);
  3260. for (var i=0;i<w.length;i++)
  3261. {
  3262. if (w[i].type=='shiny') w[i].durMult*=gain;
  3263. }
  3264. }
  3265. else if (type=='multiply frequency')
  3266. {
  3267. var w=G.getThings(me.w,owner);
  3268. var gain=amount*G.getVarValue(me.v,owner);
  3269. for (var i=0;i<w.length;i++)
  3270. {
  3271. if (w[i].type=='shiny') w[i].freqMult*=gain;
  3272. }
  3273. }
  3274. }
  3275. else if (context=='cache ps')
  3276. {
  3277. if (type=='yield' || type=='lose')
  3278. {
  3279. var w=G.getThings(me.w,owner);
  3280. var v=amount;
  3281. if (me.v) v*=G.getVarValue(me.v,owner);
  3282. if (owner.type=='item') owner=owner.base;
  3283. for (var i=0;i<w.length;i++)
  3284. {
  3285. var w2=w[i];
  3286. if (w2.type=='res' || w2.type=='building')
  3287. {
  3288. if (type=='lose') v=-v;
  3289. else if (w.type!='building')
  3290. {
  3291. v+=w2.boostAdd;
  3292. v*=w2.boostMult;
  3293.  
  3294. v+=owner.boostAdd;
  3295. v*=owner.boostMult;
  3296.  
  3297. if (owner.boostAddFor[w2.id]) v+=owner.boostAddFor[w2.id];
  3298. if (owner.boostMultFor[w2.id]) v*=owner.boostMultFor[w2.id];
  3299. }
  3300. w2.ps+=v;
  3301. }
  3302. }
  3303. }
  3304. }
  3305. else//on tick, on click, others
  3306. {
  3307. if (type=='set')
  3308. {
  3309. var w=G.getThings(me.w,owner);
  3310. var v=G.getVarValue(me.v,owner);
  3311. for (var i=0;i<w.length;i++)
  3312. {
  3313. if (w[i].type=='res' || w[i].type=='building') w[i].set(v);
  3314. }
  3315. }
  3316. else if (type=='set var')
  3317. {
  3318. var w=me.w;
  3319. var v=G.getVarValue(me.v,owner);
  3320. G.localVars[w]=v;
  3321. }
  3322. else if ((type=='yield' || type=='lose') && context!='tick')
  3323. {
  3324. var w2=G.getThings(me.w,owner);
  3325. var v=amount;
  3326. if (me.v) v*=G.getVarValue(me.v,owner);
  3327. if (owner.type=='item') owner=owner.base;
  3328. for (var i=0;i<w2.length;i++)
  3329. {
  3330. var w=w2[i];
  3331. if ((w.type=='upgrade' || w.type=='achiev'))
  3332. {
  3333. if (type=='yield' && !w.owned) {w.owned=1;w.doEffects('earn');}
  3334. else if (type=='lose' && w.owned) {w.owned=0;w.doEffects('lose');}
  3335. }
  3336. else if (w.type=='itemType' || w.type=='item')
  3337. {
  3338. if (type=='yield')//gain V items of type W, or of the same type as the existing item W
  3339. {
  3340. if (w.type=='item') w=w.base;
  3341. for (var ii=0;ii<v;ii++)
  3342. {
  3343. var newItem=G.gainItem(w);
  3344. }
  3345. }
  3346. else//lose V items of type W, or lose the specified existing item
  3347. {
  3348. if (w.type=='itemType')
  3349. {
  3350. var v2=('v' in me?v:100);
  3351. if (v2<0) v2=0;
  3352. for (var ii=0;ii<v2;ii++)
  3353. {
  3354. G.loseItem(w);
  3355. }
  3356. }
  3357. else
  3358. {
  3359. G.loseItem(w);
  3360. }
  3361. }
  3362. }
  3363. else
  3364. {
  3365. if (w.type=='res' || w.type=='building')
  3366. {
  3367. var v2=(('v' in me || type=='yield')?v:w.amount);
  3368. if (type=='lose') v2=-v2;
  3369. else if (w.type=='res')
  3370. {
  3371. v2+=w.boostAdd;
  3372. v2*=w.boostMult;
  3373.  
  3374. v2+=owner.boostAdd;
  3375. v2*=owner.boostMult;
  3376.  
  3377. if (owner.boostAddFor[w.id]) v2+=owner.boostAddFor[w.id];
  3378. if (owner.boostMultFor[w.id]) v2*=owner.boostMultFor[w.id];
  3379. }
  3380.  
  3381. if (out && v2!=0)
  3382. {
  3383. if (!out.produced[w.id]) out.produced[w.id]=0;
  3384. out.produced[w.id]+=v2;
  3385. }
  3386. w.earn(v2);
  3387. }
  3388. }
  3389. }
  3390. }
  3391. else if (type=='spawn')
  3392. {
  3393. if (me.w.type=='shiny') G.spawnShiny(me.w);
  3394. }
  3395. else if (type=='log')
  3396. {
  3397. if (me.classes) G.log(G.getTextValue(me.w,owner,1),me.classes);
  3398. else G.log(G.getTextValue(me.w,owner,1));
  3399. }
  3400. else if (type=='clear log')
  3401. {
  3402. G.clearLog();
  3403. }
  3404. else if (type=='toast')
  3405. {
  3406. G.toast({text:'<div>'+G.getTextValue(me.w,owner,1)+'</div>',classes:'center',dur:10});
  3407. }
  3408. else if (type=='echo')
  3409. {
  3410. console.log(owner.name+' : '+G.makeUnsafe(G.getTextValue(me.w,owner,1)));
  3411. }
  3412. else if (type=='do')
  3413. {
  3414. for (var i in w)
  3415. {
  3416. var it=w[i];
  3417. if (it.effects[me.v]) it.doEffects(me.v);
  3418. }
  3419. }
  3420. else if (type=='light')
  3421. {
  3422. var w=G.getThings(me.w,owner);
  3423. for (var i in w){w[i].light();}
  3424. }
  3425. else if (type=='dim')
  3426. {
  3427. var w=G.getThings(me.w,owner);
  3428. for (var i in w){w[i].dim();}
  3429. }
  3430. else if (type=='show')
  3431. {
  3432. var w=G.getThings(me.w,owner);
  3433. for (var i in w){w[i].display();}
  3434. }
  3435. else if (type=='hide')
  3436. {
  3437. var w=G.getThings(me.w,owner);
  3438. for (var i in w){w[i].hide();}
  3439. }
  3440. else if (type=='anim')
  3441. {
  3442. if (owner.l) triggerAnim(owner.l,me.w);
  3443. }
  3444. else if (type=='animicon')
  3445. {
  3446. if (owner.iconl) triggerAnim(owner.iconl,me.w);
  3447. }
  3448. else if (type=='eval' && true)
  3449. {
  3450. eval(me.w);
  3451. }
  3452. }
  3453. G.effectNestLevel--;
  3454. }
  3455.  
  3456. G.effectsByContext={
  3457. 'start':'start',
  3458. 'save':'save',
  3459. 'load':'load',
  3460. 'earn':'earn',
  3461. 'lose':'lose',
  3462. 'tick':'tick',
  3463. 'cache ps':'tick',
  3464. 'cache boosts':'tick',
  3465. 'do grants':'tick',
  3466. 'undo grants':'tick',
  3467. 'click':'click',
  3468. };
  3469. G.Thing.prototype.doEffects=function(context,returnOut)
  3470. {
  3471. if (returnOut)
  3472. {
  3473. var out={};
  3474. out.produced={};
  3475. }
  3476. var amount=1;
  3477. if (this.type=='building') amount=this.amount;
  3478.  
  3479. var custom=false;
  3480. if (!G.effectsByContext[context]) custom=true;
  3481. var resetVars=true;
  3482. if (custom || context=='earn' || context=='lose') resetVars=false;
  3483.  
  3484. if (resetVars) G.localVars=[];
  3485. var effs=this.effects[G.effectsByContext[context]]||this.effects[context]||[];
  3486. for (var i in effs)
  3487. {
  3488. G.doEffect(effs[i],this,context,amount,out);
  3489. }
  3490. if (resetVars) G.localVars=[];
  3491.  
  3492. if (returnOut) return out;
  3493. }
  3494.  
  3495.  
  3496. G.spendCosts=function(costs,mult)
  3497. {
  3498. var out={produced:{}};
  3499. var mult=mult||1;
  3500. for (var i in costs)
  3501. {
  3502. var w=G.things[i];
  3503. var v=costs[i];
  3504. if (!w.canBeNegative && w.amount<v*mult) return false;
  3505. }
  3506. for (var i in costs)
  3507. {
  3508. var w=G.things[i];
  3509. var v=costs[i]*mult;
  3510. w.earn(-v);
  3511. if (v!=0)
  3512. {
  3513. if (!out.produced[w.id]) out.produced[w.id]=0;
  3514. out.produced[w.id]-=v;
  3515. }
  3516. }
  3517. return out;
  3518. }
  3519. G.refundCosts=function(costs)
  3520. {
  3521. var out={produced:{}};
  3522. for (var i in costs)
  3523. {
  3524. var w=G.things[i];
  3525. var v=-costs[i];
  3526. if (!w.canBeNegative && w.amount<v) return false;
  3527. }
  3528. for (var i in costs)
  3529. {
  3530. var w=G.things[i];
  3531. var v=-costs[i];
  3532. w.earn(-v);
  3533. if (v!=0)
  3534. {
  3535. if (!out.produced[w.id]) out.produced[w.id]=0;
  3536. out.produced[w.id]-=v;
  3537. }
  3538. }
  3539. return out;
  3540. }
  3541.  
  3542.  
  3543. /*=====================================================================================
  3544. HELPERS
  3545. =======================================================================================*/
  3546.  
  3547. G.getCostsStr=function(costs,neutral,specialOutput)
  3548. {
  3549. var str='';
  3550. var notEnough=0;
  3551. var t=0;
  3552. for (var i in costs)
  3553. {
  3554. var w=G.things[i];
  3555. var v=costs[i];
  3556. if (v>w.amount && w.ps<=0) t=-1;
  3557. else if (w.ps>0 && t!=-1) t=Math.max(t,(v-w.amount)/w.ps);
  3558. var classes='cost';
  3559. if (!neutral && !w.canBeNegative && v>w.amount) {classes+=' notEnough';notEnough++;}
  3560. else if (!neutral) classes+=' hasEnough';
  3561. str+='<div class="'+classes+'">'+B(v)+' '+(v==1?w.name:w.plural)+'</div>';
  3562. }
  3563. if (t>0 && !neutral) str+='<div class="costTimeRemaining">(in '+sayTime(t*1000+750)+')</div>';
  3564. if (!specialOutput) return str;
  3565. else return {str:str,lacking:notEnough};
  3566. }
  3567.  
  3568. G.resolveIcon=function(icon,small)
  3569. {
  3570. //returns a bit of CSS
  3571. var str='';
  3572. if (icon)
  3573. {
  3574. str='background-image:';
  3575. for (var ii in icon)
  3576. {
  3577. str+='url('+icon[ii].url+'),';
  3578. }
  3579. str=str.slice(0,-1);
  3580. str+=';background-position:';
  3581. for (var ii in icon)
  3582. {
  3583. str+=icon[ii].x+'px '+icon[ii].y+'px,';
  3584. }
  3585. str=str.slice(0,-1);
  3586. if (small)
  3587. {
  3588. str+=';background-size:';
  3589. for (var ii in icon)
  3590. {
  3591. if (icon[ii].tile) str+='auto,';
  3592. else str+='100%,';
  3593. }
  3594. str=str.slice(0,-1);
  3595. }
  3596. str+=';';
  3597. }
  3598. return str;
  3599. }
  3600.  
  3601. G.strToList=function(str)
  3602. {
  3603. //convert a string such as "3 gold, 1 banana, 11 monkeys" into an array of things [{w:Thing,v:3}...]
  3604. var list={};
  3605. var str=str.replaceAll(', and ',',');
  3606. var str=str.replaceAll(' and ',',');
  3607. var str=str.replaceAll(', ',',');
  3608. str=str.split(',');
  3609. for (var i in str)
  3610. {
  3611. var bit=str[i].split(' ');
  3612. if (bit[1] && G.thingsByName[bit[1]])
  3613. {
  3614. var val=parseFloat(bit[0]);
  3615. var thing=bit[1];//G.thingsByName[bit[1]];
  3616. if (list[thing]) list[thing]+=val;
  3617. else list[thing]=val;
  3618. }
  3619. }
  3620. var list2=[];
  3621. for (var i in list)
  3622. {
  3623. list2.push({w:G.thingsByName[i],v:list[i]});
  3624. }
  3625. return list2;
  3626. }
  3627.  
  3628. G.strToThings=function(str)
  3629. {
  3630. //convert a string such as "monkeys, tagged:fruit" into an array of things [Thing...]
  3631. var list=[];
  3632. var str=str.replaceAll(', and ',',');
  3633. var str=str.replaceAll(' and ',',');
  3634. var str=str.replaceAll(', ',',');
  3635. str=str.split(',');
  3636. for (var i in str)
  3637. {
  3638. var bit=str[i].split(':');
  3639. if (bit[0] && G.thingsByName[bit[0]] && list.indexOf(bit[0])==-1) list.push(bit[0]);
  3640. //TODO : tags
  3641. /*if (bit[1] && G.thingsByName[bit[0]])
  3642. {
  3643. var val=parseFloat(bit[0]);
  3644. var thing=bit[1];//G.thingsByName[bit[1]];
  3645. if (list[thing]) list[thing]+=val;
  3646. else list[thing]=val;
  3647. }*/
  3648. }
  3649. var list2=[];
  3650. for (var i in list)
  3651. {
  3652. list2.push(G.thingsByName[list[i]]);
  3653. }
  3654. return list2;
  3655. }
  3656.  
  3657. /*=====================================================================================
  3658. PARSE EXPRESSION STRING TO FUNCTION
  3659. =======================================================================================*/
  3660. G.parseToFunc=function(cmd,dummyTest)
  3661. {
  3662. /* this function takes a string and returns a function based on the string that can be safely executed
  3663. if dummyTest is true, test the syntax safely; if no syntax error, return 'ok'; otherwise, return the error text
  3664. examples :
  3665. G.parseToFunc('floor(gold/10)+3');
  3666. G.parseToFunc('max(random(2,luck),2)*2');
  3667. available operations :
  3668. a+b
  3669. a-b
  3670. a*b
  3671. a/b
  3672. floor(a)
  3673. round(a)
  3674. ceil(a)
  3675. roundr(a) (returns a rounded up or down randomly depending on its value; 0.1 is much more likely to be rounded down than up)
  3676. min(a,b)
  3677. max(a,b)
  3678. pow(a,b)
  3679. chance(a) chance(a%) (a is between 0 and 100; returns true if pass, false otherwise)
  3680. random(a) random(a,b)
  3681. frandom(a) frandom(a,b)
  3682. a=b a==b a is b
  3683. a!=b !(a=b) a isn't b
  3684. && and
  3685. || or
  3686. this (if the function is called with func(Thing,Thing.amount) this will return the Thing's amount)
  3687. thingKey (returns the Thing's amount if res or building; if upgrade or achiev, return 1 if owned, 0 if not)
  3688. $localVar (returns the value of the local variable with that name)
  3689. have thingKey (returns whether we have the upgrade or achiev, or if the res or building is above 0)
  3690. */
  3691.  
  3692. //put a space before and after every word and number
  3693. var cmd2='';
  3694. var inWord=0;
  3695. cmd='('+cmd+')';
  3696. for (var i=0;i<cmd.length;i++)
  3697. {
  3698. var it=cmd.charAt(i);
  3699. if (G.Kalphanum.indexOf(it)!=-1 || it==':')
  3700. {
  3701. if (!inWord) cmd2+=' ';
  3702. inWord=1;
  3703. }
  3704. else
  3705. {
  3706. if (inWord) cmd2+=' ';
  3707. inWord=0;
  3708. }
  3709. cmd2+=it;
  3710. }
  3711. cmd2=cmd2.replace(/ +(?= )/g,'');//remove all multiple spaces
  3712.  
  3713.  
  3714. var tokens=[];
  3715. var str=STR2(cmd2);
  3716. var ok=1;
  3717. var prevKeyword='';
  3718. var setKeyword=0;
  3719.  
  3720. while(ok && str.val.length>0)
  3721. {
  3722. var setKeyword=0;
  3723. if (str.gulpSymbol(',')) tokens.push(',');
  3724. else if (str.gulpSymbol('('))
  3725. {
  3726. if (prevKeyword=='floor') tokens.push('Math.floor(');
  3727. else if (prevKeyword=='round') tokens.push('Math.round(');
  3728. else if (prevKeyword=='ceil') tokens.push('Math.ceil(');
  3729. else if (prevKeyword=='roundr') tokens.push('randomFloor(');
  3730. else if (prevKeyword=='min') tokens.push('Math.min(');
  3731. else if (prevKeyword=='max') tokens.push('Math.max(');
  3732. else if (prevKeyword=='pow') tokens.push('Math.pow(');
  3733. else if (prevKeyword=='chance') tokens.push('luck(');
  3734. else if (prevKeyword=='random') tokens.push('rand(');
  3735. else if (prevKeyword=='frandom') tokens.push('frand(');
  3736. else tokens.push('(');
  3737. }
  3738. else if (str.gulpSymbol(')')) tokens.push(')');
  3739. else if (str.gulpSymbol('+')) tokens.push('+');
  3740. else if (str.gulpSymbol('-')) tokens.push('-');
  3741. else if (str.gulpSymbol('*')) tokens.push('*');
  3742. else if (str.gulpSymbol('/')) tokens.push('/');
  3743. else if (str.gulpSymbol('<=')) tokens.push('<=');
  3744. else if (str.gulpSymbol('>=')) tokens.push('>=');
  3745. else if (str.gulpSymbol('<')) tokens.push('<');
  3746. else if (str.gulpSymbol('>')) tokens.push('>');
  3747. else if (str.gulpSymbol('%)') || str.gulpSymbol('% )')) tokens.push(')');
  3748. else if (str.gulpSymbol('%')) tokens.push('%');
  3749. else if (str.gulpSymbol('isn \' t ') || str.gulpSymbol('!=')) tokens.push('!=');
  3750. else if (str.gulp('is') || str.gulpSymbol('==') || str.gulpSymbol('=')) tokens.push('==');
  3751. else if (str.gulpSymbol('!')) tokens.push('!');
  3752. else if (str.gulp('and')) tokens.push('&&');
  3753. else if (str.gulp('or')) tokens.push('||');
  3754. else if (str.gulp('this')) tokens.push(dummyTest?'V':'v');
  3755. else if (str.gulp('ItemsLeft')) tokens.push(' (G.itemsMax-G.itemsTotal) ');
  3756. else if (str.gulp('lit')) tokens.push(dummyTest?'V':'w?w.lit:0');
  3757. else if (str.gulp('dim')) tokens.push(dummyTest?'V':'w?(!w.lit):0');
  3758. else if (str.val.charAt(0)=='$')
  3759. {
  3760. str.gulpSymbol('$');
  3761. var localVar='$'+str.gulp();
  3762. localVar=G.validateVar(localVar);
  3763. if (dummyTest) tokens.push('V'); else tokens.push('(G.localVars[\''+localVar+'\']||0)');
  3764. }
  3765. else
  3766. {
  3767. var tkn=str.gulp();
  3768. //console.log(' doing ['+tkn+']');
  3769. if ( tkn=='have'
  3770. || tkn=='no'
  3771. || tkn=='floor'
  3772. || tkn=='round'
  3773. || tkn=='roundr'
  3774. || tkn=='ceil'
  3775. || tkn=='min'
  3776. || tkn=='max'
  3777. || tkn=='pow'
  3778. || tkn=='chance'
  3779. || tkn=='random'
  3780. || tkn=='frandom'
  3781. ){setKeyword=tkn;}
  3782. else if (G.thingsByName[tkn.split(':')[0]])
  3783. {
  3784. var tkn2=tkn.split(':')[1];
  3785. if (tkn2)
  3786. {
  3787. if ( tkn2=='clicks'
  3788. || tkn2=='ps'
  3789. || tkn2=='max'
  3790. || tkn2=='earned'
  3791. ){}
  3792. else {ok=0;tokens.push(tkn);}
  3793. }
  3794. if (ok)
  3795. {
  3796. if (dummyTest) tokens.push('V');
  3797. else
  3798. {
  3799. var thing=G.thingsByName[tkn.split(':')[0]];
  3800. if (tkn2=='ps' && thing.type=='res') tokens.push('(G.things['+thing.id+'].ps)');
  3801. else if (tkn2=='ps') tokens.push('(false)');
  3802. else if (tkn2=='clicks' && (thing.type=='button' || thing.type=='shiny')) tokens.push('(G.things['+thing.id+'].clicks)');
  3803. else if (tkn2=='clicks') tokens.push('(false)');
  3804. else if (tkn2=='max' && (thing.type=='res' || thing.type=='building')) tokens.push('(G.things['+thing.id+'].maxAmount)');
  3805. else if (tkn2=='max') tokens.push('(false)');
  3806. else if (tkn2=='earned' && (thing.type=='res')) tokens.push('(G.things['+thing.id+'].earned)');
  3807. else if (tkn2=='earned') tokens.push('(false)');
  3808. else if (prevKeyword=='have')
  3809. {
  3810. if (thing.type=='res' || thing.type=='building') tokens.push('(G.things['+thing.id+'].amount>0)');
  3811. else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==1)');
  3812. }
  3813. else if (prevKeyword=='no')
  3814. {
  3815. if (thing.type=='res' || thing.type=='building') tokens.push('(G.things['+thing.id+'].amount==0)');
  3816. else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==0)');
  3817. }
  3818. else if (thing.type=='res' || thing.type=='building') tokens.push('G.things['+thing.id+'].amount');
  3819. else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==1)');
  3820. else {ok=0;tokens.push(tkn);}
  3821. }
  3822. }
  3823. }
  3824. else if (isNumber(tkn)) {tokens.push(parseFloat(tkn)+'');}
  3825. else {ok=0;tokens.push(tkn);}
  3826. }
  3827. if (setKeyword) prevKeyword=setKeyword; else prevKeyword='';
  3828. }
  3829. //console.log(' [ '+tokens.join(' ][ ')+' ]');
  3830. var funcBody=tokens.join(' ');
  3831. funcBody='return parseNum( '+funcBody+' );';
  3832. if (!ok && tokens[tokens.length-1])
  3833. {
  3834. //console.log(' ERROR : '+tokens[tokens.length-1]);
  3835. if (dummyTest) return 'Failed to parse "'+tokens[tokens.length-1]+'"';
  3836. else return false;
  3837. }
  3838. else
  3839. {
  3840. if (dummyTest) funcBody='var V=1; '+funcBody;
  3841. else funcBody='var w=w||0;var v=v||0; '+funcBody;
  3842. //console.log(' '+funcBody);
  3843. var func=0;
  3844. var result=0;
  3845. try
  3846. {
  3847. func=new Function('w','v',funcBody);
  3848. if (dummyTest) result=func();
  3849. if (dummyTest) return 'ok'; else return func;
  3850. //console.log(' Result is '+result);
  3851. } catch (e)
  3852. {
  3853. if (true)//e instanceof SyntaxError)
  3854. {
  3855. if (dummyTest)
  3856. {
  3857. //console.log(funcBody);
  3858. //console.log(e);
  3859. return 'Error when executing function : "'+e.message+'".';
  3860. }
  3861. else return false;
  3862. }
  3863. }
  3864. }
  3865. }
  3866.  
  3867. /*=====================================================================================
  3868. PARSER
  3869. =======================================================================================*/
  3870. G.line=0;
  3871. G.lineNums=[];
  3872. G.foundError=false;
  3873. G.context='';
  3874. G.parseError=function(str)
  3875. {
  3876. var rawStr=str;
  3877.  
  3878. if (!G.foundError)
  3879. {
  3880. G.l.innerHTML=`
  3881. <div id="errorWrap">
  3882. <div id="error">
  3883. The game couldn't function due to the following problems :
  3884. <div id="errors"></div>
  3885. <small>You may want to contact the author of this game.</small>
  3886. </div>
  3887. </div>
  3888. `;
  3889. G.l.classList.add('on');
  3890. }
  3891.  
  3892. var str=str||'';
  3893. str=str.replaceAll('&','&amp;');
  3894. str=str.replaceAll('<','&lt;');
  3895. str=str.replaceAll('>','&gt;');
  3896. str=str.replaceAll('"','&quot;');
  3897. str=str.replaceAll('\'','&apos;');
  3898.  
  3899. var div=document.createElement('div');
  3900. div.className='error';
  3901. div.innerHTML='&bull; ERROR '+(G.context||'(unidentified)')+' :<br>['+str+']';
  3902. l('errors').appendChild(div);
  3903. console.log('ERROR '+(G.context||'(unidentified)')+' : '+rawStr);
  3904.  
  3905. G.foundError=true;
  3906. return false;
  3907. }
  3908. G.shorten=function(str,len)
  3909. {
  3910. if (str.length<len) return str; else return str.substring(0,len)+'…';
  3911. }
  3912.  
  3913. //sanitizing functions
  3914. //regexps are used scarcely because I don't trust myself with those
  3915. G.Kalpha='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
  3916. G.Kalphanum='.1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
  3917. G.Knum='.1234567890';
  3918. G.Kvar='1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
  3919. G.reservedKeywords=['this','that','all','except','but','is','isn\'t','no','not','have','floor','round','ceil','max','min','include','by','and','or','for','per','in','if','end','else','name','desc','tag','tags','Box','Boxes','Building','Buildings','Resource','Resources','Button','Buttons','Shiny','Shinies','Upgrade','Upgrades','Item','Items','Achievement','Achievements','Setting','Settings','slot1','slot2','slot3','slot4','prop1','prop2','prop3','prop4'];
  3920. G.checkId=function(str)
  3921. {
  3922. //raise an error if str is not alphanumeric or collides with a reserved word
  3923. if (G.reservedKeywords.indexOf(str)!=-1) return G.parseError('"'+str+'" is a reserved keyword, pick another one!');
  3924. else if (str.length>20) return G.parseError('"'+str+'" is longer than 20 characters, the maximum allowed size for keys.');
  3925. else
  3926. {
  3927. var fail=false;
  3928. var letterFound=false;
  3929. for (var i=0;i<str.length;i++)
  3930. {
  3931. var it=str.charAt(i);
  3932. if (G.Kalphanum.indexOf(it)==-1) {fail=true;break;}
  3933. if (G.Kalpha.indexOf(it)!=-1) {letterFound=true;}
  3934. }
  3935. if (fail || !letterFound) return G.parseError('"'+str+'" is not a valid key. A key can only contain letters and digits.');
  3936. }
  3937. return true;
  3938. }
  3939. G.validateId=function(ids)
  3940. {
  3941. //perform G.checkId on the declaring token of a new Thing (in the form *key1|key2|key3)
  3942. var ids=ids.substring(1,ids.length).split('|');
  3943. for (var i in ids)
  3944. {
  3945. if (G.thingsByName[ids[i]]) return G.parseError('There is already something with the id "'+ids[i]+'" ('+G.thingsByName[ids[i]].name+').');
  3946. if (!G.checkId(ids[i])) return false;
  3947. }
  3948. return true;
  3949. }
  3950. G.Kclass=/^([a-z_]|-[a-z_-])[a-z\d_-]*$/i;
  3951. G.validateClasses=function(str)
  3952. {
  3953. //raise an error if str isn't composed of alphanumeric words separated by spaces
  3954. var bits=str.split(' ');
  3955. for (var i in bits)
  3956. {
  3957. if (!G.Kclass.test(bits[i])) return G.parseError('"'+bits[i]+'" is not a valid CSS class name.');
  3958. }
  3959. return str;
  3960. }
  3961. G.validateVar=function(str)
  3962. {
  3963. if (str.charAt(0)!='$') return G.parseError('"'+str+'" is not a valid variable name.');
  3964. str=str.substring(1);
  3965. if (str.length<=0) return G.parseError('"'+str+'" is too short for a variable name.');
  3966. var fail=false;
  3967. for (var i=0;i<str.length;i++)
  3968. {
  3969. var it=str.charAt(i);
  3970. if (G.Kvar.indexOf(it)==-1) {fail=true;break;}
  3971. }
  3972. str='$'+str;
  3973. if (fail) return G.parseError('"'+str+'" is not a valid variable name as it is not alphanumeric.');
  3974. return str;
  3975. }
  3976.  
  3977. G.makeSafe=function(str)
  3978. {
  3979. //makes the string okay to use in html without triggering any tags, hopefully
  3980. str=str||'';
  3981. str=str.replaceAll('&','&amp;');
  3982. str=str.replaceAll('<','&lt;');
  3983. str=str.replaceAll('>','&gt;');
  3984. str=str.replaceAll('"','&quot;');
  3985. str=str.replaceAll('\'','&apos;');
  3986. return str;
  3987. }
  3988. G.makeUnsafe=function(str)
  3989. {
  3990. str=str||'';
  3991. str=str.replaceAll('&lt;','<');
  3992. str=str.replaceAll('&gt;','>');
  3993. str=str.replaceAll('&quot;','"');
  3994. str=str.replaceAll('&apos;','\'');
  3995. str=str.replaceAll('&amp;','&');
  3996. return str;
  3997. }
  3998. G.makeHTML=function(str)
  3999. {
  4000. //G.makeSafe, plus additional stuff
  4001. //replace pseudotags
  4002. str=G.makeSafe(str);
  4003. str=str.replace(/&lt;\/&gt;/gi,'<br>');
  4004. str=str.replace(/&lt;\/\/&gt;/gi,'</div><div>');
  4005. str=str.replace(/&lt;\.&gt;/gi,'</div><div>&bull; ');
  4006. str=str.replace(/&lt;t&gt;/gi,'<div class="title">').replace(/&lt;\/t&gt;/gi,'</div>');
  4007. str=str.replace(/&lt;b&gt;/gi,'<b>').replace(/&lt;\/b&gt;/gi,'</b>');
  4008. str=str.replace(/&lt;i&gt;/gi,'<i>').replace(/&lt;\/i&gt;/gi,'</i>');
  4009. str=str.replace(/&lt;u&gt;/gi,'<u>').replace(/&lt;\/u&gt;/gi,'</u>');
  4010. str=str.replace(/&lt;q&gt;/gi,'<q>').replace(/&lt;\/q&gt;/gi,'</q>');
  4011. str=str.replace(/&lt;#([0-9A-F]{3,6})&gt;/gi,'<span style="color:#$1;">').replace(/&lt;\/#&gt;/gi,'</span>');
  4012. return str;
  4013. }
  4014.  
  4015. //grabbers
  4016. G.grabThing=function(str)
  4017. {
  4018. //"bunny" will return the Thing with the key "bunny"
  4019. if (str=='this') return 'this';
  4020. else if (str.indexOf(':')!=-1)
  4021. {
  4022. //return G.thingsByTag[???]
  4023. return G.parseError('Selectors ("'+str+'") do not work for this command.');
  4024. }
  4025. else if (!G.thingsByName[str]) return G.parseError('Nothing found with the key "'+str+'".');
  4026. return G.thingsByName[str];
  4027. }
  4028. G.grabThings=function(str)
  4029. {
  4030. //output is always an array
  4031. //"bunny" will return the Thing with the key "bunny"
  4032. //"Upgrades" will return all Things with type "upgrade"
  4033. //"tag:animal" will return all Things with the tag "animal"
  4034. if (str=='this') return ['this'];
  4035. else if (str.indexOf(':')!=-1)//selector
  4036. {
  4037. var bits=str.split(':');
  4038. var type=0;
  4039. var tag=0;
  4040. var notTag=0;
  4041. var specials=[];
  4042. for (var i=0;i<bits.length;i++)
  4043. {
  4044. var me=bits[i];
  4045. if (me=='tag' && bits[i+1]) {i++;tag=G.makeSafe(bits[i]);}
  4046. else if (me=='notTag' && bits[i+1]) {i++;notTag=G.makeSafe(bits[i]);}
  4047. else if (me=='Buttons') type='button';
  4048. else if (me=='Resources') type='res';
  4049. else if (me=='Buildings') type='building';
  4050. else if (me=='Upgrades') type='upgrade';
  4051. else if (me=='Achievements') type='achiev';
  4052. else if (me=='Items') type='itemType';
  4053. else if (me=='Shinies') type='shiny';
  4054. else if (me=='All') {}
  4055. else if ( me=='owned'
  4056. || me=='notOwned'
  4057. ) specials.push(me);
  4058. else if (me=='') {}
  4059. else return G.parseError('In the selector "'+str+'", failed to understand "'+me+'".');
  4060. }
  4061. var arr=[];
  4062. for (var i in G.things)
  4063. {
  4064. var me=G.things[i];
  4065. if (
  4066. (!type || me.type==type)
  4067. && (!tag || me.tags.includes(tag))
  4068. && (!notTag || !me.tags.includes(notTag))
  4069. ) arr.push(me);
  4070. }
  4071. if (specials.length>0)
  4072. {
  4073. var obj={w:arr};
  4074. for (var i in specials){obj[specials[i]]=1;}
  4075. return obj;
  4076. }
  4077. else return arr;
  4078. }
  4079. else if (!G.thingsByName[str]) return G.parseError('Nothing found with the key "'+str+'".');
  4080. return [G.thingsByName[str]];
  4081. }
  4082. G.getThings=function(w,owner)
  4083. {
  4084. if (w.w)
  4085. {
  4086. var arr=[];
  4087. for (var i in w.w)
  4088. {
  4089. var me=w.w[i];
  4090. if ((!w.owned || (me.amount>0 || me.owned))
  4091. && (!w.notOwned || (me.amount<=0 || !me.owned))
  4092. ) arr.push(me);
  4093. }
  4094. return arr;
  4095. }
  4096. else if (w[0]=='this') return [owner];
  4097. //TODO : items
  4098. else return w;
  4099. }
  4100.  
  4101. G.KisFunc=('+-*/=<>()%!,:').split('');
  4102. G.isFunc=function(str)
  4103. {
  4104. //check if the string contains one of the above characters
  4105. for (var i=0;i<G.KisFunc.length;i++){if (str.indexOf(G.KisFunc[i])!=-1) return true;}
  4106. return false;
  4107. }
  4108. G.grabVar=function(str)
  4109. {
  4110. //"4" will return 4
  4111. //"bunny" will return the Thing with the key "bunny"
  4112. //"(4+bunnies*3)" will return a function that executes that operation
  4113. //"$local" will return the local variable named "local" (or 0 if not found)
  4114. if (str=='this') return 'this';
  4115. if (str.charAt(0)!='(' && G.isFunc(str)) str='('+str+')';
  4116. if (str.charAt(0)=='(' && str.charAt(str.length-1)==')')
  4117. {
  4118. var success=G.parseToFunc(str,1);
  4119. if (success=='ok') return {type:'f',f:G.parseToFunc(str)};
  4120. return G.parseError('Syntax error in "'+str+'" : '+success);
  4121. }
  4122. if (str.charAt(0)=='$') return {type:'l',l:str};
  4123. if (isNumber(str)) return parseFloat(str);
  4124. if (!G.thingsByName[str]) return G.parseError('"'+str+'" is not a valid thing, local variable, number, or expression.');
  4125. return G.thingsByName[str];
  4126. }
  4127. G.getVarValue=function(w,thing)
  4128. {
  4129. if (w=='this' && thing) return thing.amount;
  4130. else if (w.type)
  4131. {
  4132. var amount=0;
  4133. if (w.type=='building' || w.type=='res') amount=w.amount;
  4134. else if (w.type=='upgrade' || w.type=='achiev') amount=(w.owned?1:0);
  4135. if (w.type=='f')
  4136. {
  4137. if (!thing) amount=0;
  4138. else
  4139. {
  4140. if (thing.type=='building' || thing.type=='res') amount=thing.amount;
  4141. else if (thing.type=='upgrade' || thing.type=='achiev') amount=(thing.owned?1:0);
  4142. else if (thing.type=='button' || thing.type=='shiny') amount=thing.clicks;
  4143. }
  4144. return w.f(thing,amount);
  4145. }
  4146. else if (w.type=='l')
  4147. {
  4148. if (!G.localVars[w.l]) return 0;
  4149. else return G.localVars[w.l];
  4150. }
  4151. else return amount;
  4152. return 0;
  4153. }
  4154. else return w;
  4155. }
  4156.  
  4157. G.grabText=function(str)
  4158. {
  4159. var bits=[];
  4160. var bit='';
  4161. //chop into bits
  4162. var inTkn=false;
  4163. for (var i=0;i<str.length;i++)
  4164. {
  4165. var c=str.charAt(i);
  4166. var add=false;
  4167. if (c=='[')
  4168. {
  4169. if (inTkn>0) add=true;
  4170. else
  4171. {
  4172. bits.push({t:G.makeHTML(bit)});
  4173. bit='';
  4174. }
  4175. inTkn++;
  4176. }
  4177. else if (c==']' && inTkn>0)
  4178. {
  4179. inTkn--;
  4180. if (inTkn>0) add=true;
  4181. else
  4182. {
  4183. //bits.push({c:bit});
  4184. if (bit.indexOf('?')==0)//[?(test)|(exp1)|(exp2)]=>if (test) is >0, return (exp1), else return (exp2)
  4185. {
  4186. bit=bit.substring(1).split('|');
  4187. var X=bit[0]||0;
  4188. var Y=bit[1]||'';
  4189. var Z=bit[2]||'';
  4190. bits.push({i:G.grabVar(X),w1:G.grabText(Y),w2:G.grabText(Z)});
  4191. }
  4192. else if (bit.indexOf('s?')==0)//[s?(exp)]=>if (exp) is !=1, return "s"
  4193. {
  4194. bit=bit.substring(2);
  4195. var X=bit||0;
  4196. bits.push({s:G.grabVar(X)});
  4197. }
  4198. else if (bit.indexOf('n:')==0)//[n:thingId]=>returns the name of the given thing
  4199. {
  4200. bit=bit.substring(2);
  4201. var X=bit||0;
  4202. if (X=='this') bits.push({ns:true});
  4203. else
  4204. {
  4205. if (!G.thingsByName[X]) return G.parseError('"'+X+'" is not a valid thing.');
  4206. bits.push({n:G.thingsByName[X]});
  4207. }
  4208. }
  4209. else if (bit.indexOf('p:')==0)//[p:thingId]=>returns the plural of the given thing
  4210. {
  4211. bit=bit.substring(2);
  4212. var X=bit||0;
  4213. if (X=='this') bits.push({ps:true});
  4214. else
  4215. {
  4216. if (!G.thingsByName[X]) return G.parseError('"'+X+'" is not a valid thing.');
  4217. bits.push({p:G.thingsByName[X]});
  4218. }
  4219. }
  4220. else bits.push({w:G.grabVar(bit)});//[(exp)]=>value of exp
  4221. bit='';
  4222. }
  4223. }
  4224. else add=true;
  4225. if (add) bit+=c;
  4226. }
  4227. if (bit!='') bits.push({t:G.makeHTML(bit)});
  4228. //console.log(bits);
  4229. return bits;
  4230. }
  4231. G.getTextValue=function(w,thing,wrapMode)
  4232. {
  4233. var str='';
  4234. for (var i in w)
  4235. {
  4236. var bit=w[i];
  4237. if (bit.t) str+=bit.t;//raw text
  4238. else if (bit.i) str+=G.getVarValue(bit.i,thing)?G.getTextValue(bit.w1,thing,1):G.getTextValue(bit.w2,thing,1);//ternary
  4239. else if (bit.s) str+=(G.getVarValue(bit.s,thing)!=1)?'s':'';//s if !=1
  4240. else if (bit.w) str+=B(G.getVarValue(bit.w,thing));//var
  4241. else if (bit.n) str+=bit.n.name;//name
  4242. else if (bit.p) str+=bit.n.plural;//plural
  4243. else if (bit.ns) str+=thing.name;//name self
  4244. else if (bit.ps) str+=thing.plural;//plural self
  4245. }
  4246. if (wrapMode==1) return str;
  4247. else if (wrapMode==2) return '<div style="display:inline;">'+str+'</div>';
  4248. return '<div>'+str+'</div>';
  4249. }
  4250.  
  4251. G.applyIncludes=function(str)
  4252. {
  4253. var out='';
  4254. var lines=[];
  4255. str=str.replaceAll('[i:','[include ');
  4256. str=STR2(str);
  4257. while (str.val.length>0)
  4258. {
  4259. out+=str.gulpUntilSymbol('[include ',true);
  4260. if (str.val.length>0)
  4261. {
  4262. var bit=str.val;
  4263. var incStr='';
  4264. var inInclude=true;
  4265. var inStr=false;
  4266. var trailSpace=false;
  4267. for (var i=0;i<bit.length;i++)
  4268. {
  4269. var it=bit.charAt(i);
  4270. if (it==']' && !inStr) {inInclude=false;if (bit.charAt(i+1)==' '){trailSpace=true;};break;}
  4271. else if (it=='"' && inInclude) inStr=!inStr;
  4272. incStr+=it;
  4273. }
  4274.  
  4275. var bits=STR2(incStr);
  4276. var inc=G.includes[bits.gulp()];
  4277.  
  4278. var incContents=inc.mono?inc.text:inc.lines.join('///NEWLINE///');
  4279. var args={};
  4280. for (var i in inc.args){args[i]=inc.args[i];}
  4281. while(bits.val.length>0)
  4282. {
  4283. var arg=STR2(bits.gulp());
  4284. if (arg.gulpSymbol('%'))
  4285. {
  4286. var name=arg.gulpUntilSymbol('="',true);
  4287. var def=arg.gulpUntilSymbol('"',true);
  4288. if ((name in args) && def) args[name]=def;
  4289. }
  4290. }
  4291. for (var i in args)
  4292. {
  4293. incContents=incContents.replaceAll('[%'+i+']',args[i]||'???');
  4294. }
  4295. if (!inc.mono) {lines=incContents.split('///NEWLINE///');break;}
  4296. else out+=incContents;
  4297.  
  4298. if (trailSpace) out+=' ';
  4299. str.gulpSymbol(incStr+']');
  4300. }
  4301. }
  4302. if (lines.length>0) return lines; else return [out];
  4303. }
  4304.  
  4305. G.parse=function(toParse)
  4306. {
  4307.  
  4308. var game={
  4309. name:'Untitled',
  4310. author:'Anonymous',
  4311. desc:'',
  4312. created:0,
  4313. updated:0,
  4314. version:0,
  4315. forumPost:0,
  4316. costRate:1.15,
  4317. refundRate:0.5,
  4318. css:'',
  4319. stylesheets:[],
  4320. spritesheets:{},
  4321. noParticles:false,
  4322. noBulkParticles:false,
  4323. };
  4324.  
  4325. G.foundError=false;
  4326. G.context='when initializing';
  4327.  
  4328. G.line=0;
  4329.  
  4330. G.includes=[];
  4331.  
  4332. for (var STEP=0;STEP<2;STEP++)//pre-parse on the first step, then turn into proper lines on the second
  4333. {
  4334. if (STEP==0) G.context='when pre-parsing';
  4335. else G.context='when parsing';
  4336. var lines=toParse.split(String.fromCharCode(10));
  4337. var lines2=[];
  4338. var lineNum=0;
  4339. var lineNums=[];
  4340.  
  4341. var inComment=false;//inside a multiline comment
  4342.  
  4343. for (var i in lines)
  4344. {
  4345. var line=lines[i].trim();
  4346. if (line.length>0)
  4347. {
  4348. if (line.substring(0,2)=='/*') {inComment=true;}//multiline comment start
  4349. if (line.slice(-2)=='*/') {inComment=false;line='';/*line.slice(0,-2);*/}//multiline comment end
  4350. if (inComment || line.substring(0,2)=='//') {}//comment
  4351. else lines2.push(line);lineNums.push(lineNum);
  4352. }
  4353. lineNum++;
  4354. }
  4355. lines=lines2;
  4356. G.lineNums=lineNums;
  4357.  
  4358. if (STEP==1)
  4359. {
  4360. var i=0;
  4361. while(i<lines.length)
  4362. {
  4363. if (i<lines.length-1)
  4364. {
  4365. var next=lines[i+1];
  4366. var out=G.applyIncludes(next);
  4367. lines.splice(i+1,1);
  4368. for (var ii=out.length-1;ii>=0;ii--)
  4369. {
  4370. lines.splice(i+1,0,out[ii]);
  4371. }
  4372. }
  4373. i++;
  4374. }
  4375. }
  4376.  
  4377. //chop into blocks
  4378. var blockNames={
  4379. 'Let\'s make a game!': 'main',
  4380. 'Settings': 'settings',
  4381. 'CSS': 'css',
  4382. 'Includes': 'includes',
  4383. 'Layout': 'layout',
  4384. 'Resources': 'resources',
  4385. 'Buttons': 'buttons',
  4386. 'Buildings': 'buildings',
  4387. 'Upgrades': 'upgrades',
  4388. 'Items': 'items',
  4389. 'Achievements': 'achievs',
  4390. 'Shinies': 'shinies',
  4391. };
  4392. var contentBlocks=['Resources','Buttons','Buildings','Upgrades','Items','Achievements','Shinies'];
  4393. var blocksNamesById={};for (var i in blockNames){blocksNamesById[blockNames[i]]=i;}
  4394. var blocks={};
  4395. var block=0;
  4396. var prevBlock=0;
  4397. for (var i in lines)
  4398. {
  4399. var me=lines[i];
  4400. if (blockNames[me])
  4401. {
  4402. prevBlock=block;
  4403. block=blockNames[me];
  4404. if (!blocks[block]) blocks[block]=[];
  4405. if (contentBlocks.includes(me)) blocks[block].push('new block');
  4406. }
  4407. else if (block) {blocks[block].push(me);}
  4408. }
  4409.  
  4410. if (STEP==0 && blocks['includes'])
  4411. {
  4412. var thing=0;
  4413. var i=0;
  4414. //for (var i in blocks['includes'])
  4415. while(i<blocks['includes'].length)
  4416. {
  4417. var line=STR2(blocks['includes'][i]);
  4418. if (line.gulpSymbol('*include '))
  4419. {
  4420. if (thing!=0) G.includes[thing.name]=thing;
  4421. thing={};
  4422. thing.name=line.gulp();
  4423. thing.args={};
  4424. while(line.val.length>0)
  4425. {
  4426. if (line.gulpSymbol(':'))//mono (only one line)
  4427. {
  4428. thing.mono=true;
  4429. thing.text=line.val;
  4430. G.includes[thing.name]=thing;
  4431. thing=0;
  4432. }
  4433. else
  4434. {
  4435. var arg=STR2(line.gulp());
  4436. if (arg.gulpSymbol('%'))
  4437. {
  4438. var name=arg.gulpUntilSymbol('="',true);
  4439. var def=arg.gulpUntilSymbol('"',true);
  4440. if (def) thing.args[name]=def;
  4441. else thing.args[name]=0;
  4442. }
  4443. }
  4444. }
  4445. if (thing)//still in thing therefore not mono
  4446. {
  4447. thing.lines=[];
  4448. }
  4449. }
  4450. else if (thing)
  4451. {
  4452. thing.lines.push(line.val);
  4453. }
  4454.  
  4455. if (i<blocks['includes'].length-1)
  4456. {
  4457. var next=blocks['includes'][i+1];
  4458. var out=G.applyIncludes(next);
  4459. blocks['includes'].splice(i+1,1);
  4460. for (var ii=out.length-1;ii>=0;ii--)
  4461. {
  4462. blocks['includes'].splice(i+1,0,out[ii]);
  4463. }
  4464. }
  4465. i++;
  4466. if (i>1000) {console.log('TOO MUCH INCLUDING HAPPENING!!! THERE IS A BUG SOMEWHERE PROBABLY AAAAAHHHH');i+=10000;}
  4467. }
  4468. if (thing!=0) G.includes[thing.name]=thing;
  4469. }
  4470. delete blocks['includes'];
  4471.  
  4472. if (STEP==0)
  4473. {
  4474. //default layout
  4475. if (!blocks['layout'] || blocks['layout'][0]=='use default' || blocks['layout'].length==0)
  4476. {
  4477. if (blocks['layout'] && blocks['layout'][0]=='use default') blocks['layout'].splice(0,1);
  4478. blocks['layout']=((
  4479. `*main
  4480. contains:res, buttons
  4481. *res
  4482. contains:Resources
  4483. class:fullWidth
  4484. *buttons
  4485. contains:Buttons
  4486. *store
  4487. contains:buildings, upgrades
  4488. *buildings
  4489. contains:BulkDisplay, Buildings
  4490. header:<t>Buildings</t>
  4491. tooltip origin:left
  4492. *upgrades
  4493. contains:Upgrades
  4494. header:<t>Upgrades</t>
  4495. costs:hide
  4496. names:hide`
  4497. ).split(String.fromCharCode(10))).concat(blocks['layout']);
  4498. }
  4499. //paste back into 1 string
  4500. toParse='';
  4501. for (var i in blocks)
  4502. {
  4503. toParse+=blocksNamesById[i]+String.fromCharCode(10);
  4504. toParse+=blocks[i].join(String.fromCharCode(10))+String.fromCharCode(10);
  4505. }
  4506. }
  4507. }
  4508.  
  4509. if (lines[0]!='Let\'s make a game!') return G.parseError('A valid source file must begin with "Let\'s make a game!"');
  4510.  
  4511. var block='main';
  4512. var thing=0;//not an actual Thing at this stage; this is an anonymous object that gets filled with properties as we define them; when we change blocks or start declaring another Thing, this gets turned into an actual Thing
  4513. var virtualLineNum=0;
  4514.  
  4515. var inEffects=false;//inside a thing's effects: / end effects
  4516. var effectsType=0;//the type of the current effect block; can be "click", "tick", "sell", "c:customEffectName" etc
  4517. var ends=0;//+1 everytime we start a new effect or condition block, -1 if we find "end"
  4518.  
  4519. G.baseThing=0;//this is a pseudo-thing which can be defined in a section, making all subsequent Things use it as a base
  4520.  
  4521.  
  4522. for (var block in blocks)
  4523. {
  4524. thing=0;ok=1;G.baseThing=0;
  4525. for (var i in blocks[block])
  4526. {
  4527. G.line=virtualLineNum;
  4528. var me=blocks[block][i];//lines[i];
  4529. //G.context='on line '+(G.lineNums[G.line]||'?')+', "'+G.shorten(me,30)+'"';
  4530. G.context='on line "'+G.shorten(me,30)+'"';//line numbers are unreliable in the current system
  4531.  
  4532. var ok=0;
  4533.  
  4534. //if (me.substring(0,2)=='/*') {ok=1;inComment=true;}//multiline comment start
  4535. //if (me.slice(-2)=='*/') {ok=1;inComment=false;}//multiline comment end
  4536. //if (ok || inComment || me.substring(0,2)=='//') {ok=1;}//comment
  4537. if (true)
  4538. {
  4539. var prop=me.substring(0,me.indexOf(':')).toLowerCase();
  4540. var val=me.substring(me.indexOf(':')+1);
  4541. //we don't run G.makeSafe on the initial val because some commands, such as expressions, may use different handlers
  4542. if (me=='new block') {G.baseThing=0;ok=1;}
  4543. else if (block=='main')
  4544. {
  4545. ok=1;
  4546. if (prop=='name') game.name=G.makeSafe(val);
  4547. else if (prop=='by') game.author=G.makeSafe(val);
  4548. else if (prop=='desc') game.desc=val;
  4549. else if (prop=='created') game.created=G.makeSafe(val);
  4550. else if (prop=='updated') game.updated=G.makeSafe(val);
  4551. else if (prop=='version') game.version=parseFloat(val);
  4552. else if (prop=='forum post') game.forumPost=parseInt(val);
  4553. else ok=0;
  4554. }
  4555. else if (block=='settings')
  4556. {
  4557. ok=1;
  4558. if (prop=='background')
  4559. {
  4560. game.background=G.makeSafe(val);
  4561. }
  4562. else if (prop=='tiling background')
  4563. {
  4564. game.background=G.makeSafe(val);
  4565. game.backgroundTile=true;
  4566. }
  4567. else if (prop=='spritesheet')
  4568. {
  4569. var sprites=G.makeSafe(val).split(', ');
  4570. game.spritesheets[sprites[0]]={name:sprites[0],w:parseInt(sprites[1].split(' by ')[0]),h:parseInt(sprites[1].split(' by ')[1]),url:sprites[2]};
  4571. }
  4572. else if (prop=='stylesheet')
  4573. {
  4574. game.stylesheets.push(val);
  4575. }
  4576. else if (prop=='bloom')
  4577. {
  4578. G.bloomFilter=Math.min(100,Math.max(0,parseFloat(val)))/200;
  4579. }
  4580. else if (prop=='building cost increase') game.costRate=parseFloat(val.replace('%',''))/100;
  4581. else if (prop=='building cost refund') game.refundRate=parseFloat(val.replace('%',''))/100;
  4582. else if (me=='no particles') game.noParticles=true;
  4583. else if (me=='no bulk particles') game.noBulkParticles=true;
  4584. }
  4585. else if (block=='css')
  4586. {
  4587. ok=1;
  4588. if (me) game.css+=''+/*G.makeSafe*/(me)+String.fromCharCode(10);
  4589. }
  4590. else if (block=='layout' || block=='resources' || block=='buttons' || block=='shinies' || block=='buildings' || block=='upgrades' || block=='items' || block=='achievs')
  4591. {
  4592. //create and edit things
  4593. ok=1;
  4594. var effects=thing.effects;
  4595. if (me.charAt(0)=='*')
  4596. {
  4597. G.createThing(thing);
  4598.  
  4599. if (me=='*TEMPLATE'){}
  4600. else if (!G.validateId(me)) return false;
  4601.  
  4602. var type='';
  4603. if (block=='layout') type='box';
  4604. if (block=='resources') type='res';
  4605. else if (block=='buttons') type='button';
  4606. else if (block=='buildings') type='building';
  4607. else if (block=='upgrades') type='upgrade';
  4608. else if (block=='items') type='itemType';
  4609. else if (block=='achievs') type='achiev';
  4610. else if (block=='shinies') type='shiny';
  4611. thing={type:type};
  4612. thing.ids=me;
  4613.  
  4614. if (thing.type=='box')
  4615. {
  4616. thing.showCosts=true;
  4617. thing.showNames=true;
  4618. thing.showIcons=true;
  4619. thing.showPs=true;
  4620. }
  4621. else
  4622. {
  4623. thing.effects={};
  4624. /*
  4625. .effects is groups of effects by effectType
  4626. ie.:
  4627. .effects={
  4628. 'click':['effect text 1','effect text 2'],
  4629. 'tick':['effect text 1','effect text 2'],
  4630. };
  4631. the effect texts are parsed into actual effects later on
  4632. */
  4633. thing.costs=[];
  4634. thing.reqs=[];
  4635. thing.costRate=game.costRate;
  4636. thing.refundRate=game.refundRate;
  4637.  
  4638. if (thing.type=='shiny')
  4639. {
  4640. thing.freq=0;
  4641. thing.freqV=0;
  4642. thing.dur=10;//default to 10 secs duration
  4643. thing.moves=[];
  4644. }
  4645. }
  4646. }
  4647. //boxes only
  4648. else if (!thing) G.parseError('Trying to edit something, but there is nothing to edit.');
  4649. else if (type=='box')
  4650. {
  4651. if (prop=='contains')
  4652. {
  4653. if (!thing.children){thing.children=[];}
  4654. val=G.makeSafe(val).split(', ');
  4655. for (var ii in val){thing.children.push(val[ii]);}
  4656. }
  4657. else if (prop=='in')
  4658. {
  4659. thing.isIn=G.makeSafe(val);
  4660. }
  4661. else if (prop=='costs') thing.showCosts=(val=='show'?true:false);
  4662. else if (prop=='names') thing.showNames=(val=='show'?true:false);
  4663. else if (prop=='icons') thing.showIcons=(val=='show'?true:false);
  4664. else if (prop=='ps') thing.showPs=(val=='show'?true:false);
  4665. else if (prop=='header') thing.header=val;
  4666. else if (prop=='footer') thing.footer=val;
  4667. else if (prop=='class') thing.classes=G.validateClasses(val);
  4668. else if (prop=='tooltip origin' && (val=='top' || val=='bottom' || val=='left' || val=='right')) thing.tooltipOrigin=G.makeSafe(val);
  4669. else if (prop=='tooltip class') thing.tooltipClasses=G.validateClasses(val);
  4670. else if (me=='no tooltip') thing.noTooltip=true;
  4671. else ok=0;
  4672. }
  4673. //general
  4674. else if (prop=='name') {val=val.split('|');thing.name=G.makeSafe(val[0]);thing.plural=(G.makeSafe(val[1])||val[0]);thing.customName=true;}
  4675. else if (prop=='desc') thing.desc=val;
  4676. else if (prop=='text') thing.text=val;
  4677. else if (prop=='tag' || prop=='tags') thing.tags=G.makeSafe(val);
  4678. else if (prop=='class') thing.classes=G.validateClasses(val);
  4679. else if (prop=='icon class') thing.iconClasses=G.validateClasses(val);
  4680. else if (prop=='icon') thing.icon=G.makeSafe(val);
  4681. else if (prop=='start with' && (thing.type=='res' || thing.type=='building')) thing.startWith=parseFloat(val);
  4682. else if ((me=='start with' || me=='owned') && (thing.type=='upgrade' || thing.type=='achiev')) thing.startWith=true;
  4683. else if (prop=='is always' && (thing.type=='res' || thing.type=='building')) {thing.isAlways=val;}
  4684. else if (me=='can be negative' && (thing.type=='res')) {thing.canBeNegative=true;}
  4685. else if (me=='show max' && (thing.type=='res' || thing.type=='building')) thing.showMax=true;
  4686. else if (me=='show earned' && (thing.type=='res')) thing.showEarned=true;
  4687. else if (me=='show clicks' && thing.type=='button') thing.showClicks=true;
  4688. else if (prop=='cost') thing.costs.push((val));
  4689. else if (prop=='cost increase') thing.costRate=parseFloat(val.replace('%',''))/100;
  4690. else if (prop=='cost refund') thing.refundRate=parseFloat(val.replace('%',''))/100;
  4691. else if (prop=='req') thing.reqs.push((val));
  4692. else if (me=='unlocked') thing.unlocked=true;//(val=='true' || val=='yes');
  4693. else if (me=='no text') thing.noText=true;
  4694. else if (me=='no click') thing.noClick=true;
  4695. else if (me=='shown') thing.show=1;
  4696. else if (me=='hidden') thing.show=0;
  4697. else if (me=='lit') thing.lit=1;
  4698. else if (me=='dim') thing.lit=0;
  4699. else if (me=='always hidden') thing.alwaysHidden=1;
  4700. else if (me=='hidden when 0') thing.hiddenWhen0=true;
  4701. else if (me=='no buy') thing.noBuy=true;
  4702. else if (prop=='limit' && thing.type=='building') thing.limit=val;
  4703. else if (prop=='movement' && thing.type=='shiny') thing.moves=G.makeSafe(val).split(' ');
  4704. else if (prop=='frequency' && thing.type=='shiny') thing.freq=parseFloat(val);
  4705. else if (prop=='frequency variation' && thing.type=='shiny') thing.freqV=parseFloat(val);
  4706. else if (prop=='duration' && thing.type=='shiny') thing.dur=parseFloat(val);
  4707. //effects
  4708. else if (prop=='passive' || prop.indexOf('on ')==0)
  4709. {
  4710. if (inEffects || ends>0) G.parseError('Attempted to start an effect inside another effect.');
  4711. else
  4712. {
  4713. ends++;
  4714. inEffects=true;
  4715. var type='tick';//"passive" just redirects to "on tick"
  4716. if (prop!='passive') type=prop.substring(3);
  4717. /*if ( type=='passive'
  4718. || type=='click'
  4719. || type=='tick'
  4720. || type=='start'
  4721. || type=='save'
  4722. || type=='load'
  4723. || type=='earn'
  4724. || type=='lose'
  4725. ) effectsType=type;
  4726. else G.parseError('The effect type "'+type+'" was not recognized.');*/
  4727. if (G.validateId(type)) effectsType=type; else G.parseError('The effect type "'+type+'" is invalid.');
  4728. if (val && val.length>0)//1-line effect
  4729. {
  4730. if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
  4731. thing.effects[effectsType].push(val);
  4732. ends--;inEffects=false;effectsType=0;
  4733. }
  4734. else
  4735. {
  4736. if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
  4737. }
  4738. }
  4739. }
  4740. else if (me=='end')
  4741. {
  4742. ends--;
  4743. if (ends>0 && effectsType)
  4744. {
  4745. //end to a condition inside an effect block
  4746. thing.effects[effectsType].push(me);
  4747. }
  4748. else if (ends==0 && effectsType)
  4749. {
  4750. //end to an effect block
  4751. //if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
  4752. //thing.effects[effectsType].push(val);
  4753. inEffects=false;effectsType=0;
  4754. }
  4755. else if (ends<0) G.parseError('Tried to end conditions or effects when there were none started.');
  4756. }
  4757. else if (inEffects && effectsType)
  4758. {
  4759. if (me.indexOf('if (')==0)
  4760. {
  4761. if (me.charAt(me.length-1)==')') ends++;//only ends++ for multi-line condition blocks
  4762. }
  4763. thing.effects[effectsType].push(me);
  4764. }
  4765. else if (me.indexOf('if (')==0 || me.indexOf('else if (')==0) G.parseError('Tried to start a condition outside of an effect block.');
  4766. //visual
  4767. else if (prop=='tooltip origin' && (val=='top' || val=='bottom' || val=='left' || val=='right')) thing.tooltipOrigin=G.makeSafe(val);
  4768. else if (prop=='tooltip class') thing.tooltipClasses=G.validateClasses(val);
  4769. else if (me=='no tooltip') thing.noTooltip=true;
  4770. else ok=0;
  4771. }
  4772. }
  4773. if (G.foundError || !ok) return G.parseError('Could not parse "'+G.shorten(me,30)+'".');
  4774. virtualLineNum++;
  4775. }
  4776. G.createThing(thing);
  4777. if (inEffects || ends>0) return G.parseError('An effects block was not closed properly.');
  4778. G.baseThing=0;
  4779. }
  4780.  
  4781. G.spritesheets=game.spritesheets||{};
  4782.  
  4783.  
  4784. //turn text into valid text objects
  4785. if (game.desc) game.desc=G.grabText(game.desc);
  4786. var props=['header','footer'];
  4787. for (var i in G.boxes)
  4788. {
  4789. var me=G.boxes[i];
  4790. G.context='when parsing text for the box "'+G.shorten(me.key,30)+'"';
  4791. for (var ii in props)
  4792. {
  4793. if (me[props[ii]]) me[props[ii]]=G.grabText(me[props[ii]]);
  4794. }
  4795. }
  4796. var props=['desc','text'];
  4797. for (var i in G.things)
  4798. {
  4799. var me=G.things[i];
  4800. G.context='when parsing text for the thing "'+G.shorten(me.name,30)+'"';
  4801. for (var ii in props)
  4802. {
  4803. if (me[props[ii]]) me[props[ii]]=G.grabText(me[props[ii]]);
  4804. }
  4805. }
  4806.  
  4807.  
  4808. //clean icons, costs, reqs and effects
  4809.  
  4810. var gulpCond=function(str)
  4811. {
  4812. //get whatever is within the next "if ()", parse it into a function and return that function
  4813. if (str.gulp('if') && str.gulpSymbol('('))
  4814. {
  4815. var parens=1;
  4816. var cond='';
  4817. //find matching parens
  4818. for (var i=0;i<str.val.length;i++)
  4819. {
  4820. var it=str.val.charAt(i);
  4821. if (it=='(') parens++;
  4822. else if (it==')') parens--;
  4823. if (parens==0) {break;}
  4824. cond+=it+'';
  4825. }
  4826. if (cond!='')
  4827. {
  4828. str.gulpSymbol(cond+')');
  4829. return G.grabVar('('+cond+')');
  4830. }
  4831. }
  4832. return false;
  4833. }
  4834.  
  4835. for (var i in G.things)
  4836. {
  4837. var me=G.things[i];
  4838.  
  4839. if (me.icon)
  4840. {
  4841. G.context='when getting the icon for the thing "'+G.shorten(me.name,30)+'"';
  4842. var icons=[];
  4843. var icon=me.icon.split(' ');
  4844. for (var ii in icon)
  4845. {
  4846. if (icon[ii].charAt(icon[ii].length-1)==']')//tile
  4847. {
  4848. var sheet=G.spritesheets[icon[ii].split('[')[0]];
  4849. var coords=icon[ii].substring(icon[ii].indexOf('[')+1,icon[ii].indexOf(']')).split(',');
  4850. icons.push({tile:true,url:sheet.url,x:-parseInt(coords[0])*sheet.w,y:-parseInt(coords[1])*sheet.h});
  4851. }
  4852. else icons.push({url:icon[ii],x:0,y:0});//plain image
  4853. }
  4854. me.icon=icons;
  4855. }
  4856.  
  4857. if (me.type=='shiny')
  4858. {
  4859. G.context='when preparing the shiny "'+G.shorten(me.name,30)+'"';
  4860. var moves={};
  4861. var recMoves=('anywhere,onRight,onLeft,onTop,onBottom,onMouse,onThing,onBox,followMouse,followMouseSlow,wiggle,pulse,randomAngle,spinCW,spinCCW,spinRandom,fade,grow,shrink,growShrink,moveRandom,moveLeft,moveRight,moveTop,moveBottom,bobVertical,bobHorizontal,bounce').split(',');
  4862. var wMoves=('onThing,onBox').split(',');//moves that accept a thing instead of a number
  4863. for (var ii in me.moves)
  4864. {
  4865. var it=me.moves[ii].split(':');
  4866. if (recMoves.indexOf(it[0])==-1) G.parseError('"'+it[0]+'" is not a recognized movement type.');
  4867. else if(wMoves.indexOf(it[0])!=-1 && !G.thingsByName[it[1]]) G.parseError('The movement type "'+me.moves[ii]+'" has an invalid parameter.');
  4868. else if(wMoves.indexOf(it[0])!=-1) moves[it[0]]=G.thingsByName[it[1]];
  4869. else moves[it[0]]=parseFloat(it[1]||0);
  4870. }
  4871. me.moves=moves;
  4872. }
  4873.  
  4874. if (me.noClick)
  4875. {
  4876. if (!me.classes) me.classes='noClick';
  4877. else me.classes+=' noClick';
  4878. }
  4879.  
  4880. G.context='when preparing the thing "'+G.shorten(me.name,30)+'"';
  4881. if (me.isAlways) me.isAlways=G.grabVar('('+me.isAlways+')').f;
  4882. if (me.limit) me.limit=G.grabVar('('+me.limit+')').f;
  4883.  
  4884. var condStack=[];//the first one is the condition effect currently being filled
  4885.  
  4886. //breaking up one-line effects and pre-parsing
  4887. for (var effectType in me.effects)
  4888. {
  4889. G.context='when parsing the effect block "'+G.shorten(effectType,30)+'" for the thing "'+G.shorten(me.name,30)+'"';
  4890. var list=[];
  4891. var effs=me.effects[effectType];
  4892. var elses=0;
  4893. for (var ii in effs)
  4894. {
  4895. var cond=0;
  4896. var str=STR2(effs[ii]);
  4897. if (str.val=='end' && elses>0)
  4898. {
  4899. //double ends if we're closing an else
  4900. for (var iii=0;iii<elses;iii++) list.push({type:'?',eff:'end'});
  4901. elses--;
  4902. }
  4903. if (str.val=='else')
  4904. {
  4905. list.push({type:'else',effs:[]});
  4906. str.val='';
  4907. elses++;
  4908. }
  4909. else
  4910. {
  4911. var isElse=str.gulp('else');
  4912. if (isElse) elses++;
  4913. cond=gulpCond(str);
  4914. if (cond.type && cond.type=='f')
  4915. {
  4916. list.push({type:(isElse?'else':'if'),cond:cond.f,effs:[],or:[]});
  4917. }
  4918. }
  4919. if (str.val.length>0)
  4920. {
  4921. list.push({type:'?',eff:str.val});
  4922. if (cond) list.push({type:'?',eff:'end'});
  4923. }
  4924. if (G.foundError) return false;
  4925. }
  4926. me.effects[effectType]=list;
  4927. }
  4928.  
  4929. for (var effectType in me.effects)
  4930. {
  4931. G.context='when parsing the effect block "'+G.shorten(effectType,30)+'" for the thing "'+G.shorten(me.name,30)+'"';
  4932. var list=[];
  4933. var effs=me.effects[effectType];
  4934. for (var ii in effs)
  4935. {
  4936. var eff=effs[ii];
  4937. if (eff.type=='if' || eff.type=='else')
  4938. {
  4939. if (eff.type=='else' && condStack.length>0) condStack[0].or.push(eff);
  4940. else if (condStack.length>0) condStack[0].effs.push(eff);
  4941. else list.push(eff);
  4942. condStack.unshift(eff);
  4943. }
  4944. if (eff.type=='?')
  4945. {
  4946. var str=STR2(eff.eff);
  4947. delete eff.eff;
  4948.  
  4949. var add=true;
  4950.  
  4951. var cmd=str.val;
  4952.  
  4953. if (str.val=='end')
  4954. {
  4955. add=false;
  4956. if (condStack.length>0) condStack.shift();
  4957. eff.type='end';
  4958. }
  4959. else
  4960. {
  4961. var whatDo=0;
  4962. if (str.gulpSymbol('log('))
  4963. {
  4964. var classes=G.validateClasses(str.gulpUntilSymbol(')'));
  4965. eff={type:'log',w:G.grabText(str.gulpUntil()),classes:classes};
  4966. }
  4967. else if (str.gulp('log')) eff={type:'log',w:G.grabText(str.gulpUntil())};
  4968. else if (str.val==('clear log') || str.val==('clean log')) eff={type:'clear log'};
  4969. else if (str.gulp('toast')) eff={type:'toast',w:G.grabText(str.gulpUntil())};
  4970. else if (str.gulp('echo')) eff={type:'echo',w:G.grabText(str.gulpUntil())};
  4971. else if (str.gulp('eval')) eff={type:'eval',w:str.gulpUntil()};
  4972.  
  4973. else if (str.gulp('do'))
  4974. {eff={type:'do'};whatDo='do X with Ys';}
  4975.  
  4976. else if (str.gulp('anim icon')) eff={type:'animicon',w:G.validateClasses(str.gulpUntil())};
  4977. else if (str.gulp('anim')) eff={type:'anim',w:G.validateClasses(str.gulpUntil())};
  4978.  
  4979. else if (str.gulp('light')) {eff={type:'light'};whatDo='do Xs';}
  4980. else if (str.gulp('dim')) {eff={type:'dim'};whatDo='do Xs';}
  4981. else if (str.gulp('show')) {eff={type:'show'};whatDo='do Xs';}
  4982. else if (str.gulp('hide')) {eff={type:'hide'};whatDo='do Xs';}
  4983.  
  4984. else if (str.gulp('spawn'))
  4985. {eff={type:'spawn'};whatDo='do X';}
  4986.  
  4987. else if (str.gulp('yield') || str.gulp('gain') || str.gulp('win') || str.gulp('get'))
  4988. {eff={type:'yield'};whatDo='do X Ys';}
  4989.  
  4990. else if (str.gulp('lose'))
  4991. {eff={type:'lose'};whatDo='do X Ys';}
  4992.  
  4993. else if (str.gulp('grant'))
  4994. {eff={type:'grant'};whatDo='do X Y';}
  4995.  
  4996. else if (str.gulp('increase cost of'))
  4997. {eff={type:'increase cost'};whatDo='change Xs by Y';}
  4998. else if (str.gulp('lower cost of'))
  4999. {eff={type:'lower cost'};whatDo='change Xs by Y';}
  5000. else if (str.gulp('multiply cost of'))
  5001. {eff={type:'multiply cost'};whatDo='change Xs by Y';}
  5002. else if (str.gulp('multiply refund of'))
  5003. {eff={type:'multiply refund'};whatDo='change Xs by Y';}
  5004.  
  5005. else if (str.gulp('increase yield of') || str.gulp('increase gain of'))
  5006. {eff={type:'increase yield'};whatDo='change Xs by Y';}
  5007. else if (str.gulp('lower yield of') || str.gulp('lower gain of'))
  5008. {eff={type:'lower yield'};whatDo='change Xs by Y';}
  5009. else if (str.gulp('multiply yield of') || str.gulp('multiply gain of'))
  5010. {eff={type:'multiply yield'};whatDo='change Xs by Y';}
  5011.  
  5012. else if (str.gulp('multiply duration of'))
  5013. {eff={type:'multiply duration'};whatDo='change Xs by Y';}
  5014. else if (str.gulp('multiply frequency of'))
  5015. {eff={type:'multiply frequency'};whatDo='change Xs by Y';}
  5016.  
  5017. if (eff.type=='?')
  5018. {
  5019. //this one is a bit annoying
  5020. var tmp=STR2(str.val);
  5021. var type=tmp.gulp();
  5022. if (type=='increase' || type=='lower' || type=='multiply')
  5023. {
  5024. var Z=tmp.gulp();
  5025. if (tmp.gulp('yield of') || tmp.gulp('gain of'))
  5026. {
  5027. eff={type:type+' yield for'};whatDo='change Zs for Xs by Y';
  5028. str.val=Z+' for '+tmp.val;
  5029. }
  5030. }
  5031. }
  5032.  
  5033. if (eff.type=='?')//still nothing?
  5034. {
  5035. //test for "X is Y" or "X=Y"
  5036. str.val=str.val.replace(' = ','=');
  5037. str.val=str.val.replace('=',' = ');
  5038. var bits=str.val.split(' ');
  5039. if (bits[1] && (bits[1]=='is' || bits[1]=='are' || bits[1]=='=') && bits[0].length>0 && bits[2])
  5040. {
  5041. bits[1]='=';
  5042. str.val=bits.join(' ');
  5043. if (str.val.charAt(0)=='$') {eff={type:'set var'};whatDo='$X=Y';}
  5044. else {eff={type:'set'};whatDo='X=Y';}
  5045. }
  5046. }
  5047.  
  5048. if (whatDo=='X=Y')
  5049. {
  5050. //X is a selector, Y is a number or variable
  5051. var bits=str.val.split(' = ');
  5052. var X=bits[0]||0;
  5053. var Y=bits[1]||0;
  5054. if (X) eff.w=G.grabThings(X);
  5055. if (Y) eff.v=G.grabVar(Y);
  5056. }
  5057. else if (whatDo=='$X=Y')
  5058. {
  5059. //X is a local var, Y is a number or variable
  5060. var bits=str.val.split(' = ');
  5061. var X=bits[0]||0;
  5062. var Y=bits[1]||0;
  5063. if (X) eff.w=G.validateVar(X);
  5064. if (Y) eff.v=G.grabVar(Y);
  5065. }
  5066. else if (whatDo=='do X')
  5067. {
  5068. //X is a thing
  5069. var X=str.gulp();
  5070. if (X) eff.w=G.grabThing(X);
  5071. }
  5072. else if (whatDo=='do Xs')
  5073. {
  5074. //X is a selector
  5075. var X=str.gulp();
  5076. if (X) eff.w=G.grabThings(X);
  5077. }
  5078. else if (whatDo=='do X Y')
  5079. {
  5080. //X can be a number or a variable, but Y is always a thing
  5081. //X may be absent
  5082. var Y=str.val.split(' ');Y=Y[Y.length-1];
  5083. var X=str.gulpUntil(Y);
  5084. if (X) eff.v=G.grabVar(X);
  5085. if (Y) eff.w=G.grabThing(Y);
  5086. }
  5087. else if (whatDo=='do X Ys')
  5088. {
  5089. //X can be a number or a variable, but Y is always a selector
  5090. //X may be absent
  5091. var Y=str.val.split(' ');Y=Y[Y.length-1];
  5092. var X=str.gulpUntil(Y);
  5093. if (X) eff.v=G.grabVar(X);
  5094. if (Y) eff.w=G.grabThings(Y);
  5095. }
  5096. else if (whatDo=='change X by Y')
  5097. {
  5098. //X is a thing, Y is a number or variable
  5099. eff.w=G.grabThing(str.gulpUntil('by'));
  5100. eff.v=G.grabVar(str.gulpUntil());
  5101. }
  5102. else if (whatDo=='change Xs by Y')
  5103. {
  5104. //X is a selector, Y is a number or variable
  5105. eff.w=G.grabThings(str.gulpUntil('by'));
  5106. eff.v=G.grabVar(str.gulpUntil());
  5107. }
  5108. else if (whatDo=='change Zs for Xs by Y')
  5109. {
  5110. //X is a selector, Y is a number or variable, Z is a selector
  5111. eff.z=G.grabThings(str.gulpUntil('for'));
  5112. eff.w=G.grabThings(str.gulpUntil('by'));
  5113. eff.v=G.grabVar(str.gulpUntil());
  5114. }
  5115. else if (whatDo=='do X with Ys')
  5116. {
  5117. //X is an effect name, Y is a selector
  5118. if (str.val.indexOf(' with ')==-1)
  5119. {
  5120. eff.v=str.val;
  5121. eff.w=G.grabThings('this');
  5122. }
  5123. else
  5124. {
  5125. eff.v=str.gulpUntil('with');
  5126. eff.w=G.grabThings(str.gulpUntil());
  5127. }
  5128. if (!G.validateId(eff.v)) G.parseError('The effect type "'+eff.v+'" is invalid.');
  5129. //else if (eff.w && !eff.w.effects[eff.v]) G.parseError(eff.w.name+' does not have any effects named "'+eff.v+'".');
  5130. }
  5131. }
  5132. if (eff.type=='?') {add=false;G.parseError('Could not parse the effect "'+G.shorten(cmd,30)+'".');}
  5133.  
  5134. if (add)
  5135. {
  5136. if (condStack.length>0) condStack[0].effs.push(eff);
  5137. else list.push(eff);
  5138. }
  5139. }
  5140. if (G.foundError) return false;
  5141. }
  5142. me.effects[effectType]=list;
  5143. }
  5144.  
  5145. G.context='when parsing costs for the thing "'+G.shorten(me.name,30)+'"';
  5146. var list=[];
  5147. for (var ii in me.costs)
  5148. {
  5149. list.push(G.strToList(me.costs[ii]));
  5150. }
  5151. me.costs=list;
  5152.  
  5153. //construct a func that checks reqs
  5154. G.context='when parsing requirements for the thing "'+G.shorten(me.name,30)+'"';
  5155. if (me.reqs.length>0)
  5156. {
  5157. var str=me.reqs.join(', ');
  5158. str=str.replaceAll(', and ',',');
  5159. str=str.replaceAll(' and ',',');
  5160. str=str.replaceAll(', ',',');
  5161. var list=str.split(',');
  5162. var str='';
  5163. for (var ii in list)
  5164. {
  5165. var bits=list[ii].split(' ');
  5166. if (bits[2] && (bits[2]=='clicks' || bits[2]=='click')) {bits[2]=0;bits[1]+=':clicks';}
  5167. if (bits[2] && (bits[2]=='per') && bits[3] && (bits[3]=='second')) {bits[2]=0;bits[3]=0;bits[1]+=':ps';}
  5168. if (bits[0] && bits[1] && !bits[2] && isNumber(bits[0])) str+=bits[1]+' >= '+bits[0];
  5169. else str+=list[ii];
  5170. str+=' and ';
  5171. }
  5172. str='( '+str+' 1 )';
  5173. me.reqFunc=G.grabVar(str).f;
  5174. if (G.foundError) return false;
  5175. }
  5176. }
  5177.  
  5178. //setup game
  5179. G.context='when initializing data';
  5180. for (var i in G.things)
  5181. {
  5182. var me=G.things[i];
  5183. if (me.type!='box')
  5184. {
  5185. me.boostAdd=0;
  5186. me.boostMult=1;
  5187. me.boostAddFor=[];
  5188. me.boostMultFor=[];
  5189. me.costAdd=0;
  5190. me.costMult=1;
  5191. me.refundMult=1;
  5192. me.costAddFor=[];
  5193. me.costMultFor=[];
  5194. me.refundMultFor=[];
  5195. }
  5196. if (me.type=='button' || me.type=='shiny')
  5197. {
  5198. me.clicks=0;
  5199. }
  5200. if (me.type=='shiny')
  5201. {
  5202. me.timeLeft=-1;
  5203. me.durMult=1;
  5204. me.freqMult=1;
  5205. }
  5206. if (me.type=='res' || me.type=='building')
  5207. {
  5208. me.amount=0;
  5209. if (me.startWith) me.amount=me.startWith;
  5210. me.maxAmount=me.amount;
  5211. }
  5212. if (me.type=='res')
  5213. {
  5214. me.ps=0;
  5215. me.amountD=me.amount;
  5216. me.earned=me.amount;
  5217. }
  5218. if (me.type=='upgrade' || me.type=='achiev')
  5219. {
  5220. me.owned=0;
  5221. if (me.startWith) me.owned=1;
  5222. }
  5223. }
  5224.  
  5225. G.startDate=parseInt(Date.now());
  5226.  
  5227. //parse the load data if any
  5228. var loadedData=G.applyLoad();
  5229.  
  5230. //build layout
  5231.  
  5232. if (game.background) G.l.style.backgroundImage='url('+game.background+')';
  5233. if (game.backgroundTile) G.l.classList.add('tilingBackground'); else G.l.classList.remove('tilingBackground');
  5234.  
  5235.  
  5236. G.thingPlacement={
  5237. 'Resources':'none',
  5238. 'Buttons':'none',
  5239. 'Buildings':'none',
  5240. 'Upgrades':'none',
  5241. 'Achievements':'none',
  5242. 'Items':'none',
  5243. };
  5244.  
  5245. G.specialBoxes={
  5246. 'BulkDisplay':`<div id="bulkDisplay" class="box-bit"><div class="box-bit-content"></div></div>`,
  5247. 'Log':`<div id="log" class="box-bit"><div id="logOuter" class="box-bit-content"><div id="logInner"></div></div></div>`,
  5248. };
  5249.  
  5250. //put boxes inside each other
  5251. for (var i in G.boxes)
  5252. {
  5253. var me=G.boxes[i];
  5254. G.context='when placing the box "'+G.shorten(me.key,30)+'"';
  5255. me.name=me.key;
  5256. if (!me.children) me.children=[];
  5257. if (me.isIn)
  5258. {
  5259. var it=me.isIn;
  5260. if (G.thingsByName[it] && G.thingsByName[it].type=='box')
  5261. {
  5262. if (!G.thingsByName[it].children) G.thingsByName[it].children=[];
  5263. G.thingsByName[it].children.push(me.name);
  5264. }
  5265. else return G.parseError('Tried to put the box "'+me.name+'" in the box "'+it+'", but "'+it+'" is not an existing box.');
  5266. }
  5267. }
  5268. for (var i in G.boxes)
  5269. {
  5270. var me=G.boxes[i];
  5271. G.context='when placing the box "'+G.shorten(me.key,30)+'"';
  5272. for (var ii in me.children)
  5273. {
  5274. var it=me.children[ii];
  5275.  
  5276. //generic Thing keyword
  5277. if (G.thingPlacement[it]) G.thingPlacement[it]=me;
  5278. //special predefined elements
  5279. else if (G.specialBoxes[it]) {me.children[ii]={type:'special',str:G.specialBoxes[it]};}
  5280. //tag
  5281. else if (it.indexOf('tag:')==0){
  5282. var tag=it.slice(4);
  5283. me.children[ii]=tag;
  5284. G.thingPlacement[tag]=me;
  5285. }
  5286. //other box
  5287. else if (G.thingsByName[it] && G.thingsByName[it].type=='box') {G.thingsByName[it].parent=me;me.children[ii]=G.thingsByName[it];}
  5288. else return G.parseError('Tried to put "'+it+'" in the box "'+me.name+'", but "'+it+'" is not an existing box or a valid Thing keyword.');
  5289. }
  5290. }
  5291.  
  5292. //create dom
  5293. G.context='when placing boxes';
  5294. var rootBoxes=[];
  5295. for (var i in G.boxes)
  5296. {
  5297. var me=G.boxes[i];
  5298. if (!me.parent) rootBoxes.push(me);
  5299. }
  5300. var getBoxStr=function(box)
  5301. {
  5302. var str='';
  5303. if (box.type && box.type=='special')
  5304. {
  5305. return box.str;
  5306. }
  5307. else if (box.type && box.type=='box')
  5308. {
  5309. for (var i in box.children){str+=getBoxStr(box.children[i]);}
  5310. var content=str;
  5311. var classes='box';
  5312. if (box.classes) classes+=' '+box.classes;
  5313. str='<div class="'+classes+'" id="box-'+box.name+'">';
  5314. if (box.header) str+='<div class="box-header">'+G.getTextValue(box.header)+'</div>';
  5315. if (box.footer) str+='<div class="box-footer">'+G.getTextValue(box.footer)+'</div>';
  5316. str+=content;
  5317. str+='</div>';
  5318. return str;
  5319. }
  5320. else {return '<div class="box-things" id="box-things-'+box+'"></div>';}
  5321. }
  5322. var str='';
  5323. for (var i in rootBoxes)
  5324. {
  5325. str+=getBoxStr(rootBoxes[i]);
  5326. }
  5327. l('content').innerHTML=str;
  5328.  
  5329. for (var i in G.boxes)
  5330. {
  5331. var me=G.boxes[i];
  5332. me.l=l('box-'+me.name)||0;
  5333. me.childrenl={};
  5334. for (var ii in me.children)
  5335. {
  5336. var child=me.children[ii];
  5337. if (child.type && child.type=='box')
  5338. {}
  5339. else {me.childrenl[child]=l('box-things-'+child)||0;}
  5340. }
  5341. }
  5342.  
  5343. //place things
  5344. G.context='when placing things';
  5345. for (var i in G.things)
  5346. {
  5347. G.things[i].createDom();
  5348. }
  5349. for (var i in G.items)
  5350. {
  5351. G.items[i].createDom();
  5352. }
  5353.  
  5354. G.context='in the final stages of initialization';
  5355.  
  5356. //bulk display
  5357. G.bulkDisplay=l('bulkDisplay')||0;
  5358. if (G.bulkDisplay)
  5359. {
  5360. G.addTooltip(G.bulkDisplay,{func:function(){return '<div class="title">Bulk-buying and selling</div><div class="desc"><div>Buy 50 at once by pressing Shift.</div><div>Sell 1 by pressing Ctrl.</div><div>Sell 50 at once by pressing Shift+Ctrl.</div></div>';}});
  5361. G.bulkDisplay=G.bulkDisplay.getElementsByClassName('box-bit-content')[0];
  5362. }
  5363.  
  5364. //log
  5365. G.initLog();
  5366.  
  5367. //info and settings
  5368. var me=l('meta-button-info');
  5369. G.addTooltip(me,{func:function(){return '<div class="title">Info & Stats</div><div class="desc"><div>View information about this game, and statistics about your playthrough.</div></div>';}});
  5370. AddEvent(me,'click',function(){G.setMainPopup('info');});
  5371.  
  5372. var me=l('meta-button-settings');
  5373. G.addTooltip(me,{func:function(){return '<div class="title">Settings</div><div class="desc"><div>Import and export your game data, and edit settings for video, audio and gameplay.</div></div>';}});
  5374. AddEvent(me,'click',function(){G.setMainPopup('settings');});
  5375.  
  5376. G.darkenL=l('darken');
  5377. AddEvent(G.darkenL,'click',function(){G.setMainPopup();});
  5378.  
  5379. //setTimeout(function(){G.setMainPopup('settings');},100);
  5380.  
  5381. G.mainPopup=0;
  5382. G.mainPopupEl=0;
  5383. G.setMainPopup=function(what)
  5384. {
  5385. if (what && what!=G.mainPopup)
  5386. {
  5387. if (G.mainPopupEl) G.closePopup(G.mainPopupEl);
  5388. G.darkenL.className='on';
  5389. if (what=='settings')
  5390. {
  5391. var text=`
  5392. <div class="headerTitle">Settings</div>
  5393. <div style="padding:4px;overflow-y:auto;">
  5394.  
  5395. <div style="display:flex;justify-content:space-evenly;align-items:center;padding:8px;text-align:center;">
  5396. <div>`+
  5397. G.button({
  5398. text:'Save',
  5399. tooltip:'Save your game.<br>If Autosave is enabled, the game saves by itself every 30 seconds.<br>You may also save with Ctrl+S.',
  5400. onclick:function(e){
  5401. triggerAnim(e.target,'glow');
  5402. G.save();
  5403. },
  5404. })
  5405. +`<br>`+
  5406. G.button({
  5407. text:'Load',
  5408. tooltip:'Reload your game.',
  5409. onclick:function(e){
  5410. triggerAnim(e.target,'glow');
  5411. G.load();
  5412. },
  5413. })
  5414. +`</div>
  5415. <div>`+
  5416. G.button({
  5417. text:'Export',
  5418. tooltip:'Export your save data to a file.<br>Use this to backup your save or to share it with other players.',
  5419. onclick:function(e){
  5420. triggerAnim(e.target,'glow');
  5421. /*G.popup(0,{text:`
  5422. <div class="headerTitle">Export save</div>
  5423. <div style="padding:4px;overflow-y:auto;overflow-x:hidden;text-align:center;">
  5424. <div>This is your save code.<br>Copy it and keep it somewhere safe!</div>
  5425. <textarea id="textareaPrompt" style="margin-top:8px;width:100%;height:128px;overflow-x:hidden;
  5426. overflow-y:scroll;" readonly>`+(0)+`</textarea>
  5427. <div class="footerTitle hoverShine closesThePopup">Done</div>
  5428. </div>
  5429. `});*/
  5430. G.fileSave();
  5431. },
  5432. })
  5433. +`<br>`+
  5434. G.button({
  5435. text:'Import<input id="FileLoadInput" type="file" style="cursor:pointer;opacity:0;position:absolute;left:0px;top:0px;width:100%;height:100%;" onchange="G.fileLoad(event);"/>',
  5436. tooltip:'Import save data from a file that was previously exported.',
  5437. onclick:function(e){
  5438. triggerAnim(e.target,'glow');
  5439. /*G.popup(0,{text:`
  5440. <div class="headerTitle">Import save</div>
  5441. <div style="padding:4px;overflow-y:auto;overflow-x:hidden;text-align:center;">
  5442. <div>Please paste in the code that was given to you on save export.</div>
  5443. <textarea id="textareaPrompt" style="margin-top:8px;width:100%;height:128px;overflow-x:hidden;
  5444. overflow-y:scroll;">`+(0)+`</textarea>
  5445. <div class="footerTitle hoverShine closesThePopup">Load</div>
  5446. </div>
  5447. `});*/
  5448. },
  5449. })
  5450. +`</div>
  5451. <div>`+
  5452. G.button({
  5453. text:'Wipe',
  5454. classes:'red',
  5455. tooltip:'Wipe your data for this game.<br>You will lose all your progress.<br>This cannot be undone!',
  5456. onclick:function(e){
  5457. triggerAnim(e.target,'glow');
  5458. G.clear();
  5459. },
  5460. })
  5461. +`</div>
  5462. </div>
  5463.  
  5464. <div class="listing b">Autosave : `+G.makeTick({
  5465. val:function(){return G.getSetting('autosave');},
  5466. on:'On',off:'Off',
  5467. func:function(val){G.setSetting('autosave',val);},
  5468. tooltip:'If this is enabled, the game will auto-save every 30 seconds.',
  5469. })+`</div>
  5470. <div class="listing b">Number display : `+G.makeChoices({
  5471. val:function(){return G.getSetting('numdisp');},
  5472. func:function(val){G.setSetting('numdisp',val);},
  5473. list:
  5474. [
  5475. {text:'Shortest',tooltip:'Numbers will be displayed in the form<br><b>1k, 1T, 1UnD</b>.'},
  5476. {text:'Short',tooltip:'Numbers will be displayed in the form<br><b>1 thousand, 1 trillion, 1 undecillion</b>.'},
  5477. {text:'Full',tooltip:'Numbers will be displayed in the form<br><b>1,000, 1,000,000,000,000, 1e+36</b>.'},
  5478. ],
  5479. })+`</div>
  5480. <div class="listing b">Particles : `+G.makeChoices({
  5481. val:function(){return G.getSetting('particles');},
  5482. func:function(val){G.setSetting('particles',val);},
  5483. list:
  5484. [
  5485. {text:'None',tooltip:'No particles will be displayed.'},
  5486. {text:'Low',tooltip:'Particles are displayed in low-performance mode.'},
  5487. {text:'Auto',tooltip:'Particles are displayed in high-performance mode, but switch to low-performance mode in low fps.'},
  5488. {text:'Full',tooltip:'Particles are displayed in high-performance mode.'},
  5489. ],
  5490. })+`</div>
  5491. <div class="listing b">CSS filters : `+G.makeTick({
  5492. val:function(){return G.getSetting('cssfilts');},
  5493. on:'On',off:'Off',
  5494. func:function(val){G.setSetting('cssfilts',val);},
  5495. tooltip:'CSS filters are visual effects such as blur and shadows which may lower performance in some browsers.',
  5496. })+`</div>
  5497. <div class="listing b">Show fps : `+G.makeTick({
  5498. val:function(){return G.getSetting('showFPS');},
  5499. on:'On',off:'Off',
  5500. func:function(val){G.setSetting('showFPS',val);},
  5501. tooltip:'Display the framerate graph in the bottom-left.',
  5502. })+`</div>
  5503. </div>
  5504. `;
  5505. }
  5506. else if (what=='info')
  5507. {
  5508. var text=`
  5509. <div class="headerTitle">Info</div>
  5510. <div style="padding:4px;overflow-y:auto;">
  5511. <div class="sectionTitle">About</div>
  5512. <div class="listing">You are playing <b>`+G.name+`</b>`+(G.version?' v.'+B(G.version):'')+`, by <b>`+(G.author||'Anonymous')+`</b>.
  5513. `+(G.forumPost?'<div><a href="http://forum.dashnet.org/discussion/'+G.forumPost+'" target="_blank">[View this game\'s forum post]</a></div>':'')+`
  5514. </div>
  5515. `+(G.desc?'<div class="listing desc"><div>'+G.getTextValue(G.desc)+'</div></div>':'')+`
  5516. <div class="listing">Game started <b>`+G.selfUpdatingText(function(){return sayTime(parseInt(Date.now())-G.startDate);})+` ago</b>.</div>
  5517. <div class="sectionTitle">Achievements</div>
  5518. <div class="listing b" style="max-width:640px;margin:auto;">
  5519. `+G.selfUpdatingText(function(){
  5520. var str='';
  5521. var owned=0;
  5522. var total=0;
  5523. for (var i in G.achievs)
  5524. {
  5525. var me=G.achievs[i];
  5526. if (me.owned) {str+=me.getQuickDom();owned++;}
  5527. total++;
  5528. }
  5529. str='<div>Owned : '+B(owned)+'/'+B(total)+'</div><div style="padding:8px;">'+str+'</div>';
  5530. return str;
  5531. },5)+`
  5532. </div>
  5533. </div>
  5534. `;
  5535. //TODO : custom stats
  5536. }
  5537. G.mainPopupEl=G.popup(0,{
  5538. text:text+'<div class="footerTitle hoverShine closesThePopup">Close</div>',
  5539. classes:'mainPopup',
  5540. onClose:function(me){G.mainPopup=0;G.mainPopupEl=0;G.darkenL.className='off';},
  5541. });
  5542. }
  5543. else if (G.mainPopupEl) {G.closePopup(G.mainPopupEl);what=0;}
  5544. G.mainPopup=what;
  5545. G.hideTooltip();
  5546. }
  5547.  
  5548.  
  5549. if (loadedData) G.doEffectsForAll('load');
  5550. else G.doEffectsForAll('start');
  5551.  
  5552. //all done!
  5553.  
  5554. for (var i in game){G[i]=game[i];}
  5555. return true;
  5556. }
  5557. }
  5558.  
  5559. G.Reset();
  5560. }
  5561. console.log("initializer for game made");
  5562.  
  5563. G.turnOn=function()
  5564. {
  5565. G.l.classList.add('on');
  5566. G.domReady=true;
  5567. G.playing=true;
  5568. }
  5569. G.turnOff=function()
  5570. {
  5571. G.l.classList.remove('on');
  5572. G.domReady=false;
  5573. G.playing=false;
  5574. }
  5575. console.log("on and off functions made");
  5576.  
  5577. /*=====================================================================================
  5578. BASIC FLOW
  5579. =======================================================================================*/
  5580. G.Logic=function()
  5581. {
  5582. if (G.playing)
  5583. {
  5584. if (G.T%G.fps==1) G.tick();
  5585. for (var i in G.things)
  5586. {
  5587. G.things[i].logic();
  5588. }
  5589. if (G.itemsToRefresh) G.refreshItems();
  5590.  
  5591. //keys
  5592. if (G.keysD[27])//esc
  5593. {
  5594. if (G.popups.length>0) G.closePopup();
  5595. }
  5596. if (G.keys[17] && G.keysD[83])//ctrl-s
  5597. {
  5598. G.save();
  5599. }
  5600.  
  5601. var oldBulk=G.bulk;
  5602. G.bulk=1;
  5603. if (G.keys[16]) G.bulk=50;//shift
  5604. if (G.keys[17]) G.bulk=-G.bulk;//ctrl
  5605. if (G.bulk!=oldBulk && !G.noBulkParticles) G.particleAt(G.l,0,(G.bulk<0?'Selling '+B(-G.bulk):'Buying '+B(G.bulk)));
  5606.  
  5607. if (G.getSetting('autosave') && (G.T+1)%(G.fps*30)==0) G.save();
  5608. }
  5609. G.shiniesLogic();
  5610. G.particlesLogic();
  5611. G.toastLogic();
  5612. }
  5613. console.log("logic made");
  5614. G.Draw=function()
  5615. {
  5616. if (G.playing)
  5617. {
  5618. for (var i in G.things)
  5619. {
  5620. G.things[i].draw();
  5621. }
  5622.  
  5623. if (G.bulkDisplay) G.bulkDisplay.innerHTML=(G.bulk>0?'Buying':'Selling')+' <b>'+B(Math.abs(G.bulk))+'</b>';
  5624. }
  5625. G.shiniesDraw();
  5626. G.popupDraw();
  5627. G.tooltipDraw();
  5628. }
  5629. console.log("drawer made");
  5630. G._Init();
  5631. }
  5632. }
  5633. }
  5634. }});
  5635. }
  5636. </script>
  5637. </body>
  5638. </html>
Add Comment
Please, Sign In to add comment