Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <title>Idle Game Maker</title>
- <!--
- Code and graphics copyright Orteil, 2017
- 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.
- -->
- <link rel="shortcut icon" href="img/favicon.ico" />
- <style>
- /* reset CSS */
- html, body, div, span, applet, object, iframe,
- h1, h2, h3, h4, h5, h6, p, blockquote, pre,
- a, abbr, acronym, address, big, cite, code,
- del, dfn, em, img, ins, kbd, q, s, samp,
- small, strike, strong, sub, sup, tt, var,
- b, u, i, center,
- dl, dt, dd, ol, ul, li,
- fieldset, form, label, legend,
- table, caption, tbody, tfoot, thead, tr, th, td,
- article, aside, canvas, details, embed,
- figure, figcaption, footer, header, hgroup,
- menu, nav, output, ruby, section, summary,
- time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
- -moz-box-sizing:border-box;
- box-sizing:border-box;
- }
- /* HTML5 display-role reset for older browsers */
- article, aside, details, figcaption, figure,
- footer, header, hgroup, menu, nav, section {
- display: block;
- }
- body {
- line-height: 1;
- }
- ol, ul {
- list-style: none;
- }
- blockquote, q {
- quotes: none;
- }
- blockquote:before, blockquote:after,
- q:before, q:after {
- content: '';
- content: none;
- }
- table {
- border-collapse: collapse;
- border-spacing: 0;
- }
- /*=====================================================================================
- BASE
- =======================================================================================*/
- html,body
- {
- width:100%;
- height:100%;
- }
- body
- {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: -moz-none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- color:#ccc;
- text-shadow:1px 1px 0px #000;
- background:#000;
- font-family:Tahoma,Arial,sans-serif;
- font-size:11px;
- }
- b{font-weight:bold;}
- i{font-style:italic;}
- u{text-decoration:underline;}
- small{font-size:80%;}
- .outFrame #game
- {
- position:absolute;
- left:0px;top:26px;right:0px;bottom:102px;
- display:block;
- background:#000;
- }
- .inFrame #game
- {
- position:absolute;
- left:0px;top:0px;right:0px;bottom:0px;
- display:none;
- }
- #player,#main
- {
- position:absolute;
- width:100%;
- height:100%;
- }
- .outFrame #code
- {
- position:absolute;
- left:0px;top:26px;right:0px;bottom:102px;
- display:block;
- background:#000;
- }
- #editor
- {
- position:absolute;
- width:100%;
- height:100%;
- display:none;
- }
- .withCode #editor
- {
- display:block;
- }
- .editor #game
- {
- overflow-x:hidden;
- overflow-y:scroll;
- }
- .editor #codeWrap
- {
- width:100%;
- height:100%;
- padding:8px;
- }
- .editor #code
- {
- width:100%;
- height:100%;
- padding:16px;
- margin:0px;
- outline:none;
- box-sizing:border-box;
- }
- #topBar,#ad
- {
- color:#999;
- font-size:11px;
- background:#222;
- }
- #topBar
- {
- position:absolute;
- left:0px;right:0px;top:0px;height:26px;
- background:url(img/darkNoiseTopBar.jpg) repeat-x bottom,url(img/darkNoise.jpg);
- }
- #topBar>div
- {
- display:inline-block;
- float:left;
- border-right:1px solid #000;
- box-shadow:0px 0px 3px 1px rgba(255,255,255,0.2) inset;
- padding:5px 8px 7px 8px;
- }
- #topBar a
- {color:#ccc;}
- #topBar a:hover
- {color:#fff;}
- #topBar a.blueLink
- {color:#06c;}
- #topBar a.blueLink:hover
- {color:#28f;text-shadow:0px 0px 3px #06c;}
- #topBar>#links
- {
- display:block;
- position:absolute;
- right:0px;
- top:0px;
- z-index:100000000000;
- float:none;
- }
- #ad
- {
- position:absolute;
- left:0px;right:0px;bottom:0px;height:102px;
- 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);
- }
- .outFrame #game
- {
- right:13%;
- bottom:0px;
- }
- .outFrame.withCode #game
- {
- left:30%;
- }
- .outFrame #code
- {
- left:0px;
- width:30%;
- bottom:0px;
- }
- #ad
- {
- left:auto;
- right:0px;
- top:26px;
- bottom:0px;
- width:13%;
- height:auto;
- }
- #fpsCounter
- {
- background:#333;
- color:#fff;
- position:absolute;
- left:0px;
- bottom:0px;
- padding:3px;
- z-index:10000001;
- opacity:0.5;
- pointer-events:none;
- }
- #fpsGraph
- {
- background:#333;
- color:#fff;
- position:absolute;
- left:0px;
- bottom:0px;
- padding:3px;
- width:128px;
- height:64px;
- z-index:10000000;
- opacity:0.5;
- pointer-events:none;
- }
- #wrap
- {
- position:absolute;left:0px;right:0px;top:0px;bottom:0px;
- overflow:hidden;
- }
- </style>
- </head>
- <body>
- <div id="wrap" class="outFrame">
- <div id="topBar"></div>
- <div id="code">
- </div>
- <div id="game">
- </div>
- <div id="ad">
- <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
- <!-- IGM responsive
- <ins class="adsbygoogle"
- style="display:block"
- data-ad-client="ca-pub-8491708950677704"
- data-ad-slot="2107382385"
- data-ad-format="auto"></ins>
- <script>
- (adsbygoogle = window.adsbygoogle || []).push({});
- </script>-->
- </div>
- </div>
- <script>
- console.log("starting script");
- function l(what) {return document.getElementById(what);}
- function choose(arr) {return arr[Math.floor(Math.random()*arr.length)];}
- function randomFloor(x) {if ((x%1)<Math.random()) return Math.floor(x); else return Math.ceil(x);}
- String.prototype.replaceAll=function(search,replacement)
- //{var target=this;return target.replace(new RegExp(search,'g'),replacement);};
- {return this.split(search).join(replacement);};
- function AddEvent(html_element,event_name,event_function)
- {
- if(html_element.attachEvent) html_element.attachEvent("on" + event_name, function() {event_function.call(html_element);});
- else if(html_element.addEventListener) html_element.addEventListener(event_name, event_function, false);
- }
- function LoadScript(url,callback,onerror)
- {
- var script=document.createElement('script');
- script.setAttribute('src',url);
- if (callback) script.onload=callback;
- if (onerror) script.onerror=onerror;
- document.head.appendChild(script);
- return script;
- }
- function ajax(url,callback)
- {
- var ajaxRequest;
- 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;}}}
- if (callback){ajaxRequest.onreadystatechange=function(){if(ajaxRequest.readyState==4){callback(ajaxRequest.responseText);}}}
- ajaxRequest.open('GET',url+'&nocache='+(new Date().getTime()),true);ajaxRequest.send(null);
- }
- var INFRAME=INFRAME||true;
- var EDITOR=EDITOR||false;
- G={};
- console.log("first functions loaded");
- G.Launch=function()
- {
- if (!INFRAME)
- {
- console.log("INFRAME is false, now loading base");
- l('topBar').innerHTML=
- '<div><b>Idle Game Maker</b>™ © <a href="http://orteil.dashnet.org" target="_blank">Orteil</a>, 2017 - <a href="http://dashnet.org" target="_blank">DashNet</a></div>'+
- '<div><a href="http://twitter.com/orteil42" target="_blank">twitter</a></div>'+
- '<div><a href="http://orteil42.tumblr.com" target="_blank">tumblr</a></div>'+
- '<div>Help? Bugs? Ideas? Check out the <a href="http://forum.dashnet.org" target="_blank">forum</a>!</div>'+
- '<div><a href="./help.html" target="_blank">Game-making help</a></div>'+
- '<div>Also : <a href="http://orteil.dashnet.org/cookieclicker/" target="_blank">Cookie Clicker</a></div>'+
- '';
- }
- console.log("defining initialization");
- G.Init=function(){};
- G._Init=function()
- {
- G.T=0;
- G.drawT=0;
- G.fps=30;
- G.l=l('game');
- G.wrapl=l('wrap');
- G.local=true;
- if (window.location.protocol=='http:' || window.location.protocol=='https:'){G.local=false;console.log("you use http protocall");}
- G.isIE=false;
- if (document.documentMode || /Edge/.test(navigator.userAgent)) G.isIE=true;
- if (!G.local && !INFRAME)
- {
- 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"};
- LoadScript("//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js");
- }
- console.log("cookie consent loaded");
- G.w=window.innerWidth;
- G.h=window.innerHeight;
- G.resizing=false;
- if (!G.stabilizeResize) G.stabilizeResize=function(){}
- G._stabilizeResize=function()
- {
- G.resizing=false;
- G.stabilizeResize();
- }
- G.resize=function()
- {
- G.resizing=true;
- }
- window.addEventListener('resize',function(event)
- {
- G.w=window.innerWidth;
- G.h=window.innerHeight;
- G.resize();
- });
- console.log("screen resizing adaption added");
- G.mouseDown=false;//mouse button just got pressed
- G.mouseUp=false;//mouse button just got released
- G.mousePressed=false;//mouse button is currently down
- G.clickL=0;//what element got clicked
- AddEvent(document,'mousedown',function(event){G.mouseDown=true;G.mousePressed=true;G.mouseDragFrom=event.target;G.mouseDragFromX=G.mouseX;G.mouseDragFromY=G.mouseY;});
- AddEvent(document,'mouseup',function(event){G.mouseUp=true;G.mouseDragFrom=0;});
- AddEvent(document,'click',function(event){G.clickL=event.target;});
- G.mouseX=0;
- G.mouseY=0;
- G.mouseMoved=0;
- G.draggedFrames=0;//increment every frame when we're moving the mouse and we're clicking
- G.GetMouseCoords=function(e)
- {
- var posx=0;
- var posy=0;
- if (!e) var e=window.event;
- if (e.pageX||e.pageY)
- {
- posx=e.pageX;
- posy=e.pageY;
- }
- else if (e.clientX || e.clientY)
- {
- posx=e.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;
- posy=e.clientY+document.body.scrollTop+document.documentElement.scrollTop;
- }
- var x=0;
- var y=0;
- G.mouseX=posx-x;
- G.mouseY=posy-y;
- G.mouseMoved=1;
- }
- AddEvent(document,'mousemove',G.GetMouseCoords);
- console.log("mouse tracker loaded");
- G.Scroll=0;
- G.handleScroll=function(e)
- {
- if (!e) e=event;
- G.Scroll=(e.detail<0||e.wheelDelta>0)?1:-1;
- };
- AddEvent(document,'DOMMouseScroll',G.handleScroll);
- AddEvent(document,'mousewheel',G.handleScroll);
- G.keys={};//key is being held down
- G.keysD={};//key was just pressed down
- G.keysU={};//key was just pressed up
- //shift=16, ctrl=17
- AddEvent(window,'keyup',function(e){
- if ((document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') && e.keyCode!=27) return;
- if (e.keyCode==27) {}//esc
- else if (e.keyCode==13) {}//enter
- G.keys[e.keyCode]=0;
- G.keysD[e.keyCode]=0;
- G.keysU[e.keyCode]=1;
- });
- AddEvent(window,'keydown',function(e){
- if (!G.keys[e.keyCode])//prevent repeats
- {
- if (e.ctrlKey && e.keyCode==83) {e.preventDefault();}//ctrl-s
- if ((document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') && e.keyCode!=27) return;
- if (e.keyCode==32) {e.preventDefault();}//space
- G.keys[e.keyCode]=1;
- G.keysD[e.keyCode]=1;
- G.keysU[e.keyCode]=0;
- //console.log('Key pressed : '+e.keyCode);
- }
- });
- console.log("key press tracker is done");
- AddEvent(window,'blur',function(e){
- G.keys={};
- G.keysD={};
- G.keysU={};
- });
- //latency compensator stuff
- G.time=new Date().getTime();
- G.fpsMeasure=new Date().getTime();
- G.accumulatedDelay=0;
- G.catchupLogic=0;
- G.fpsStartTime=0;
- G.frameNumber=0;
- G.getFps=function()
- {
- G.frameNumber++;
- var currentTime=(Date.now()-G.fpsStartTime )/1000;
- var result=Math.ceil((G.frameNumber/currentTime));
- if (currentTime>1)
- {
- G.fpsStartTime=Date.now();
- G.frameNumber=0;
- }
- return result;
- }
- var div=document.createElement('div');
- div.id='fpsCounter';
- G.wrapl.appendChild(div);
- G.fpsCounter=div;
- var div=document.createElement('canvas');
- div.id='fpsGraph';
- div.width=128;
- div.height=64;
- G.wrapl.appendChild(div);
- G.fpsGraph=div;
- G.fpsGraphCtx=G.fpsGraph.getContext('2d');
- var ctx=G.fpsGraphCtx;
- ctx.fillStyle='#000';
- ctx.fillRect(0,0,128,64);
- G.currentFps=0;
- G.previousFps=0;
- console.log("fps counter prepped.");
- G.resize();
- G.Init();
- G.Loop();
- console.log("Game initialized");
- }
- //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
- console.log("defining call back system");
- G.Callbacks=[];
- G.callbackDepth=0;
- G.addCallbacks=function()
- {
- if (G.callbackDepth>0) return false;//prevent nesting callbacks
- G.callbackDepth++;
- var len=G.Callbacks.length;
- for (var i=0;i<len;i++)
- {G.Callbacks[i]();}
- G.Callbacks=[];
- G.callbackDepth--;
- }
- G.pushCallback=function(func)
- {
- G.Callbacks.push(func);
- }
- console.log("defining button system");
- G.buttonsN=0;
- G.button=function(obj)
- {
- //returns a string for a new button; creates a callback that must be applied after the html has been created, with G.addCallbacks()
- //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)
- var id=obj.id||('button-'+G.buttonsN);
- var str='<div '+(obj.style?('style="'+obj.style+'" '):'')+'class="systemButton'+(obj.classes?(' '+obj.classes):'')+'" id="'+id+'">'+(obj.text||'-')+'</div>';
- if (obj.onclick || obj.tooltip)
- {
- G.pushCallback(function(id,obj){return function(){
- if (l(id))
- {
- if (typeof obj.tooltip==='function') G.addTooltip(l(id),{func:obj.tooltip});
- else G.addTooltip(l(id),{text:obj.tooltip});
- if (obj.onclick) l(id).onclick=obj.onclick;
- }
- }}(id,obj));
- }
- G.buttonsN++;
- return str;
- }
- console.log("defining automatically updating text system");
- G.textN=0;
- G.updateTextTimer=function(id,func,freq)
- {
- var el=l('updatabletextspan-'+id);
- if (el)
- {
- var delay=freq*1000;
- if (freq<0) {freq=-freq;delay=100;}//a trick : the first update always occurs 100ms after being declared
- el.innerHTML=func();
- G.addCallbacks();
- setTimeout(function(id,func){return function(){G.updateTextTimer(id,func,freq);}}(id,func),delay);
- }
- }
- G.selfUpdatingText=function(func,freq)
- {
- if (!freq) freq=1;
- //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()
- var id=G.textN;
- var str='<span class="updatabletextspan" id="updatabletextspan-'+id+'">'+func()+'</span>';
- G.pushCallback(function(id,func,freq){return function(){
- G.updateTextTimer(id,func,-freq);
- }}(id,func,freq));
- G.textN++;
- return str;
- }
- console.log("settihn up tool tips");
- G.tooltipdN=0;
- G.tooltipped=function(text,obj,style)
- {
- var id='tooltippedspan-'+G.tooltipdN;
- //var str='<span class="tooltippedspan"'+(style?' style="'+style+'"':'')+' id="'+id+'">'+text+'</span>';
- var div=document.createElement('div');
- //weird trickery here, don't even ask
- div.innerHTML=text;
- if (div.firstChild.nodeType==3) div.innerHTML='<span>'+div.innerHTML+'</span>';
- if (!div.firstChild.id) div.firstChild.id=id;
- else id=div.firstChild.id;
- str=div.innerHTML;
- G.pushCallback(function(id,obj){return function(){
- if (typeof obj==='string') G.addTooltip(l(id),{text:obj});
- else G.addTooltip(l(id),obj);
- }}(id,obj));
- G.tooltipdN++;
- return str;
- }
- /*=====================================================================================
- LOGIC
- =======================================================================================*/
- console.log("building main logic");
- G.Logic=function(){}
- G._Logic=function()
- {
- G.Logic();
- if (G.T%5==0 && G.resizing) {G._stabilizeResize();}
- if (G.mouseUp) G.mousePressed=false;
- G.mouseDown=false;
- G.mouseUp=false;
- if (G.mouseMoved && G.mousePressed) G.draggedFrames++; else if (!G.mousePressed) G.draggedFrames=0;
- G.mouseMoved=0;
- G.Scroll=0;
- G.clickL=0;
- G.keysD={};
- G.keysU={};
- if (document.activeElement.nodeName=='TEXTAREA' || document.activeElement.nodeName=='INPUT') G.keys={};
- G.T++;
- }
- /*=====================================================================================
- DRAW
- =======================================================================================*/
- console.log("defining draw system");
- G.Draw=function(){};
- G._Draw=function()
- {
- G.Draw();
- G.drawT++;
- }
- /*=====================================================================================
- MAIN LOOP
- =======================================================================================*/
- console.log("setting up maim game loop");
- G.Loop=function()
- {
- //update game logic !
- G.catchupLogic=0;
- G._Logic();
- G.catchupLogic=1;
- //latency compensator
- G.accumulatedDelay+=((new Date().getTime()-G.time)-1000/G.fps);
- G.accumulatedDelay=Math.min(G.accumulatedDelay,1000*5);//don't compensate over 5 seconds; if you do, something's probably very wrong
- G.time=new Date().getTime();
- while (G.accumulatedDelay>0)
- {
- G._Logic();
- 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)
- }
- G.catchupLogic=0;
- if (!document.hidden || document.hasFocus() || G.T%5==0) G._Draw();
- //fps counter and graph
- G.previousFps=G.currentFps;
- G.currentFps=G.getFps();
- if (INFRAME && !EDITOR)
- {
- l('fpsCounter').innerHTML=G.currentFps+' fps';
- var ctx=G.fpsGraphCtx;
- ctx.drawImage(G.fpsGraph,-1,0);
- ctx.fillStyle='rgb('+Math.round((1-G.currentFps/G.fps)*128)+',0,0)';
- ctx.fillRect(128-1,0,1,64);
- ctx.strokeStyle='#fff';
- ctx.beginPath();
- ctx.moveTo(128-1,(1-G.previousFps/G.fps)*64);
- ctx.lineTo(128,(1-G.currentFps/G.fps)*64);
- ctx.stroke();
- }
- setTimeout(G.Loop,1000/G.fps);
- }
- }
- console.log("g defined, now launching g");
- G.Launch();
- console.log("launch complete!");
- if (!INFRAME)
- {
- window.onload=function()
- {
- console.log("window loaded");
- if (!G.ready)
- {
- console.log("getting g ready");
- G.ready=true;
- var vars={};
- var parts=window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(m,key,value){vars[key]=value;});
- G.vars=vars;
- console.log("(NOT) loading i frames");
- if (G.vars.e)
- {
- l('code').innerHTML='<iframe id="editor" src="editor.html"></iframe>';
- l('wrap').classList.add('withCode');
- }
- if (G.vars.g)
- {
- l('game').innerHTML='<iframe id="player" src="player.html"></iframe>';
- }
- else
- {
- l('game').innerHTML='<iframe id="main" src="main.html"></iframe>';
- }
- }
- };
- G.loadedEditor=false;
- G.loadedPlayer=false;
- window.addEventListener('message',function(e)
- {
- if (e.data.playerReady && !G.loadedPlayer)
- {
- G.loadedPlayer=true;
- l('player').contentWindow.postMessage({launch:true,loc:window.location.origin,vars:G.vars},'*');
- }
- else if (e.data.editorReady) {}
- else if (e.data.code && !G.loadedEditor && l('editor'))
- {
- G.loadedEditor=true;
- l('editor').contentWindow.postMessage({code:e.data.code},'*');
- }
- else if (e.data.refresh)
- {
- l('game').innerHTML='<iframe id="player" src="player.html"></iframe>';
- setTimeout(function(){l('player').contentWindow.postMessage({launch:true,loc:window.location.origin,vars:G.vars,code:e.data.refresh},'*');},100);
- }
- });
- }
- else
- {
- console.log("inframe is "+INFRAME+", editor is "+EDITOR+", and ready is "+G.ready);
- if (!G.ready)
- {
- if (EDITOR)
- {
- console.log("code editor is on");
- G.ready=true;
- G.timeSinceTyped=0;
- l('game').innerHTML=`
- <div id="codeWrap">
- <textarea id="code"></textarea>
- </div>
- `;
- AddEvent(l('code'),'input',function(){G.timeSinceTyped=Math.ceil(G.fps/2);});
- G.Logic=function(){
- if (G.timeSinceTyped>0) G.timeSinceTyped--;
- if (G.timeSinceTyped==1) parent.postMessage({refresh:l('code').value},'*');
- }
- G._Init();
- parent.postMessage({editorReady:true},'*');
- }
- else
- {
- parent.postMessage({playerReady:true},'*');
- }
- }
- window.addEventListener('click',function(e)
- {if(!G.ready){
- e.data={};
- e.data.code=`
- `;
- console.log("click recieved");
- if (EDITOR)
- {
- console.log("code editor is on 2");
- if (e.data.code)
- {
- l('code').value=e.data.code;
- l('game').style.display='block';
- }
- }
- else
- {
- console.log("prepping for a game...");
- if (!G.ready && true)
- {
- G.ready=true;
- if (true)
- {
- if (e.data.code) TOPARSE=e.data.code;
- G.urlVars=e.data.vars;
- console.log("final step, loading game engine");
- /*=====================================================================================
- HELPER FUNCTIONS
- =======================================================================================*/
- function triggerAnim(element,anim)
- {
- if (!element) return;
- element.classList.remove(anim);
- void element.offsetWidth;
- element.classList.add(anim);
- }
- function isNumber(n)
- {return (!isNaN(parseFloat(n)) && isFinite(n));}
- function parseNum(n)
- {
- if (n===true) return 1;
- else if (n===false) return 0;
- else if (isNaN(n)) return 0;
- else return n;
- }
- var luck=function(x)
- {
- if (Math.random()<x/100) return true; else return false;
- }
- var rand=function(x,y)
- {
- if (!y) y=0;
- if (y<x) {var tmp=x;x=y;y=tmp;}
- return Math.round(Math.random()*(y-x)+x);
- }
- var frand=function(x,y)
- {
- if (!y) y=0;
- if (y<x) {var tmp=x;x=y;y=tmp;}
- return (Math.random()*(y-x)+x);
- }
- console.log("math and animation helper functions added");
- //String2 is a pseudo-string type with some added functions that are as ambiguous in purpose as they are buggy
- var String2=function(str)
- {
- this.val=str;
- }
- String2.prototype.toString=function(){return this.val;}
- /*
- String2.gulp(str) : if the string begins with str, remove str from the string and return what we gulped; else return false
- If str is unspecified, gulp a whole word until the next space and return it
- If a quote " is encountered, keep gulping until the next quote
- Example :
- var str=STR2('set value3 to 100');
- if (str.gulp('set'))
- {
- var val=str.gulpUntil('to');
- var amount=str.gulpUntil();
- console.log(val,amount);
- }
- */
- String2.prototype.gulp=function(str,isSymbol)
- {
- if (str)
- {
- if (!isSymbol) str=str+=' ';
- if (this.val.indexOf(str)==0)
- {
- this.val=this.val.substring(str.length);
- if (isSymbol && this.val.charAt(0)==' ') this.val=this.val.substring(1);//remove first space
- return str;
- }
- else return false;
- }
- else
- {
- if ((this.val.split('"').length-1)>=2 && (this.val.indexOf(' ')>this.val.indexOf('"')))
- {
- var str=this.val.substring(0,this.val.indexOf('"',this.val.indexOf('"')+1)+1);
- this.val=this.val.substring(str.length+1);
- return str;
- }
- else if (this.val.indexOf(' ')>0)
- {
- var str=this.val.substring(0,this.val.indexOf(' '));
- this.val=this.val.substring(str.length+1);
- return str;
- }
- else
- {
- var str=this.val;this.val='';return str;
- }
- }
- }
- String2.prototype.gulpSymbol=function(str){return this.gulp(str,true);}//like gulp, but ignores spaces
- String2.prototype.gulpUntil=function(str,isSymbol,toEnd)
- {
- //gulp all words until str (str is not included in the result, and removed from the String2)
- //if str is unspecified (or toEnd is true and str wasn't found), just return the rest of the string
- var bumpStr=str;
- var paddedBumpStr=bumpStr;
- if (!isSymbol) paddedBumpStr=' '+paddedBumpStr;
- if (!str || (toEnd && this.val.indexOf(paddedBumpStr)==-1))
- {
- var str=this.val;
- this.val='';
- return str;
- }
- else if (this.val.indexOf(paddedBumpStr)>=0)
- {
- var str=this.val.substring(0,this.val.indexOf(paddedBumpStr));
- if (isSymbol) this.val=this.val.substring(str.length+bumpStr.length);
- else this.val=this.val.substring(str.length+1+bumpStr.length);
- if (this.val.charAt(0)==' ') this.val=this.val.substring(1);//remove first space
- if (this.val.charAt(this.val.length-1)==' ') this.val=this.val.slice(0,-1);//remove last space
- return str;
- }
- else return false;
- }
- String2.prototype.gulpUntilSymbol=function(str,toEnd){return this.gulpUntil(str,true,toEnd);}//like gulpUntil, but ignores spaces
- var STR2=function(str)//shortcut
- {return new String2(str);}
- console.log("string prototypes added");
- //the old Beautify function from Cookie Clicker, shortened to B(value)
- //initially adapted from http://cookieclicker.wikia.com/wiki/Frozen_Cookies_%28JavaScript_Add-on%29
- function formatEveryThirdPower(notations)
- {
- return function (value)
- {
- var base = 0,
- notationValue = '';
- if (!isFinite(value)) return 'Inf.';
- if (value >= 1000)
- {
- value /= 1000;
- while(Math.round(value) >= 1000)
- {
- value /= 1000;
- base++;
- }
- if (base >= notations.length) {return 'Inf.';} else {notationValue = notations[base];}
- }
- return ( Math.round(value * 10) / 10 ) + notationValue;
- };
- }
- function rawFormatter(value) {return Math.round(value * 1000) / 1000;}
- var formatLong=[' thousand',' million',' billion',' trillion',' quadrillion',' quintillion',' sextillion',' septillion',' octillion',' nonillion'];
- var prefixes=['','un','duo','tre','quattuor','quin','sex','septen','octo','novem'];
- var suffixes=['decillion','vigintillion','trigintillion','quadragintillion','quinquagintillion','sexagintillion','septuagintillion','octogintillion','nonagintillion'];
- for (var i in suffixes)
- {
- for (var ii in prefixes)
- {
- formatLong.push(' '+prefixes[ii]+suffixes[i]);
- }
- }
- var formatShort=['k','M','B','T','Qa','Qi','Sx','Sp','Oc','No'];
- var prefixes=['','Un','Do','Tr','Qa','Qi','Sx','Sp','Oc','No'];
- var suffixes=['D','V','T','Qa','Qi','Sx','Sp','O','N'];
- for (var i in suffixes)
- {
- for (var ii in prefixes)
- {
- formatShort.push(' '+prefixes[ii]+suffixes[i]);
- }
- }
- formatShort[10]='Dc';
- var numberFormatters =
- [
- formatEveryThirdPower(formatShort),
- formatEveryThirdPower(formatLong),
- rawFormatter
- ];
- var numberFormatter=numberFormatters[2];
- console.log("beautify prepped...");
- function Beautify(value,floats)
- {
- var negative=(value<0);
- var decimal='';
- var fixed=value.toFixed(floats);
- if (Math.abs(value)<1000 && floats>0 && Math.floor(fixed)!=fixed) decimal='.'+(fixed.toString()).split('.')[1];
- value=Math.floor(Math.abs(value));
- if (floats>0 && fixed==value+1) value++;
- var formatter=numberFormatter;
- var output=formatter(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g,',');
- if (output=='0') negative=false;
- return negative?'-'+output:output+decimal;
- }
- var B=Beautify;
- console.log("beautify made");
- function BeautifyTime(value)
- {
- //value should be in seconds
- value=Math.max(Math.ceil(value,0));
- var years=Math.floor(value/31536000);
- value-=years*31536000;
- var days=Math.floor(value/86400);
- value-=days*86400;
- var hours=Math.floor(value/3600)%24;
- value-=hours*3600;
- var minutes=Math.floor(value/60)%60;
- value-=minutes*60;
- var seconds=Math.floor(value)%60;
- var str='';
- if (years) str+=B(years)+'Y';
- if (days || str!='') str+=B(days)+'d';
- if (hours || str!='') str+=hours+'h';
- if (minutes || str!='') str+=minutes+'m';
- if (seconds || str!='') str+=seconds+'s';
- if (str=='') str+='0s';
- return str;
- }
- var BT=BeautifyTime;
- var sayTime=function(time,detail)
- {
- //time is a value where one second is equal to 1000.
- //detail skips days when >1, hours when >2, minutes when >3 and seconds when >4.
- //if detail is -1, output something like "3 hours, 9 minutes, 48 seconds"
- if (time<=0) return '';
- var str='';
- var detail=detail||0;
- time=Math.floor(time);
- var second=1000;
- if (detail==-1)
- {
- var days=0;
- var hours=0;
- var minutes=0;
- var seconds=0;
- if (time>=second*60*60*24) days=(Math.floor(time/(second*60*60*24)));
- if (time>=second*60*60) hours=(Math.floor(time/(second*60*60)));
- if (time>=second*60) minutes=(Math.floor(time/(second*60)));
- if (time>=second) seconds=(Math.floor(time/(second)));
- hours-=days*24;
- minutes-=hours*60+days*24*60;
- seconds-=minutes*60+hours*60*60+days*24*60*60;
- if (days>10) {hours=0;}
- if (days) {minutes=0;seconds=0;}
- if (hours) {seconds=0;}
- var bits=[];
- if (days>0) bits.push(B(days)+' day'+(days==1?'':'s'));
- if (hours>0) bits.push(B(hours)+' hour'+(hours==1?'':'s'));
- if (minutes>0) bits.push(B(minutes)+' minute'+(minutes==1?'':'s'));
- if (seconds>0) bits.push(B(seconds)+' second'+(seconds==1?'':'s'));
- if (bits.length==0) str='less than 1 second';
- else str=bits.join(', ');
- }
- else
- {
- if (time>=second*60*60*24*2 && detail<2) str=B(Math.floor(time/(second*60*60*24)))+' days';
- else if (time>=second*60*60*24 && detail<2) str='1 day';
- else if (time>=second*60*60*2 && detail<3) str=B(Math.floor(time/(second*60*60)))+' hours';
- else if (time>=second*60*60 && detail<3) str='1 hour';
- else if (time>=second*60*2 && detail<4) str=B(Math.floor(time/(second*60)))+' minutes';
- else if (time>=second*60 && detail<4) str='1 minute';
- else if (time>=second*2 && detail<5) str=B(Math.floor(time/(second)))+' seconds';
- else if (time>=second && detail<5) str='1 second';
- else str='less than 1 second';
- }
- return str;
- }
- console.log("time beautifier done");
- function cap(str)
- {return str.charAt(0).toUpperCase()+str.slice(1);}
- console.log("building save function");
- //file save function from https://github.com/eligrey/FileSaver.js
- 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})}
- /*=====================================================================================
- ENGINE
- =======================================================================================*/
- var TOPARSE=TOPARSE||false;
- console.log("helper functions made!");
- G.Init=function()
- {
- console.log("stabilizijg...");
- G.stabilizeResize=function()
- {
- }
- console.log("intiializimg the game itself");
- /*=====================================================================================
- SETTINGS
- =======================================================================================*/
- G.settings={
- 'vol':{val:100},
- 'autosave':{val:1},
- 'numdisp':{val:0,onchange:function(val){
- val=parseInt(val);
- if (val>=0 && val<=2) numberFormatter=numberFormatters[val];
- }},
- 'cssfilts':{val:1,onchange:function(val){
- if (val)
- {
- G.l.classList.remove('filtersOff');
- G.l.classList.add('filtersOn');
- }
- else
- {
- G.l.classList.remove('filtersOn');
- G.l.classList.add('filtersOff');
- }
- }},
- 'particles':{val:2},
- 'showFPS':{val:G.local?1:0,onchange:function(val){
- if (val)
- {
- G.fpsGraph.style.display='block';
- G.fpsCounter.style.display='block';
- }
- else
- {
- G.fpsGraph.style.display='none';
- G.fpsCounter.style.display='none';
- }
- }},
- };
- console.log("settijg preset up");
- for (var i in G.settings){G.settings[i].key=i;}
- G.setSetting=function(what,val)
- {
- G.settings[what].val=val;
- if (G.settings[what].onchange) G.settings[what].onchange(G.settings[what].val);
- }
- G.getSetting=function(what)
- {
- return G.settings[what].val;
- }
- G.loadSettings=function()
- {
- for (var i in G.settings)
- {
- if (G.settings[i].onchange) G.settings[i].onchange(G.settings[i].val);
- }
- }
- G.makeChoices=function(o)
- {
- var str='';
- var buttonIds=[];
- for (var i in o.list)
- {
- buttonIds.push('button-'+(G.buttonsN+parseInt(i)));
- }
- for (var i in o.list)
- {
- var id=parseInt(i);
- str+=G.button({
- text:o.list[i].text,
- classes:'tickbox '+(o.val()==id?'on':'off'),
- tooltip:o.list[i].tooltip,
- onclick:function(e){
- for (var i in buttonIds)
- {
- l(buttonIds[i]).classList.remove('on');
- l(buttonIds[i]).classList.add('off');
- if (e.target.id==buttonIds[i])
- {
- var id=parseInt(i);
- o.func(id);
- }
- }
- e.target.classList.remove('off');
- e.target.classList.add('on');
- triggerAnim(e.target,'glow');
- },
- });
- }
- return str;
- }
- console.log("setting menu functions ready for later");
- G.makeTick=function(o)
- {
- if (!o.off) o.off=o.on;
- return G.button({
- text:(o.val()?o.on:o.off),
- classes:'tickbox '+(o.val()?'on':'off'),
- tooltip:o.tooltip,
- onclick:function(e){
- if (o.val()) o.func(0); else o.func(1);
- if (o.val())
- {
- e.target.classList.remove('off');
- e.target.classList.add('on');
- e.target.innerHTML=o.on;
- }
- else
- {
- e.target.classList.remove('on');
- e.target.classList.add('off');
- e.target.innerHTML=o.off;
- }
- triggerAnim(e.target,'glow');
- },
- });
- }
- console.log("game 'tick' function made");
- /*=====================================================================================
- SAVE & LOAD
- =======================================================================================*/
- G.saveTo=0;//we save the game to the key "IGM-"+game's url
- G.save=function(returnOnly)
- {
- if (!G.saveTo) return false;
- G.doEffectsForAll('save');
- G.doEffectsForAll('undo grants');
- var str='';
- str+='BEGIN|';
- var list=[];
- list.push('1');//engine version
- list.push(G.startDate);
- list.push(parseInt(Date.now()));//time last played
- str+=list.join(';');
- str+='|SET|';
- var list=[];
- for (var i in G.settings)
- {
- var me=G.settings[i];
- list.push(me.key+','+parseInt(me.val));
- }
- str+=list.join(';');
- str+='|RES|';
- var list=[];
- for (var i in G.res)
- {
- var me=G.res[i];
- list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.amount+','+me.maxAmount+','+me.earned);
- }
- str+=list.join(';');
- str+='|BTN|';
- var list=[];
- for (var i in G.buttons)
- {
- var me=G.buttons[i];
- list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.clicks);
- }
- str+=list.join(';');
- str+='|BLD|';
- var list=[];
- for (var i in G.buildings)
- {
- var me=G.buildings[i];
- list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.amount+','+me.maxAmount);
- }
- str+=list.join(';');
- str+='|UPG|';
- var list=[];
- for (var i in G.upgrades)
- {
- var me=G.upgrades[i];
- list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.owned);
- }
- str+=list.join(';');
- str+='|ACH|';
- var list=[];
- for (var i in G.achievs)
- {
- var me=G.achievs[i];
- list.push(me.key+','+((me.show<<1)|(me.lit))+','+me.owned);
- }
- str+=list.join(';');
- str+='|ITM|';
- //group up items by base key
- var itemsByBase=[];
- for (var i in G.items)
- {
- if (!itemsByBase[G.items[i].base.key]) itemsByBase[G.items[i].base.key]=[];
- itemsByBase[G.items[i].base.key].push(G.items[i]);
- }
- var list=[];
- for (var i in itemsByBase)
- {
- var list2=[];
- for (var ii in itemsByBase[i])
- {
- var me=itemsByBase[i][ii];
- list2.push(((me.show<<1)|(me.lit)));
- }
- list.push(i+'/'+list2.join('/'));
- }
- str+=list.join(';');
- str+='|SHN|';
- var list=[];
- for (var i in G.shinies)
- {
- var me=G.shinies[i];
- list.push(me.key+','+me.clicks);
- }
- str+=list.join(';');
- str+='|END';
- G.doEffectsForAll('do grants');
- str=window.btoa(str);
- if (returnOnly) return str;
- localStorage[G.saveTo]=str;
- if (localStorage[G.saveTo]!=str) return false;
- G.toast({text:'Game saved',classes:'center',dur:3});
- return true;
- }
- console.log("save to function made");
- G.saveData=0;
- G.applyLoad=function(data)
- {
- if (!G.saveData) return false;
- var str=data||G.saveData;
- try{str=window.atob(str);}catch(err){return false;}
- G.saveData=0;
- str=str.split('|');
- if (str[0]!='BEGIN' || str[str.length-1]!='END') return false;
- var blocks=[];
- var blockNames=['BEGIN','SET','RES','BTN','BLD','UPG','ACH','ITM'];
- for (var i in str)
- {
- if (i>0)
- {
- if (blockNames.indexOf(str[i])==-1 && blockNames.indexOf(str[i-1])!=-1) blocks[str[i-1]]=str[i].split(';');
- }
- }
- for (var block in blocks)
- {
- if (block=='ITM')
- {
- for (var subblock in blocks[block])
- {
- var things=blocks[block][subblock].split('/');
- var key=things[0];
- if (G.thingsByName[key])
- {
- var base=G.thingsByName[key];
- things.shift();
- for (var thing in things)
- {
- var bits=things[thing].split(',');
- var i=0;
- var me={};
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- G.gainItem(base,me);
- }
- }
- }
- }
- else if (block=='BEGIN')
- {
- var i=0;
- var bits=blocks[block];
- G.saveUsedEngineVersion=parseInt(bits[i++]);
- G.startDate=parseInt(bits[i++]);
- G.lastDate=parseInt(bits[i++]);
- }
- else
- {
- for (var thing in blocks[block])
- {
- var bits=blocks[block][thing].split(',');
- var i=0;
- var key=bits[i++];
- if (block=='SET' && G.settings[key])
- {
- var me=G.settings[key];
- G.settings[key].val=parseInt(bits[i++]);
- }
- else if (G.thingsByName[key])
- {
- var me=G.thingsByName[key];
- if (block=='RES')
- {
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- me.amount=parseFloat(bits[i++]);
- me.maxAmount=parseFloat(bits[i++]);
- me.earned=parseFloat(bits[i++]);
- }
- else if (block=='BTN')
- {
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- me.clicks=parseInt(bits[i++]);
- }
- else if (block=='BLD')
- {
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- me.amount=parseFloat(bits[i++]);
- me.maxAmount=parseFloat(bits[i++]);
- }
- else if (block=='UPG')
- {
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- me.owned=parseInt(bits[i++]);
- }
- else if (block=='ACH')
- {
- var vis=parseInt(bits[i++]);me.show=vis>>1;me.lit=vis&1;
- me.owned=parseInt(bits[i++]);
- }
- else if (block=='SHN')
- {
- me.clicks=parseInt(bits[i++]);
- }
- }
- }
- }
- }
- G.toast({text:'Game loaded',classes:'center',dur:3});
- return true;
- }
- console.log("1st save loading function made");
- G.load=function(data)
- {
- //reset the game and get the save data
- //(the save data is parsed inside G.parse using G.applyLoad)
- //the data parameter lets us load arbitrary save data; if none is specified, use localStorage instead
- G.turnOff();
- G.saveData=0;
- if (!data && G.saveTo) var data=localStorage[G.saveTo];
- if (!data) data=0;
- G.saveData=data;
- try {
- G.Reset(-1);
- var done=G.parse(G.data);
- }
- catch(e)
- {
- G.context='(javascript)';
- console.log(e);
- G.parseError(e.message);
- return false;
- }
- if (done && !G.parsedOnce)
- {
- //TODO : remove stylesheets first
- //load stylesheets
- /*
- G.stylesheetUrls=G.stylesheets.slice(0);
- G.stylesheets=[];
- G.stylesheetsToLoad=0;
- var onCompletion=function()
- {
- G.stylesheetsToLoad--;
- if (G.stylesheetsToLoad<=0)
- {
- G.turnOn();
- }
- }
- var onBadCompletion=function()
- {
- G.stylesheetsToLoad--;
- if (G.stylesheetsToLoad<=0)
- {
- //TODO : display a warning message; perhaps a confirm prompt
- setTimeout(G.turnOn,1000);
- }
- }
- if (G.stylesheetUrls.length==0) G.stylesheetUrls.push('stuff/basic.css');
- for (var i in G.stylesheetUrls)
- {
- var me={url:G.stylesheetUrls[i],loaded:false};
- G.stylesheets.push(me);
- G.stylesheetsToLoad++;
- }
- for (var i in G.stylesheets)
- {
- var me=G.stylesheets[i];
- if (me.url.endsWith('.css'))
- {
- var link=document.createElement('link');
- link.type='text/css';
- link.rel='stylesheet';
- link.href=me.url;
- link.onload=function(me){return function(){me.loaded=true;onCompletion();}}(me);
- link.onerror=function(me){return function(){console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');onBadCompletion();}}(me);
- document.head.appendChild(link);
- }
- else if (!G.local)
- {
- ajax('server.php?q=fetch|'+me.url,function(me){return function(out){
- if (out=='[NONE]')
- {
- console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');
- onBadCompletion();
- return false;
- }
- var css=document.createElement('style');
- css.type='text/css';
- css.rel='stylesheet';
- css.innerHTML=out;
- document.head.appendChild(css);
- me.loaded=true;
- onCompletion();
- }}(me));
- }
- else
- {
- console.log('WARNING : Failed to load the stylesheet at '+me.url+'.');
- onBadCompletion();
- }
- }
- if (G.css!='')
- {
- var css=document.createElement('style');
- css.type='text/css';
- css.innerHTML=G.css;
- document.getElementsByTagName('head')[0].appendChild(css);
- }
- */
- if (G.bloomFilter>0)
- {
- //disabled (problems in Firefox, slowdowns on complex games, all-around just kinda gaudy)
- /*var bloom=document.createElementNS('http://www.w3.org/2000/svg','svg');
- bloom.innerHTML=`
- <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
- <defs>
- <filter id="bloom" x="0" y="0" width="100%" height="100%">
- <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
- <feColorMatrix in="blur" type="matrix" values="
- 1 0 0 0 0
- 0 1 0 0 0
- 0 0 1 0 0
- 0 0 0 `+G.bloomFilter+` 0
- " result="blur2" />
- <feComposite result="mix" operator="atop" in2="SourceGraphic" in="blur2" />
- <feBlend in2="blur2" in="mix" result="out" mode="overlay" />
- </filter>
- </defs>
- </svg>
- `;
- document.head.appendChild(bloom);*/
- }
- G.parsedOnce=true;
- }
- else if (done)
- {
- setTimeout(G.turnOn,10);
- }
- G.loadSettings();
- }
- console.log("2nd save loading function made");
- G.clear=function(data)
- {
- //completely get rid of the save data
- if (G.saveTo) localStorage.removeItem(G.saveTo);
- G.load();
- G.loadSettings();
- }
- G.getSaveData=function(){return G.save(true);}
- G.loadFromData=function(data){return G.load(data);}
- G.fileSave=function()
- {
- var filename='IGM-'+(G.name.replace(/[^a-zA-Z0-9]+/g,'')||'game');
- var text=G.getSaveData();
- var blob=new Blob([text],{type:'text/plain;charset=utf-8'});
- saveAs(blob,filename+'.txt');
- G.toast({text:'File saved to '+filename+'.txt',classes:'center',dur:3});
- }
- G.fileLoad=function(e)
- {
- if (e.target.files.length==0) return false;
- var file=e.target.files[0];
- var reader=new FileReader();
- reader.onload=function(e)
- {
- G.loadFromData(e.target.result);
- }
- reader.readAsText(file);
- }
- console.log("other save functions made");
- /*=====================================================================================
- GET GAME SOURCE
- =======================================================================================*/
- G.parsedOnce=false;//we've parsed the data at least once; this helps prevent declaring stylesheets more than once etc
- G.playing=false;
- G.dataLoaded=function(data)
- {
- console.log(TOPARSE);
- //if (data=='[NONE]') {G.noData();return false;}
- if (true) G.data=TOPARSE;
- else if (data) G.data=data;
- else {G.noData();return false;}
- console.log('Got '+B(G.data.length)+' characters of data.');
- parent.postMessage({code:G.data},'*');
- G.load();
- }
- G.noData=function()
- {
- var str=G.url||'';
- str=str.replaceAll('&','&');
- str=str.replaceAll('<','<');
- str=str.replaceAll('>','>');
- str=str.replaceAll('"','"');
- str=str.replaceAll('\'',''');
- G.l.innerHTML=`
- <div id="errorWrap">
- <div id="noGameData">
- <b>Woops!</b><br>
- The specified game data<br>
- <small>(`+str+`)</small><br>
- could not be found.<br>
- <a href="./index.html" target="_top">Go back to homepage</a>
- </div>
- </div>
- `;
- G.l.classList.add('on');
- }
- console.log("basic data loaders set up");
- if (true/*G.urlVars && G.urlVars.g*/)
- {
- //load data file
- G.url="here";//G.urlVars.g;
- //if (G.url.indexOf('/')==0) G.url='./games'+G.url+'.txt';
- //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;
- console.log('Fetching game at '+G.url+'...');
- G.saveTo=G.url;
- if (TOPARSE) setTimeout(G.dataLoaded,100);
- else if (G.local) LoadScript(G.url,G.dataLoaded,function(){G.noData();});
- else ajax('server.php?q=fetch|'+G.url,G.dataLoaded);
- }
- else G.noData();
- console.log("data fetched (or no?)");
- /*=====================================================================================
- RESET
- =======================================================================================*/
- G.Reset=function()
- {
- //G.Reset is called on save load.
- //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.
- G.T=0;
- G.domReady=false;
- G.playing=false;
- G.l.innerHTML=`
- <div id="content"></div>
- <div id="meta-buttons">
- <div class="meta-button" id="meta-button-settings"></div>
- <div class="meta-button" id="meta-button-info"></div>
- </div>
- <div id="shinies" class="shadowed"></div>
- <div id="particles" class="shadowed"></div>
- <div id="toasts" class="shadowed"></div>
- <div id="popups"><div id="darken" class="off"></div></div>
- <div id="tooltip">
- <div id="tooltipPU" class="tooltipPoint"></div>
- <div id="tooltipPD" class="tooltipPoint"></div>
- <div id="tooltipPL" class="tooltipPoint"></div>
- <div id="tooltipPR" class="tooltipPoint"></div>
- <div id="tooltipContent"></div>
- </div>
- `;
- /*=====================================================================================
- LOG
- =======================================================================================*/
- G.initLog=function()
- {
- G.logL=l('log')||0;
- G.logLin=0;
- G.logs=[];
- G.maxLogs=50;
- if (G.logL)
- {
- G.logLin=l('logInner');
- }
- }
- G.log=function(text,classes)
- {
- if (!G.logL) return false;
- var scrolled=!(Math.abs(G.logL.scrollTop-(G.logL.scrollHeight-G.logL.offsetHeight))<3);
- var me={};
- var str='<div class="messageContent"><div>'+text+'</div></div>';
- me.text=str;
- var div=document.createElement('div');
- div.innerHTML=str;
- div.className='message popInVertical'+(classes?(' '+classes):'');
- me.l=div;
- G.logLin.appendChild(div);
- G.logs.push(me);
- if (G.logs.length>G.maxLogs)
- {
- var el=G.logLin.firstChild;
- for (var i in G.logs)
- {
- if (G.logs[i].l==el)
- {
- G.logs.splice(i,1);
- break;
- }
- }
- G.logLin.removeChild(el);
- }
- if (!scrolled) G.logL.scrollTop=G.logL.scrollHeight-G.logL.offsetHeight;
- G.addCallbacks();
- }
- G.clearLog=function()
- {
- G.logs=[];
- G.logLin.innerHTML='';
- }
- G.initLog();
- /*=====================================================================================
- PARTICLES
- =======================================================================================*/
- G.particlesL=l('particles');
- G.particlesN=50;
- G.particlesI=0;
- G.particles=[];
- G.particlesReset=function()
- {
- var str='';
- for (var i=0;i<G.particlesN;i++)
- {
- str+='<div id="particle-'+i+'" class="particle"><div id="particleText-'+i+'" class="particleText"></div></div>';
- }
- G.particlesL.innerHTML=str;
- for (var i=0;i<G.particlesN;i++)
- {
- 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)};
- }
- }
- G.particlesReset();
- G.particleAt=function(el,icon,text)
- {
- if (G.getSetting('particles')==0) return false;
- var me=G.particles[G.particlesI];
- /*var box=el.getBoundingClientRect();
- var x=box.left;
- var y=box.top;
- var w=box.right-x;
- var h=box.bottom-y;
- me.x=x+w*0.2+Math.random()*w*0.6-24;
- me.y=y+h*0.2+Math.random()*h*0.6-24-48;*/
- me.x=G.mouseX-24+(Math.random()*20-10);
- me.y=G.mouseY-24-48+(Math.random()*20-10);
- me.xd=Math.random()*8-4;
- me.yd=-Math.random()*8-4;
- me.r=Math.random()*90-45;
- me.t=0;
- if (text) me.lt.innerHTML=text;
- me.baseCSS='display:block;'+G.resolveIcon(icon,true);
- if (G.getSetting('particles')<3 && (G.currentFps<20 || G.getSetting('particles')==1))
- {
- //if low fps, trigger simple CSS animation instead
- me.low=true;
- me.l.style.cssText=me.baseCSS+'opacity:0;left:'+Math.floor(me.x)+'px;top:'+Math.floor(me.y)+'px;';
- triggerAnim(me.l,'particlePop');
- }
- else {me.low=false;me.l.classList.remove('particlePop');}
- G.particlesI++;
- if (G.particlesI>=G.particlesN) G.particlesI=0;
- }
- G.particlesLogic=function()
- {
- for (var i=0;i<G.particlesN;i++)
- {
- var me=G.particles[i];
- if (!me.low && me.t>=0)
- {
- var r=Math.pow(me.t/20,0.15);
- var r2=Math.pow(me.t/20,5);
- 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%;';
- me.x+=me.xd;
- me.y+=me.yd;
- me.xd*=0.95;
- me.yd+=1;
- me.yd=Math.min(me.yd,6);
- me.t++;
- if (me.t>20)
- {
- me.t=-1;
- me.l.style.cssText='display:none;';
- me.lt.innerHTML='';
- }
- }
- }
- }
- /*=====================================================================================
- POPUPS
- =======================================================================================*/
- G.popupsL=l('popups');
- G.popups=[];
- G.popup=function(el,o)
- {
- //TODO : handle el
- var me={};
- for (var i in o){me[i]=o[i];}
- me.l=document.createElement('div');
- var classes='popup';
- if (me.classes) classes+=' '+me.classes;
- me.l.className=classes;
- me.l.innerHTML=(me.text||'');
- if (me.init) me.init(me);
- var buttonl=document.createElement('div');
- buttonl.innerHTML='x';
- buttonl.className='closeButton closesThePopup';
- me.l.insertBefore(buttonl,me.l.firstChild);
- var closers=me.l.getElementsByClassName('closesThePopup');
- for (var i in closers)
- {AddEvent(closers[i],'click',function(me){return function(){G.closePopup(me);}}(me));}
- G.popupsL.appendChild(me.l);
- G.popups.push(me);
- G.addCallbacks();
- return me;
- }
- G.closePopup=function(me)
- {
- if (!me) var me=G.popups[G.popups.length-1];
- if (me.onClose) me.onClose(me);
- G.popups.splice(G.popups.indexOf(me),1);
- me.l.parentNode.removeChild(me.l);
- }
- G.popupDraw=function()
- {
- var topb=0;
- var bottomb=G.h;
- var leftb=0;
- var rightb=G.w;
- for (var i in G.popups)
- {
- var me=G.popups[i];
- if (me.func) me.func(me);
- me.l.style.left=Math.floor((rightb-leftb)/2-me.l.clientWidth/2)+'px';
- me.l.style.top=Math.floor((bottomb-topb)/2-me.l.clientHeight/2)+'px';
- me.l.style.opacity=1;
- }
- }
- /*=====================================================================================
- TOASTS
- =======================================================================================*/
- G.toastsL=l('toasts');
- G.toasts=[];
- G.toast=function(o)
- {
- var me={};
- for (var i in o){me[i]=o[i];}
- me.l=document.createElement('div');
- var classes='toast popInVertical';
- if (me.classes) classes+=' '+me.classes;
- me.l.className=classes;
- me.l.innerHTML=(me.text||'');
- me.t=0;
- me.toDie=0;
- if (me.init) me.init(me);
- var buttonl=document.createElement('div');
- buttonl.innerHTML='x';
- buttonl.className='closeButton closesThePopup';
- me.l.insertBefore(buttonl,me.l.firstChild);
- var closers=me.l.getElementsByClassName('closesThePopup');
- for (var i in closers)
- {AddEvent(closers[i],'click',function(me){return function(){G.closeToast(me);}}(me));}
- G.toastsL.appendChild(me.l);
- G.toasts.push(me);
- G.addCallbacks();
- return me;
- }
- G.closeToast=function(me)
- {
- if (!me) var me=G.toasts[G.toasts.length-1];
- if (me.toDie) return false;
- me.toDie=1;
- me.l.classList.remove('popInVertical');
- me.l.classList.add('popOutVertical');
- if (me.onClose) me.onClose(me);
- }
- G.killToast=function(me)
- {
- if (!me) var me=G.toasts[G.toasts.length-1];
- G.toasts.splice(G.toasts.indexOf(me),1);
- me.l.parentNode.removeChild(me.l);
- }
- G.toastLogic=function()
- {
- for (var i in G.toasts)
- {
- var me=G.toasts[i];
- if (me.toDie)
- {
- me.toDie++;
- if (me.toDie>=G.fps*0.3) G.killToast(me);
- }
- else
- {
- me.t++;
- if (me.dur>0 && me.t>=me.dur*G.fps) G.closeToast(me);
- }
- }
- }
- /*=====================================================================================
- TOOLTIP
- =======================================================================================*/
- G.tooltipL=l('tooltip');
- G.tooltipContentL=l('tooltipContent');
- G.tooltipPU=l('tooltipPU');
- G.tooltipPD=l('tooltipPD');
- G.tooltipPL=l('tooltipPL');
- G.tooltipPR=l('tooltipPR');
- G.pseudoHover=new Event('pseudoHover');
- G.tooltipReset=function()
- {
- G.tooltip={
- parent:0,origin:0,classes:'',text:'',on:false,settled:false,t:0,
- };
- }
- G.tooltipReset();
- G.addTooltip=function(el,o)
- {
- AddEvent(el,'mouseover',function(el,o){return function(){
- var settled=(el==G.tooltip.parent);
- G.showTooltip(el,o);
- if (settled) G.tooltip.settled=true;
- }}(el,o));
- AddEvent(el,'pseudoHover',function(el,o){return function(){
- G.showTooltip(el,o);
- }}(el,o));
- AddEvent(el,'mouseout',function(el,o){return function(){
- G.hideTooltip(el);
- }}(el,o));
- }
- G.showTooltip=function(el,o)
- {
- G.tooltipReset();
- var me=G.tooltip;
- me.on=true;
- for (var i in o){me[i]=o[i];}
- me.parent=el;
- if (!me.origin) me.origin='top';
- }
- G.hideTooltip=function(el)
- {
- var prev=G.tooltip.parent;
- if (el==-1) G.tooltipReset();
- else if (!el || el==prev)
- {
- G.tooltipReset();
- if (!prev) G.tooltip.settled=true;
- var underneath=document.elementFromPoint(G.mouseX,G.mouseY);
- if (underneath && prev && underneath!=prev)
- {
- underneath.dispatchEvent(G.pseudoHover);
- G.tooltip.settled=true;
- }
- }
- }
- G.tooltipDraw=function()
- {
- var me=G.tooltip;
- if (me.on)
- {
- if (!me.parent || !document.body.contains(me.parent)) {G.hideTooltip();}
- else
- {
- if (!me.settled)
- {
- if (me.classes) G.tooltipL.className=me.classes;
- if (me.text) G.tooltipContentL.innerHTML=me.text;
- G.tooltipL.style.opacity='0';
- G.tooltipL.style.display='block';
- G.tooltipL.classList.remove('stretchIn');
- G.tooltipL.classList.remove('stretchInV');
- }
- if (me.func && me.t%10==0) G.tooltipContentL.innerHTML=me.func();
- var div=me.parent;
- var box=div.getBoundingClientRect();
- var topb=0;
- var bottomb=G.h;
- var leftb=0;
- var rightb=G.w;
- var margin=8;
- var tx=G.tooltipL.offsetLeft;
- var ty=G.tooltipL.offsetTop;
- var tw=G.tooltipL.clientWidth;
- var th=G.tooltipL.clientHeight;
- var x=0;
- var y=0;
- var i=0;
- var origin=me.origin;
- //try to fit within the screen
- var spins=[];
- if (origin=='top') spins=['top','bottom','right','left'];
- else if (origin=='bottom') spins=['bottom','top','right','left'];
- else if (origin=='left') spins=['left','right','top','bottom'];
- else if (origin=='right') spins=['right','left','top','bottom'];
- for (var i=0;i<4;i++)
- {
- var spin=spins[i];
- origin=spin;
- if (spin=='top')
- {
- x=(box.left+box.right)/2;
- y=box.top;
- x=x-tw/2;
- y=y-th-margin;
- x=Math.max(0,Math.min(G.w-tw,x));
- }
- else if (spin=='bottom')
- {
- x=(box.left+box.right)/2;
- y=box.bottom;
- x=x-tw/2;
- y=y+margin;
- x=Math.max(0,Math.min(G.w-tw,x));
- }
- else if (spin=='left')
- {
- x=box.left;
- y=(box.top+box.bottom)/2;
- x=x-tw-margin;
- y=y-th/2;
- y=Math.max(0,Math.min(G.h-th,y));
- }
- else if (spin=='right')
- {
- x=box.right;
- y=(box.top+box.bottom)/2;
- x=x+margin;
- y=y-th/2;
- y=Math.max(0,Math.min(G.h-th,y));
- }
- if (y>=topb && y+th<=bottomb && x>=leftb && x+tw<=rightb) break;
- }
- G.tooltipPU.style.display='none';
- G.tooltipPD.style.display='none';
- G.tooltipPL.style.display='none';
- G.tooltipPR.style.display='none';
- if (origin=='top')
- {
- G.tooltipPD.style.display='block';
- G.tooltipPD.style.left=Math.floor((box.left+box.right)/2-x-6)+'px';
- G.tooltipPD.style.bottom=Math.floor(-6)+'px';
- }
- else if (origin=='bottom')
- {
- G.tooltipPU.style.display='block';
- G.tooltipPU.style.left=Math.floor((box.left+box.right)/2-x-6)+'px';
- G.tooltipPU.style.top=Math.floor(-6)+'px';
- }
- else if (origin=='left')
- {
- G.tooltipPR.style.display='block';
- G.tooltipPR.style.right=Math.floor(-6)+'px';
- G.tooltipPR.style.top=Math.floor((box.top+box.bottom)/2-y-6)+'px';
- }
- else if (origin=='right')
- {
- G.tooltipPL.style.display='block';
- G.tooltipPL.style.left=Math.floor(-6)+'px';
- G.tooltipPL.style.top=Math.floor((box.top+box.bottom)/2-y-6)+'px';
- }
- if (!me.settled) triggerAnim(G.tooltipL,(origin=='top' || origin=='bottom')?'stretchIn':'stretchInV');
- me.settled=true;
- G.tooltipL.style.left=Math.floor(x)+'px';
- G.tooltipL.style.top=Math.floor(y)+'px';
- G.tooltipL.style.opacity='1';
- me.t++;
- }
- }
- else
- {
- if (!me.settled)
- {
- me.settled=true;
- G.tooltipL.classList.remove('stretchIn');
- G.tooltipL.classList.remove('stretchInV');
- G.tooltipL.style.opacity='0';
- triggerAnim(G.tooltipL,'fadeOutQuick');
- //G.tooltipL.style.display='none';
- //G.tooltipL.className='';
- }
- }
- }
- /*=====================================================================================
- THINGS
- =======================================================================================*/
- G.thingsN=0;
- G.things=[];//by id
- G.thingsByName=[];//by name id
- G.thingsByTag={};
- G.res=[];//by id
- G.buttons=[];//by id
- G.buildings=[];//by id
- G.upgrades=[];//by id
- G.itemTypes=[];//by id
- G.items=[];//by id
- G.itemsN=0;
- G.achievs=[];//by id
- G.shinies=[];//by id
- G.boxes=[];//by id
- G.Thing=function(o)
- {
- if (o.ids) var ids=o.ids.substring(1,o.ids.length).split('|');
- for (var i in ids)
- {
- ids[i]=G.makeSafe(ids[i]);
- }
- this.key='-';
- if (ids) this.key=ids[0];
- if (ids) this.name=ids[0]; else this.name='???';
- this.tags=0;
- this.classes='';
- this.show=1;
- this.lit=0;
- this.alwaysHidden=0;//overrides .show
- this.refreshText=true;//if true, change the text during Draw
- this.toRefresh=1;//if 1, update visibility
- if (G.baseThing)
- {
- for (var i in G.baseThing)
- {
- if (i=='effects')
- {
- if (!this[i]) this[i]={};
- for (var ii in G.baseThing[i])
- {
- if (!this[i][ii]) this[i][ii]=[];
- this[i][ii]=G.baseThing[i][ii];
- }
- }
- else this[i]=G.baseThing[i];
- }
- }
- for (var i in o)
- {
- if (i=='effects')
- {
- if (!this[i]) this[i]={};
- for (var ii in o[i])
- {
- if (!this[i][ii]) this[i][ii]=[];
- this[i][ii]=this[i][ii].concat(o[i][ii]);
- }
- }
- else if (Array.isArray(o[i]) && this[i]) this[i]=this[i].concat(o[i]);
- else this[i]=o[i];
- }
- if (!this.plural) this.plural=this.name;
- if (this.type=='item') this.id='ITEM'+G.itemsN;
- else this.id=G.thingsN;
- this.ids=[];
- if (ids) this.ids=ids;
- if (this.tags) this.tags=this.tags.split(' '); else this.tags=[];
- for (var i in this.tags)
- {
- this.tags[i]=G.makeSafe(this.tags[i]);
- if (!G.thingsByTag[this.tags[i]]) G.thingsByTag[this.tags[i]]=[];
- G.thingsByTag[this.tags[i]].push(this);
- }
- if (this.type=='res') G.res.push(this);
- else if (this.type=='button') G.buttons.push(this);
- else if (this.type=='building') G.buildings.push(this);
- else if (this.type=='upgrade') G.upgrades.push(this);
- else if (this.type=='itemType') G.itemTypes.push(this);
- else if (this.type=='item') G.items.push(this);
- else if (this.type=='achiev') G.achievs.push(this);
- else if (this.type=='shiny') G.shinies.push(this);
- else if (this.type=='box') G.boxes.push(this);
- else {G.parseError('The type "'+this.type+'" is not recognized.');return {type:0};}
- if (this.type!='item' && this.type!='box') G.things.push(this);
- if (ids) {for (var i in ids){G.thingsByName[ids[i]]=this;}}
- //console.log(this);
- if (this.type=='item') G.itemsN++; else if (this.type!='box') G.thingsN++;
- return this;
- }
- G.createThing=function(thing)
- {
- if (thing)
- {
- if (thing.ids=='*TEMPLATE') {G.baseThing=thing;}
- else new G.Thing(thing);
- }
- }
- G.copyEffects=function(effects)
- {
- //returns new effects by type cloned from the original; this lets us do have a Thing using the same effects as another
- var out={};
- for (var effectType in effects)
- {
- out[effectType]=[];
- for (var i in effects[effectType])
- {
- var eff={};
- for (var ii in effects[effectType][i])
- {
- eff[ii]=effects[effectType][i][ii];
- }
- if (eff.type=='if')
- {
- eff.effs=G.copyEffects(eff.effs);
- }
- out[effectType].push(eff);
- }
- }
- return out;
- }
- G.shiniesE=[];
- G.shiniesL=l('shinies');
- G.shiniesN=0;
- G.spawnShiny=function(type)
- {
- var fail=false;
- var me={
- type:type,
- x:0,
- y:0,
- t:0,
- tm:Math.max(0,G.fps*type.dur*type.durMult),
- };
- me.l=document.createElement('div');
- var moves=me.type.moves;
- if ('anywhere' in moves)
- {
- me.x=Math.random()*G.w;
- me.y=Math.random()*(G.h);
- }
- else if ('onRight' in moves)
- {
- me.x=G.w;
- me.y=Math.random()*(G.h);
- }
- else if ('onLeft' in moves)
- {
- me.x=0;
- me.y=Math.random()*(G.h);
- }
- else if ('onBottom' in moves)
- {
- me.x=Math.random()*G.w;
- me.y=(G.h);
- }
- else if ('onTop' in moves)
- {
- me.x=Math.random()*G.w;
- me.y=0;
- }
- else if ('onMouse' in moves)
- {
- me.x=G.mouseX;
- me.y=G.mouseY;
- }
- else if ('onThing' in moves || 'onBox' in moves)
- {
- if ('onThing' in moves) var it=moves['onThing'];
- else var it=moves['onBox'];
- if (it && it.l)
- {
- var box=it.l.getBoundingClientRect();
- me.x=box.left+Math.random()*(box.right-box.left);
- me.y=box.top+Math.random()*(box.bottom-box.top);
- }
- else fail=true;
- }
- me.a=0;
- if ('randomAngle' in moves)
- {
- me.a=Math.random()*360;
- }
- me.d=0;
- if ('moveRandom' in moves)
- {
- me.d=Math.random();
- }
- if (fail) return false;
- var classes='thing shiny';
- if (me.type.classes) classes+=' '+me.type.classes;
- if (!me.type.noClick)
- {
- AddEvent(me.l,'click',function(me){return function(){
- if (me.t<me.tm) {me.type.click();me.t=me.tm;}
- }}(me));
- }
- var str='';
- if (!me.type.icon) classes+=' noIcon';
- else
- {
- var icon=G.resolveIcon(me.type.icon);
- str+='<div class="thing-icon shiny-icon" style="'+icon+'"></div>';
- }
- if (me.type.noText || !me.type.customName) classes+=' noText';
- else str+='<div class="thing-text shiny-text">'+me.type.name+'</div>';
- me.l.innerHTML=str;
- me.l.className=classes;
- G.shiniesL.appendChild(me.l);
- me.offx=-me.l.clientWidth/2;
- me.offy=-me.l.clientHeight/2;
- me.id=G.shiniesN;
- G.shiniesN++;
- G.shiniesE.push(me);
- }
- G.shiniesLogic=function()
- {
- var shinies=[];
- for (var i in G.shiniesE)
- {
- var me=G.shiniesE[i];
- me.t++;
- if (me.t>=me.tm)
- {
- me.l.parentNode.removeChild(me.l);
- }
- else shinies.push(me);
- }
- G.shiniesE=shinies;
- }
- G.shiniesDraw=function()
- {
- for (var i in G.shiniesE)
- {
- var me=G.shiniesE[i];
- var moves=me.type.moves;
- var r=me.t/me.tm;
- var x=me.x;
- var y=me.y;
- var o=1;
- var a=me.a;
- var s=1;
- if ('fade' in moves)
- {
- if (r<0.15) o=r/0.15;
- else if (r<0.85) o=1;
- else o=1-(r-0.85)/0.15;
- }
- if ('grow' in moves) s*=r;
- else if ('shrink' in moves) s*=1-r;
- else if ('growShrink' in moves) s*=Math.sqrt(1-(1-r*2)*(1-r*2));
- if ('wiggle' in moves)
- {
- a+=Math.sin(me.t*(moves['wiggle']||0.25)+me.id)*18;
- }
- if ('spinCW' in moves) a+=me.t*(moves['spinCW']||1);
- else if ('spinCCW' in moves) a-=me.t*(moves['spinCCW']||1);
- else if ('spinRandom' in moves) a+=me.t*(me.id%2==0?1:-1)*(moves['spinRandom']||1);
- if ('pulse' in moves)
- {
- s*=1+0.05*Math.sin(me.t*(moves['pulse']||0.35)+me.id);
- }
- if ('followMouse' in moves)
- {
- x=G.mouseX;
- y=G.mouseY;
- }
- else if ('followMouseSlow' in moves)
- {
- x+=(G.mouseX-x)*(moves['followMouseSlow']||0.1);
- y+=((G.mouseY)-y)*(moves['followMouseSlow']||0.1);
- me.x=x;
- me.y=y;
- }
- else if ('moveRandom' in moves)
- {
- x+=Math.sin(me.d*Math.PI*2)*(moves['moveRandom']||3);
- y+=Math.cos(me.d*Math.PI*2)*(moves['moveRandom']||3);
- me.x=x;
- me.y=y;
- }
- else if ('moveLeft' in moves) x=moves['moveLeft']||(me.x*(1-r));
- else if ('moveRight' in moves) x=moves['moveRight']||(me.x+(G.w-me.x)*(r));
- else if ('moveTop' in moves) y=moves['moveTop']||(me.y*(1-r));
- else if ('moveBottom' in moves) y=moves['moveBottom']||(me.y+(G.h-me.y)*(r));
- if ('bobVertical' in moves)
- {
- y+=Math.sin(me.t*(moves['bobVertical']||0.2)+me.id)*8;
- }
- if ('bobHorizontal' in moves)
- {
- x+=Math.cos(me.t*(moves['bobHorizontal']||0.2)+me.id)*8;
- }
- var sx=s;
- var sy=s;
- if ('bounce' in moves)
- {
- var bounce=me.t*(moves['bounce']||0.05)+me.id;
- y-=Math.abs(Math.cos(bounce)*128)-64;
- /*sx=1+Math.sin(bounce*2+0.3*(Math.PI*2))*0.2;
- sy=1+Math.sin(bounce*2-0.2*(Math.PI*2))*0.2;
- a+=Math.sin(bounce*2-0.15*(Math.PI*2))*18+12;*/
- }
- x+=me.offx;
- y+=me.offy;
- me.l.style.transform='translate('+(x)+'px,'+(y)+'px) rotate('+(a)+'deg) scale('+(sx)+','+(sy)+')';
- me.l.style.opacity=o;
- }
- }
- G.A=0;
- G.itemsMax=100;//how many items can be owned maximum
- G.itemsTotal=0;//how many items we currently have
- G.gainItem=function(thing,o)
- {
- if (G.itemsTotal>=G.itemsMax) return false;
- if (thing && thing.type=='itemType')
- {
- var inherit=['name','classes','costs','effects'];
- var data={type:'item',base:thing};
- for (var i in inherit)
- {
- data[inherit[i]]=thing[inherit[i]];
- }
- data.effects=G.copyEffects(data.effects);
- for (var i in o)
- {
- data[i]=o[i];
- }
- var item=new G.Thing(data);
- item.tags=thing.tags;
- if (G.domReady) item.createDom();
- G.itemsTotal++;
- return item;
- }
- else console.log('Couldn\'t create the item : ',thing,o||'no extra parameters');
- return false;
- }
- G.loseItem=function(thing)
- {
- if (thing && thing.type=='itemType')
- {
- var ret=false;
- for (var i in G.items)
- {
- var item=G.items[i];
- if (item.base==thing && !item.removed)
- {
- item.remove();
- ret=item;
- break;
- }
- }
- return ret;
- }
- else if (thing && thing.type=='item')
- {
- thing.remove();
- return thing;
- }
- else console.log('Couldn\'t lose the item : ',thing);
- return false;
- }
- G.Thing.prototype.remove=function()
- {
- if (this.type=='item' && !this.removed)
- {
- this.removed=true;
- this.doEffects('undo grants');
- if (G.domReady) this.removeDom();
- G.itemsTotal--;
- G.itemsToRefresh=true;
- }
- }
- G.itemsToRefresh=true;
- G.refreshItems=function()
- {
- G.itemsToRefresh=false;
- var arr=[];
- for (var i in G.items)
- {
- var me=G.items[i];
- if (!me.removed)
- {
- arr.push(me);
- }
- else
- {
- if (G.things.indexOf(me)!=-1) G.things.splice(G.things.indexOf(me),1);
- }
- }
- G.items=arr;
- }
- G.Thing.prototype.tooltip=function()
- {
- var me=this;
- var str='';
- if (!me.noBuy && me.costs.length>0)//me.type=='res' || me.type=='building')
- {
- str+='<div class="costs">'+G.getCostsStr(me.getCosts())+'</div>';
- }
- if (me.icon) str+='<div class="thing-icon" style="'+G.resolveIcon(me.icon,true)+'"></div>';
- if (me.name) str+='<div class="title">'+me.name+'</div>';
- if ((me.type=='upgrade' || me.type=='achiev') && me.owned) str+='<div class="subtitle">(owned)</div>';
- if (me.type=='res' || me.type=='building') str+='<div class="subtitle">(amount : '+B(me.amount)+')</div>';
- if (me.showEarned) str+='<div class="subtitle">(total earned : '+B(me.earned)+')</div>';
- if (me.showMax) str+='<div class="subtitle">(max : '+B(me.maxAmount)+')</div>';
- if (me.showClicks) str+='<div class="subtitle">(clicks : '+B(me.clicks)+')</div>';
- if (me.desc) str+='<div class="desc"><div>'+G.getTextValue(me.desc,me)+'</div></div>';
- return str;
- }
- G.Thing.prototype.updateDisplayed=function()
- {
- var me=this;
- var hide=false;
- if (me.alwaysHidden || !me.show) hide=true;
- else
- {
- if (me.hiddenWhen0)
- {
- if ((me.type=='building' || me.type=='res') && me.amount<=0) hide=true;
- else if ((me.type=='upgrade' || me.type=='achiev') && me.owned<=0) hide=true;
- }
- if (!hide && me.reqFunc)
- {
- if ((me.type=='building' || me.type=='res') && !me.checkReqs()) hide=true;
- else if ((me.type=='upgrade' || me.type=='achiev') && me.owned<=0 && !me.checkReqs()) hide=true;
- }
- }
- me.displayed=hide?0:1;
- }
- G.Thing.prototype.createDom=function()
- {
- var me=this;
- if (!me.alwaysHidden && me.type!='itemType')
- {
- //select which box we should put this in; first by tag, then by type
- var box=0;
- var category=0;
- for (var i in me.tags)
- {
- if (G.thingPlacement[me.tags[i]]) {category=me.tags[i];box=G.thingPlacement[category];break;}
- }
- if (!box)
- {
- if (me.type=='res') category='Resources';
- else if (me.type=='button') category='Buttons';
- else if (me.type=='building') category='Buildings';
- else if (me.type=='upgrade') category='Upgrades';
- else if (me.type=='achiev') category='Achievements';
- else if (me.type=='item') category='Items';
- if (category && G.thingPlacement[category])
- {
- box=G.thingPlacement[category];
- }
- }
- if (box && box.type && box.type=='box')
- {
- me.box=box;
- var classes='thing '+me.type;
- if (me.classes) classes+=' '+me.classes;
- if (false && me.show) classes+=' visible'; else classes+=' hidden';
- if (me.lit) classes+=' lit'; else classes+=' dim';
- if (!me.icon) classes+=' noIcon';
- if (me.noText) classes+=' noText';
- if (me.tags) classes+=' tag-'+me.tags.join(' tag-');
- var iconClasses='thing-icon';
- if (me.iconClasses) iconClasses+=' '+me.iconClasses;
- var icon=G.resolveIcon(me.icon);
- var div=me.box.childrenl[category];
- var str='';
- str+='<div id="thing-'+me.id+'" class="'+classes+'">';
- if (me.box.showIcons && me.icon) str+='<div id="thing-icon-'+me.id+'" class="'+iconClasses+'" style="'+icon+'"></div>';
- if (me.text && !me.noText) str+='<div id="thing-text-'+me.id+'" class="thing-text">'+G.getTextValue(me.text,me)+'</div>';
- else if ((me.box.showNames && !me.noText) || !me.icon) str+='<div id="thing-text-'+me.id+'" class="thing-text">'+me.name+'</div>';
- if (me.box.showCosts && !me.noBuy && me.costs.length>0 && !me.noText) str+='<div id="thing-costs-'+me.id+'" class="thing-costs"></div>';
- str+='</div>';
- div.appendChild(document.createRange().createContextualFragment(str));
- me.l=l('thing-'+me.id)||0;
- me.iconl=l('thing-icon-'+me.id)||0;
- me.textl=l('thing-text-'+me.id)||0;
- me.costsl=l('thing-costs-'+me.id)||0;
- if (me.l)
- {
- AddEvent(me.l,'click',function(me){return function(){me.click();}}(me));
- if (me.tooltip && !me.noTooltip && !me.box.noTooltip)
- {
- var obj={func:function(me){return function(){return me.tooltip();}}(me)};
- if (me.tooltipOrigin) obj.origin=me.tooltipOrigin;
- else if (me.box.tooltipOrigin) obj.origin=me.box.tooltipOrigin;
- if (me.tooltipClasses) obj.classes=me.tooltipClasses;
- else if (me.box.tooltipClasses) obj.classes=me.box.tooltipClasses;
- G.addTooltip(me.l,obj);
- }
- }
- }
- }
- }
- G.Thing.prototype.getQuickDom=function(id)
- {
- //returns simplified non-gameplay DOM with no bindings save for tooltip, such as something you'd see in the stats page
- var me=this;
- var classes='thing '+me.type;
- if (me.classes) classes+=' '+me.classes;
- if (!me.icon) classes+=' noIcon';
- if (me.noText) classes+=' noText';
- if (me.tags) classes+=' tag-'+me.tags.join(' tag-');
- var iconClasses='thing-icon';
- if (me.iconClasses) iconClasses+=' '+me.iconClasses;
- var icon=G.resolveIcon(me.icon);
- var str='';
- str+='<div '+(id?'id="'+id+'" ':'')+'class="'+classes+'">';
- str+='<div class="'+iconClasses+'" style="'+icon+'"></div>';
- if (!me.icon) str+='<div class="thing-text">'+me.name+'</div>';
- str+='</div>';
- if (me.tooltip && !me.noTooltip)
- {
- var obj={func:function(me){return function(){return me.tooltip();}}(me)};
- if (me.tooltipClasses) obj.classes=me.tooltipClasses;
- str=G.tooltipped(str,obj,'display:inline-block;');
- }
- return str;
- }
- G.Thing.prototype.removeDom=function()
- {
- if (G.tooltip.parent==this.l) G.hideTooltip();
- this.l.parentNode.removeChild(this.l);
- delete this.l;
- delete this.iconl;
- delete this.textl;
- delete this.costsl;
- }
- G.Thing.prototype.getCosts=function(mult)
- {
- //also see : G.getCostsStr, G.spendCosts
- var costs=this.costs;
- var costsByThing={};
- var mult=mult||1;
- var refund=false;
- if (mult<0) refund=true;
- var add=this.costAdd;
- mult*=this.costMult;
- if (refund) mult*=this.refundMult;
- for (var i in costs)
- {
- for (var ii in costs[i])
- {
- var me=costs[i][ii];
- var w=me.w;
- var v=(me.v);
- if (this.type=='building') v=(me.v*Math.pow(this.costRate,Math.max(0,this.amount)));
- v+=add;
- v+=w.costAdd;
- if (this.costAddFor[w.id]) v+=this.costAddFor[w.id];
- v*=mult;
- v*=w.costMult;
- if (refund) v*=w.refundMult;
- if (this.costMultFor[w.id]) v*=this.costMultFor[w.id];
- if (refund && this.refundMultFor[w.id]) v*=this.refundMultFor[w.id];
- if (refund && this.type=='building' && this.amount==0) v=0;
- if (!costsByThing[w.id]) costsByThing[w.id]=0;
- costsByThing[w.id]+=v;
- }
- }
- return costsByThing;
- }
- G.Thing.prototype.checkReqs=function()
- {
- if (this.reqFunc && this.reqFunc()) return true;
- else return false;
- }
- G.Thing.prototype.logic=function()
- {
- //every logic tick
- if (true)
- {
- var me=this;
- if (me.type=='res')
- {
- if (me.amountD!=me.amount) me.refreshText=true;
- if (Math.abs(me.amount-me.amountD)<0.01) {me.amountD=me.amount;}
- else {me.amountD+=(me.amount-me.amountD)*(Math.abs(me.amount-me.amountD)<10?0.5:0.1);}
- }
- else if (me.type=='shiny')
- {
- if (me.freq>0)
- {
- if (me.timeLeft>0)
- {
- me.timeLeft--;
- if (me.timeLeft==0)
- {
- G.spawnShiny(me);
- me.timeLeft=-1;
- }
- }
- if (me.timeLeft==-1)//init time
- {
- me.timeLeft=Math.ceil(Math.max(0,(me.freq+Math.random()*me.freqV)*me.freqMult)*G.fps);
- }
- }
- }
- }
- }
- G.Thing.prototype.draw=function()
- {
- //every draw tick
- var me=this;
- if (me.l && !me.alwaysHidden)
- {
- if (me.toRefresh)
- {
- if (me.toRefresh!=2) me.updateDisplayed();
- if (!me.displayed)
- {
- if (G.tooltip.parent==me.l) G.hideTooltip(-1);
- me.l.classList.add('hidden');
- me.l.classList.remove('visible');
- }
- else
- {
- me.l.classList.add('visible');
- me.l.classList.remove('hidden');
- var lit=me.lit;
- if (lit)
- {
- this.l.classList.add('lit');
- this.l.classList.remove('dim');
- }
- else
- {
- this.l.classList.add('dim');
- this.l.classList.remove('lit');
- }
- }
- }
- if (me.text && (me.toRefresh || G.drawT%10==0))
- {
- me.textl.innerHTML=G.getTextValue(me.text,me);
- }
- me.toRefresh=0;
- if (me.displayed)
- {
- if (me.text)
- {
- }
- else if (me.refreshText)
- {
- me.refreshText=false;
- if (me.type=='res')
- {
- var str=me.plural+' : ';
- str+=B(Math.floor(me.amountD));
- if (me.box.showPs)
- {
- var ps=me.ps;
- if (ps>0) str+=' (+'+B(ps,1)+'/s)';
- else if (ps<0) str+=' ('+B(ps,1)+'/s)';
- //else str+=' (0/s)';
- }
- me.textl.innerHTML=str;
- }
- else if (me.type=='building')
- {
- var str=me.plural+' : ';
- str+=B(me.amount);
- me.textl.innerHTML=str;
- }
- }
- if (me.type=='res')
- {
- if (me.ps>0) me.l.classList.add('earning');
- else me.l.classList.remove('earning');
- if (me.ps<0) me.l.classList.add('losing');
- else me.l.classList.remove('losing');
- }
- if (G.drawT%10==0)
- {
- var owned=0;
- if (me.owned && (me.type=='upgrade' || me.type=='achiev')) owned=1;
- else if (me.amount>0 && (me.type=='res' || me.type=='building')) owned=1;
- if (me.costs.length>0)
- {
- var amount=1;
- if (me.type=='building') {amount=(G.bulk<0?G.bulk*0.5:G.bulk);}
- var costs=G.getCostsStr(me.getCosts(amount),(me.type=='upgrade'?(owned>0):0),true);
- if (!me.noText && me.costsl) me.costsl.innerHTML=costs.str;
- if (costs.lacking>0 && (!owned || me.type=='building')) me.l.classList.add('cantAfford');
- else me.l.classList.remove('cantAfford');
- }
- me.updateOwned(owned>0);
- }
- }
- }
- }
- G.bulk=1;//how much of buildings we buy at once (or sell, if negative)
- G.Thing.prototype.click=function()
- {
- if (true)
- {
- var win=false;
- var out=0;
- if (this.type=='button' || this.type=='shiny')
- {
- this.clicks++;
- win=true;
- }
- if (this.type=='building' && !this.noBuy)
- {
- var amount=G.bulk;
- if (amount<0)//selling
- {
- if (this.amount+amount>=0) out=G.refundCosts(this.getCosts(-amount*this.refundRate));
- }
- else if (!this.limit || this.limit()>=this.amount+amount)//buying
- {
- out=G.spendCosts(this.getCosts(amount));
- }
- if (out)
- {
- this.doEffects('undo grants');
- this.earn(amount);
- win=true;
- }
- }
- else if (this.type=='upgrade' && !this.owned && !this.noBuy)
- {
- out=G.spendCosts(this.getCosts());
- if (out)
- {
- this.doEffects('undo grants');
- this.earn(1);
- win=true;
- }
- }
- var out2=this.doEffects('click',true);
- if (out2.produced && out.produced)
- {
- for (var i in out2.produced)
- {
- if (!out.produced[i]) out.produced[i]=0;
- out.produced[i]+=out2.produced[i];
- }
- }
- else out=out2;
- if (win && this.l)
- {
- if (this.type=='button') G.hideTooltip();
- if (out && !G.noParticles)
- {
- for (var i in out.produced)
- {G.particleAt(this.l,G.things[i].icon,(G.things[i].icon?'':(G.things[i].name))+(out.produced[i]>0?'+':'')+B(out.produced[i]));}
- }
- }
- }
- }
- G.Thing.prototype.updateOwned=function(val)
- {
- if (val) {this.l.classList.add('owned');this.l.classList.remove('notOwned');}
- else {this.l.classList.add('notOwned');this.l.classList.remove('owned');}
- }
- G.Thing.prototype.set=function(v)
- {
- var w=this;
- if (w.type=='res' || w.type=='building')
- {
- if (w.type=='building') v=Math.round(v);
- w.amount=v;
- if (w.type=='res' && v>0) w.earned=Math.max(v,w.earned);
- if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
- w.maxAmount=Math.max(w.amount,w.maxAmount);
- this.updateOwned(this.amount>0);
- this.refreshText=true;
- }
- else if (w.type=='upgrade' || w.type=='achiev')
- {
- var v2=v?1:0;
- if (w.owned!=v2 && w.l)
- {
- w.updateOwned(v2>0);
- }
- w.owned=v2;
- }
- }
- G.Thing.prototype.earn=function(v)
- {
- var w=this;
- if (w.type=='res' || w.type=='building')
- {
- if (w.type=='building') v=Math.round(v);
- var old=w.amount;
- w.amount+=v;
- if (w.type=='res' && v>0) w.earned+=v;
- if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
- if (w.amount-old>0) w.doEffects('earn');
- else if (w.amount-old<0) w.doEffects('lose');
- w.maxAmount=Math.max(w.amount,w.maxAmount);
- if (w.l)
- {
- w.updateOwned(w.amount>0);
- }
- w.refreshText=true;
- }
- else if (w.type=='upgrade' || w.type=='achiev')
- {
- var v2=v?1:0;
- if (w.owned!=v2 && w.l)
- {
- w.updateOwned(v2>0);
- }
- w.owned=v2;
- if (w.owned) w.doEffects('earn');
- else w.doEffects('lose');
- if (w.owned && w.type=='achiev')
- {
- var str='';
- if (w.icon) str+='<div class="thing-icon" style="'+G.resolveIcon(w.icon,true)+'"></div>';
- if (w.name) str+='Got achievement :<div class="title">'+w.name+'</div>'; else str+='Got achievement!';
- G.toast({text:str,dur:10});
- }
- }
- }
- G.Thing.prototype.grant=function(v)
- {
- var w=this;
- if (w.type=='res' || w.type=='building')
- {
- if (w.type=='building') v=Math.round(v);
- w.amount+=v;
- if (w.type=='res' && v>0) w.earned=Math.max(w.earned,w.amount);
- if (!w.canBeNegative) w.amount=Math.max(0,w.amount);
- w.maxAmount=Math.max(w.amount,w.maxAmount);
- if (w.l)
- {
- w.updateOwned(w.amount>0);
- }
- w.refreshText=true;
- }
- }
- G.Thing.prototype.light=function()
- {
- if (this.lit || this.alwaysHidden) return false;
- this.lit=1;
- this.toRefresh=1;
- }
- G.Thing.prototype.dim=function()
- {
- if (!this.lit || this.alwaysHidden) return false;
- this.lit=0;
- this.toRefresh=1;
- }
- G.Thing.prototype.display=function()
- {
- if (this.show || this.alwaysHidden) return false;
- this.show=1;
- this.toRefresh=1;
- }
- G.Thing.prototype.hide=function()
- {
- if (!this.show || this.alwaysHidden) return false;
- this.show=0;
- this.toRefresh=1;
- }
- G.doEffectsForAll=function(type)
- {
- var things=[];
- for (var i in G.things)
- {
- var me=G.things[i];
- 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);
- }
- for (var i in G.items)
- {things.push(G.items[i]);}
- for (var i in things)
- {
- var me=things[i];
- me.doEffects(type);
- }
- }
- /*=====================================================================================
- TICK
- =======================================================================================*/
- G.tick=function()
- {
- //every second :
- var things=[];
- for (var i in G.things)
- {
- var me=G.things[i];
- 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);
- }
- for (var i in G.items)
- {things.push(G.items[i]);}
- //reset boosts and production
- for (var i in G.things)
- {
- var me=G.things[i];
- me.ps=0;
- me.boostAdd=0;
- me.boostMult=1;
- me.boostAddFor=[];
- me.boostMultFor=[];
- me.costAdd=0;
- me.costMult=1;
- me.refundMult=1;
- me.costAddFor=[];
- me.costMultFor=[];
- me.refundMultFor=[];
- if (me.type=='shiny')
- {
- me.durMult=1;
- me.freqMult=1;
- }
- }
- //cache boosts
- for (var i in things)
- {
- var me=things[i];
- me.doEffects('cache boosts');
- }
- //apply grants
- for (var i in things)
- {
- var me=things[i];
- me.doEffects('do grants');
- }
- //cache production
- for (var i in things)
- {
- var me=things[i];
- me.doEffects('cache ps');
- }
- //tick
- for (var i in things)
- {
- var me=things[i];
- me.doEffects('tick');
- }
- //apply production
- for (var i in things)
- {
- var me=things[i];
- if (me.type=='res' || me.type=='building')
- {
- if (me.ps!=0)
- {
- me.earn(me.ps);
- }
- }
- }
- for (var i in G.things)
- {
- var me=G.things[i];
- if (me.isAlways) me.amount=me.isAlways();
- if (me.limit) me.amount=Math.min(me.limit(),me.amount);
- if (me.type=='achiev' && !me.owned)
- {
- if (me.checkReqs())
- {
- me.earn(1);
- }
- }
- var old=me.displayed||0;
- me.updateDisplayed();
- if (me.displayed!=old) me.toRefresh=2;//triggers a refresh without re-running updateDisplayed()
- }
- }
- /*=====================================================================================
- EFFECTS
- =======================================================================================*/
- G.effectNestLevel=0;
- G.localVars=[];
- G.doEffect=function(effect,owner,context,amount,out)
- {
- G.effectNestLevel++;
- if (G.effectNestLevel>10) {G.effectNestLevel--;return false;}//possible endless loop
- var me=effect;
- var type=me.type;
- if (me.w)
- {
- var w=me.w;
- if (w==='this') w=owner;
- else if (w[0]==='this') w=[owner];
- }
- if (type=='if' || type=='else')
- {
- var pass=false;
- if (me.cond && context!='undo grants')//when undoing grants, disregard conditions
- {
- var amount2=0;
- if (!owner) amount2=0;
- else
- {
- if (owner.type=='building' || owner.type=='res') amount2=owner.amount;
- else if (owner.type=='upgrade' || owner.type=='achiev') amount2=(owner.owned?1:0);
- else if (owner.type=='button' || owner.type=='shiny') amount2=owner.clicks;
- }
- if (me.cond(owner,amount2)) pass=true;
- }
- else pass=true;
- if (pass)
- {
- for (var i in me.effs)
- {
- G.doEffect(me.effs[i],owner,context,amount,out);
- }
- }
- else if (me.or)
- {
- for (var i in me.or)
- {
- G.doEffect(me.or[i],owner,context,amount,out);
- }
- }
- }
- else if (type=='grant' && context=='do grants' && !me.done)
- {
- me.done=true;
- w.grant(amount*G.getVarValue(me.v,owner));
- }
- else if (type=='grant' && context=='undo grants' && me.done)
- {
- me.done=false;
- w.grant(-amount*G.getVarValue(me.v,owner));
- }
- else if (context=='do grants' || context=='undo grants') {}
- else if (context=='cache boosts')
- {
- if (type=='increase yield' || type=='lower yield')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- if (type=='lower yield') gain*=-1;
- for (var i=0;i<w.length;i++)
- {
- w[i].boostAdd+=gain;
- }
- }
- else if (type=='multiply yield')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- w[i].boostMult*=gain;
- }
- }
- else if (type=='increase yield for' || type=='lower yield for')
- {
- var w=G.getThings(me.w,owner);
- var z=G.getThings(me.z,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- if (type=='lower yield for') gain*=-1;
- for (var i=0;i<w.length;i++)
- {
- for (var ii=0;ii<z.length;ii++)
- {
- if (!w[i].boostAddFor[z[ii].id]) w[i].boostAddFor[z[ii].id]=0;
- w[i].boostAddFor[z[ii].id]+=gain;
- }
- }
- }
- else if (type=='multiply yield for')
- {
- var w=G.getThings(me.w,owner);
- var z=G.getThings(me.z,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- for (var ii=0;ii<z.length;ii++)
- {
- if (!w[i].boostMultFor[z[ii].id]) w[i].boostMultFor[z[ii].id]=1;
- w[i].boostMultFor[z[ii].id]*=gain;
- }
- }
- }
- else if (type=='increase cost' || type=='lower cost')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- if (type=='lower cost') gain*=-1;
- for (var i=0;i<w.length;i++)
- {
- w[i].costAdd+=gain;
- }
- }
- else if (type=='multiply cost')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- w[i].costMult*=gain;
- }
- }
- else if (type=='multiply refund')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- w[i].refundMult*=gain;
- }
- }
- else if (type=='multiply duration')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- if (w[i].type=='shiny') w[i].durMult*=gain;
- }
- }
- else if (type=='multiply frequency')
- {
- var w=G.getThings(me.w,owner);
- var gain=amount*G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- if (w[i].type=='shiny') w[i].freqMult*=gain;
- }
- }
- }
- else if (context=='cache ps')
- {
- if (type=='yield' || type=='lose')
- {
- var w=G.getThings(me.w,owner);
- var v=amount;
- if (me.v) v*=G.getVarValue(me.v,owner);
- if (owner.type=='item') owner=owner.base;
- for (var i=0;i<w.length;i++)
- {
- var w2=w[i];
- if (w2.type=='res' || w2.type=='building')
- {
- if (type=='lose') v=-v;
- else if (w.type!='building')
- {
- v+=w2.boostAdd;
- v*=w2.boostMult;
- v+=owner.boostAdd;
- v*=owner.boostMult;
- if (owner.boostAddFor[w2.id]) v+=owner.boostAddFor[w2.id];
- if (owner.boostMultFor[w2.id]) v*=owner.boostMultFor[w2.id];
- }
- w2.ps+=v;
- }
- }
- }
- }
- else//on tick, on click, others
- {
- if (type=='set')
- {
- var w=G.getThings(me.w,owner);
- var v=G.getVarValue(me.v,owner);
- for (var i=0;i<w.length;i++)
- {
- if (w[i].type=='res' || w[i].type=='building') w[i].set(v);
- }
- }
- else if (type=='set var')
- {
- var w=me.w;
- var v=G.getVarValue(me.v,owner);
- G.localVars[w]=v;
- }
- else if ((type=='yield' || type=='lose') && context!='tick')
- {
- var w2=G.getThings(me.w,owner);
- var v=amount;
- if (me.v) v*=G.getVarValue(me.v,owner);
- if (owner.type=='item') owner=owner.base;
- for (var i=0;i<w2.length;i++)
- {
- var w=w2[i];
- if ((w.type=='upgrade' || w.type=='achiev'))
- {
- if (type=='yield' && !w.owned) {w.owned=1;w.doEffects('earn');}
- else if (type=='lose' && w.owned) {w.owned=0;w.doEffects('lose');}
- }
- else if (w.type=='itemType' || w.type=='item')
- {
- if (type=='yield')//gain V items of type W, or of the same type as the existing item W
- {
- if (w.type=='item') w=w.base;
- for (var ii=0;ii<v;ii++)
- {
- var newItem=G.gainItem(w);
- }
- }
- else//lose V items of type W, or lose the specified existing item
- {
- if (w.type=='itemType')
- {
- var v2=('v' in me?v:100);
- if (v2<0) v2=0;
- for (var ii=0;ii<v2;ii++)
- {
- G.loseItem(w);
- }
- }
- else
- {
- G.loseItem(w);
- }
- }
- }
- else
- {
- if (w.type=='res' || w.type=='building')
- {
- var v2=(('v' in me || type=='yield')?v:w.amount);
- if (type=='lose') v2=-v2;
- else if (w.type=='res')
- {
- v2+=w.boostAdd;
- v2*=w.boostMult;
- v2+=owner.boostAdd;
- v2*=owner.boostMult;
- if (owner.boostAddFor[w.id]) v2+=owner.boostAddFor[w.id];
- if (owner.boostMultFor[w.id]) v2*=owner.boostMultFor[w.id];
- }
- if (out && v2!=0)
- {
- if (!out.produced[w.id]) out.produced[w.id]=0;
- out.produced[w.id]+=v2;
- }
- w.earn(v2);
- }
- }
- }
- }
- else if (type=='spawn')
- {
- if (me.w.type=='shiny') G.spawnShiny(me.w);
- }
- else if (type=='log')
- {
- if (me.classes) G.log(G.getTextValue(me.w,owner,1),me.classes);
- else G.log(G.getTextValue(me.w,owner,1));
- }
- else if (type=='clear log')
- {
- G.clearLog();
- }
- else if (type=='toast')
- {
- G.toast({text:'<div>'+G.getTextValue(me.w,owner,1)+'</div>',classes:'center',dur:10});
- }
- else if (type=='echo')
- {
- console.log(owner.name+' : '+G.makeUnsafe(G.getTextValue(me.w,owner,1)));
- }
- else if (type=='do')
- {
- for (var i in w)
- {
- var it=w[i];
- if (it.effects[me.v]) it.doEffects(me.v);
- }
- }
- else if (type=='light')
- {
- var w=G.getThings(me.w,owner);
- for (var i in w){w[i].light();}
- }
- else if (type=='dim')
- {
- var w=G.getThings(me.w,owner);
- for (var i in w){w[i].dim();}
- }
- else if (type=='show')
- {
- var w=G.getThings(me.w,owner);
- for (var i in w){w[i].display();}
- }
- else if (type=='hide')
- {
- var w=G.getThings(me.w,owner);
- for (var i in w){w[i].hide();}
- }
- else if (type=='anim')
- {
- if (owner.l) triggerAnim(owner.l,me.w);
- }
- else if (type=='animicon')
- {
- if (owner.iconl) triggerAnim(owner.iconl,me.w);
- }
- else if (type=='eval' && true)
- {
- eval(me.w);
- }
- }
- G.effectNestLevel--;
- }
- G.effectsByContext={
- 'start':'start',
- 'save':'save',
- 'load':'load',
- 'earn':'earn',
- 'lose':'lose',
- 'tick':'tick',
- 'cache ps':'tick',
- 'cache boosts':'tick',
- 'do grants':'tick',
- 'undo grants':'tick',
- 'click':'click',
- };
- G.Thing.prototype.doEffects=function(context,returnOut)
- {
- if (returnOut)
- {
- var out={};
- out.produced={};
- }
- var amount=1;
- if (this.type=='building') amount=this.amount;
- var custom=false;
- if (!G.effectsByContext[context]) custom=true;
- var resetVars=true;
- if (custom || context=='earn' || context=='lose') resetVars=false;
- if (resetVars) G.localVars=[];
- var effs=this.effects[G.effectsByContext[context]]||this.effects[context]||[];
- for (var i in effs)
- {
- G.doEffect(effs[i],this,context,amount,out);
- }
- if (resetVars) G.localVars=[];
- if (returnOut) return out;
- }
- G.spendCosts=function(costs,mult)
- {
- var out={produced:{}};
- var mult=mult||1;
- for (var i in costs)
- {
- var w=G.things[i];
- var v=costs[i];
- if (!w.canBeNegative && w.amount<v*mult) return false;
- }
- for (var i in costs)
- {
- var w=G.things[i];
- var v=costs[i]*mult;
- w.earn(-v);
- if (v!=0)
- {
- if (!out.produced[w.id]) out.produced[w.id]=0;
- out.produced[w.id]-=v;
- }
- }
- return out;
- }
- G.refundCosts=function(costs)
- {
- var out={produced:{}};
- for (var i in costs)
- {
- var w=G.things[i];
- var v=-costs[i];
- if (!w.canBeNegative && w.amount<v) return false;
- }
- for (var i in costs)
- {
- var w=G.things[i];
- var v=-costs[i];
- w.earn(-v);
- if (v!=0)
- {
- if (!out.produced[w.id]) out.produced[w.id]=0;
- out.produced[w.id]-=v;
- }
- }
- return out;
- }
- /*=====================================================================================
- HELPERS
- =======================================================================================*/
- G.getCostsStr=function(costs,neutral,specialOutput)
- {
- var str='';
- var notEnough=0;
- var t=0;
- for (var i in costs)
- {
- var w=G.things[i];
- var v=costs[i];
- if (v>w.amount && w.ps<=0) t=-1;
- else if (w.ps>0 && t!=-1) t=Math.max(t,(v-w.amount)/w.ps);
- var classes='cost';
- if (!neutral && !w.canBeNegative && v>w.amount) {classes+=' notEnough';notEnough++;}
- else if (!neutral) classes+=' hasEnough';
- str+='<div class="'+classes+'">'+B(v)+' '+(v==1?w.name:w.plural)+'</div>';
- }
- if (t>0 && !neutral) str+='<div class="costTimeRemaining">(in '+sayTime(t*1000+750)+')</div>';
- if (!specialOutput) return str;
- else return {str:str,lacking:notEnough};
- }
- G.resolveIcon=function(icon,small)
- {
- //returns a bit of CSS
- var str='';
- if (icon)
- {
- str='background-image:';
- for (var ii in icon)
- {
- str+='url('+icon[ii].url+'),';
- }
- str=str.slice(0,-1);
- str+=';background-position:';
- for (var ii in icon)
- {
- str+=icon[ii].x+'px '+icon[ii].y+'px,';
- }
- str=str.slice(0,-1);
- if (small)
- {
- str+=';background-size:';
- for (var ii in icon)
- {
- if (icon[ii].tile) str+='auto,';
- else str+='100%,';
- }
- str=str.slice(0,-1);
- }
- str+=';';
- }
- return str;
- }
- G.strToList=function(str)
- {
- //convert a string such as "3 gold, 1 banana, 11 monkeys" into an array of things [{w:Thing,v:3}...]
- var list={};
- var str=str.replaceAll(', and ',',');
- var str=str.replaceAll(' and ',',');
- var str=str.replaceAll(', ',',');
- str=str.split(',');
- for (var i in str)
- {
- var bit=str[i].split(' ');
- if (bit[1] && G.thingsByName[bit[1]])
- {
- var val=parseFloat(bit[0]);
- var thing=bit[1];//G.thingsByName[bit[1]];
- if (list[thing]) list[thing]+=val;
- else list[thing]=val;
- }
- }
- var list2=[];
- for (var i in list)
- {
- list2.push({w:G.thingsByName[i],v:list[i]});
- }
- return list2;
- }
- G.strToThings=function(str)
- {
- //convert a string such as "monkeys, tagged:fruit" into an array of things [Thing...]
- var list=[];
- var str=str.replaceAll(', and ',',');
- var str=str.replaceAll(' and ',',');
- var str=str.replaceAll(', ',',');
- str=str.split(',');
- for (var i in str)
- {
- var bit=str[i].split(':');
- if (bit[0] && G.thingsByName[bit[0]] && list.indexOf(bit[0])==-1) list.push(bit[0]);
- //TODO : tags
- /*if (bit[1] && G.thingsByName[bit[0]])
- {
- var val=parseFloat(bit[0]);
- var thing=bit[1];//G.thingsByName[bit[1]];
- if (list[thing]) list[thing]+=val;
- else list[thing]=val;
- }*/
- }
- var list2=[];
- for (var i in list)
- {
- list2.push(G.thingsByName[list[i]]);
- }
- return list2;
- }
- /*=====================================================================================
- PARSE EXPRESSION STRING TO FUNCTION
- =======================================================================================*/
- G.parseToFunc=function(cmd,dummyTest)
- {
- /* this function takes a string and returns a function based on the string that can be safely executed
- if dummyTest is true, test the syntax safely; if no syntax error, return 'ok'; otherwise, return the error text
- examples :
- G.parseToFunc('floor(gold/10)+3');
- G.parseToFunc('max(random(2,luck),2)*2');
- available operations :
- a+b
- a-b
- a*b
- a/b
- floor(a)
- round(a)
- ceil(a)
- 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)
- min(a,b)
- max(a,b)
- pow(a,b)
- chance(a) chance(a%) (a is between 0 and 100; returns true if pass, false otherwise)
- random(a) random(a,b)
- frandom(a) frandom(a,b)
- a=b a==b a is b
- a!=b !(a=b) a isn't b
- && and
- || or
- this (if the function is called with func(Thing,Thing.amount) this will return the Thing's amount)
- thingKey (returns the Thing's amount if res or building; if upgrade or achiev, return 1 if owned, 0 if not)
- $localVar (returns the value of the local variable with that name)
- have thingKey (returns whether we have the upgrade or achiev, or if the res or building is above 0)
- */
- //put a space before and after every word and number
- var cmd2='';
- var inWord=0;
- cmd='('+cmd+')';
- for (var i=0;i<cmd.length;i++)
- {
- var it=cmd.charAt(i);
- if (G.Kalphanum.indexOf(it)!=-1 || it==':')
- {
- if (!inWord) cmd2+=' ';
- inWord=1;
- }
- else
- {
- if (inWord) cmd2+=' ';
- inWord=0;
- }
- cmd2+=it;
- }
- cmd2=cmd2.replace(/ +(?= )/g,'');//remove all multiple spaces
- var tokens=[];
- var str=STR2(cmd2);
- var ok=1;
- var prevKeyword='';
- var setKeyword=0;
- while(ok && str.val.length>0)
- {
- var setKeyword=0;
- if (str.gulpSymbol(',')) tokens.push(',');
- else if (str.gulpSymbol('('))
- {
- if (prevKeyword=='floor') tokens.push('Math.floor(');
- else if (prevKeyword=='round') tokens.push('Math.round(');
- else if (prevKeyword=='ceil') tokens.push('Math.ceil(');
- else if (prevKeyword=='roundr') tokens.push('randomFloor(');
- else if (prevKeyword=='min') tokens.push('Math.min(');
- else if (prevKeyword=='max') tokens.push('Math.max(');
- else if (prevKeyword=='pow') tokens.push('Math.pow(');
- else if (prevKeyword=='chance') tokens.push('luck(');
- else if (prevKeyword=='random') tokens.push('rand(');
- else if (prevKeyword=='frandom') tokens.push('frand(');
- else tokens.push('(');
- }
- else if (str.gulpSymbol(')')) tokens.push(')');
- else if (str.gulpSymbol('+')) tokens.push('+');
- else if (str.gulpSymbol('-')) tokens.push('-');
- else if (str.gulpSymbol('*')) tokens.push('*');
- else if (str.gulpSymbol('/')) tokens.push('/');
- else if (str.gulpSymbol('<=')) tokens.push('<=');
- else if (str.gulpSymbol('>=')) tokens.push('>=');
- else if (str.gulpSymbol('<')) tokens.push('<');
- else if (str.gulpSymbol('>')) tokens.push('>');
- else if (str.gulpSymbol('%)') || str.gulpSymbol('% )')) tokens.push(')');
- else if (str.gulpSymbol('%')) tokens.push('%');
- else if (str.gulpSymbol('isn \' t ') || str.gulpSymbol('!=')) tokens.push('!=');
- else if (str.gulp('is') || str.gulpSymbol('==') || str.gulpSymbol('=')) tokens.push('==');
- else if (str.gulpSymbol('!')) tokens.push('!');
- else if (str.gulp('and')) tokens.push('&&');
- else if (str.gulp('or')) tokens.push('||');
- else if (str.gulp('this')) tokens.push(dummyTest?'V':'v');
- else if (str.gulp('ItemsLeft')) tokens.push(' (G.itemsMax-G.itemsTotal) ');
- else if (str.gulp('lit')) tokens.push(dummyTest?'V':'w?w.lit:0');
- else if (str.gulp('dim')) tokens.push(dummyTest?'V':'w?(!w.lit):0');
- else if (str.val.charAt(0)=='$')
- {
- str.gulpSymbol('$');
- var localVar='$'+str.gulp();
- localVar=G.validateVar(localVar);
- if (dummyTest) tokens.push('V'); else tokens.push('(G.localVars[\''+localVar+'\']||0)');
- }
- else
- {
- var tkn=str.gulp();
- //console.log(' doing ['+tkn+']');
- if ( tkn=='have'
- || tkn=='no'
- || tkn=='floor'
- || tkn=='round'
- || tkn=='roundr'
- || tkn=='ceil'
- || tkn=='min'
- || tkn=='max'
- || tkn=='pow'
- || tkn=='chance'
- || tkn=='random'
- || tkn=='frandom'
- ){setKeyword=tkn;}
- else if (G.thingsByName[tkn.split(':')[0]])
- {
- var tkn2=tkn.split(':')[1];
- if (tkn2)
- {
- if ( tkn2=='clicks'
- || tkn2=='ps'
- || tkn2=='max'
- || tkn2=='earned'
- ){}
- else {ok=0;tokens.push(tkn);}
- }
- if (ok)
- {
- if (dummyTest) tokens.push('V');
- else
- {
- var thing=G.thingsByName[tkn.split(':')[0]];
- if (tkn2=='ps' && thing.type=='res') tokens.push('(G.things['+thing.id+'].ps)');
- else if (tkn2=='ps') tokens.push('(false)');
- else if (tkn2=='clicks' && (thing.type=='button' || thing.type=='shiny')) tokens.push('(G.things['+thing.id+'].clicks)');
- else if (tkn2=='clicks') tokens.push('(false)');
- else if (tkn2=='max' && (thing.type=='res' || thing.type=='building')) tokens.push('(G.things['+thing.id+'].maxAmount)');
- else if (tkn2=='max') tokens.push('(false)');
- else if (tkn2=='earned' && (thing.type=='res')) tokens.push('(G.things['+thing.id+'].earned)');
- else if (tkn2=='earned') tokens.push('(false)');
- else if (prevKeyword=='have')
- {
- if (thing.type=='res' || thing.type=='building') tokens.push('(G.things['+thing.id+'].amount>0)');
- else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==1)');
- }
- else if (prevKeyword=='no')
- {
- if (thing.type=='res' || thing.type=='building') tokens.push('(G.things['+thing.id+'].amount==0)');
- else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==0)');
- }
- else if (thing.type=='res' || thing.type=='building') tokens.push('G.things['+thing.id+'].amount');
- else if (thing.type=='upgrade' || thing.type=='achiev') tokens.push('(G.things['+thing.id+'].owned==1)');
- else {ok=0;tokens.push(tkn);}
- }
- }
- }
- else if (isNumber(tkn)) {tokens.push(parseFloat(tkn)+'');}
- else {ok=0;tokens.push(tkn);}
- }
- if (setKeyword) prevKeyword=setKeyword; else prevKeyword='';
- }
- //console.log(' [ '+tokens.join(' ][ ')+' ]');
- var funcBody=tokens.join(' ');
- funcBody='return parseNum( '+funcBody+' );';
- if (!ok && tokens[tokens.length-1])
- {
- //console.log(' ERROR : '+tokens[tokens.length-1]);
- if (dummyTest) return 'Failed to parse "'+tokens[tokens.length-1]+'"';
- else return false;
- }
- else
- {
- if (dummyTest) funcBody='var V=1; '+funcBody;
- else funcBody='var w=w||0;var v=v||0; '+funcBody;
- //console.log(' '+funcBody);
- var func=0;
- var result=0;
- try
- {
- func=new Function('w','v',funcBody);
- if (dummyTest) result=func();
- if (dummyTest) return 'ok'; else return func;
- //console.log(' Result is '+result);
- } catch (e)
- {
- if (true)//e instanceof SyntaxError)
- {
- if (dummyTest)
- {
- //console.log(funcBody);
- //console.log(e);
- return 'Error when executing function : "'+e.message+'".';
- }
- else return false;
- }
- }
- }
- }
- /*=====================================================================================
- PARSER
- =======================================================================================*/
- G.line=0;
- G.lineNums=[];
- G.foundError=false;
- G.context='';
- G.parseError=function(str)
- {
- var rawStr=str;
- if (!G.foundError)
- {
- G.l.innerHTML=`
- <div id="errorWrap">
- <div id="error">
- The game couldn't function due to the following problems :
- <div id="errors"></div>
- <small>You may want to contact the author of this game.</small>
- </div>
- </div>
- `;
- G.l.classList.add('on');
- }
- var str=str||'';
- str=str.replaceAll('&','&');
- str=str.replaceAll('<','<');
- str=str.replaceAll('>','>');
- str=str.replaceAll('"','"');
- str=str.replaceAll('\'',''');
- var div=document.createElement('div');
- div.className='error';
- div.innerHTML='• ERROR '+(G.context||'(unidentified)')+' :<br>['+str+']';
- l('errors').appendChild(div);
- console.log('ERROR '+(G.context||'(unidentified)')+' : '+rawStr);
- G.foundError=true;
- return false;
- }
- G.shorten=function(str,len)
- {
- if (str.length<len) return str; else return str.substring(0,len)+'…';
- }
- //sanitizing functions
- //regexps are used scarcely because I don't trust myself with those
- G.Kalpha='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
- G.Kalphanum='.1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
- G.Knum='.1234567890';
- G.Kvar='1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZ';
- 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'];
- G.checkId=function(str)
- {
- //raise an error if str is not alphanumeric or collides with a reserved word
- if (G.reservedKeywords.indexOf(str)!=-1) return G.parseError('"'+str+'" is a reserved keyword, pick another one!');
- else if (str.length>20) return G.parseError('"'+str+'" is longer than 20 characters, the maximum allowed size for keys.');
- else
- {
- var fail=false;
- var letterFound=false;
- for (var i=0;i<str.length;i++)
- {
- var it=str.charAt(i);
- if (G.Kalphanum.indexOf(it)==-1) {fail=true;break;}
- if (G.Kalpha.indexOf(it)!=-1) {letterFound=true;}
- }
- if (fail || !letterFound) return G.parseError('"'+str+'" is not a valid key. A key can only contain letters and digits.');
- }
- return true;
- }
- G.validateId=function(ids)
- {
- //perform G.checkId on the declaring token of a new Thing (in the form *key1|key2|key3)
- var ids=ids.substring(1,ids.length).split('|');
- for (var i in ids)
- {
- if (G.thingsByName[ids[i]]) return G.parseError('There is already something with the id "'+ids[i]+'" ('+G.thingsByName[ids[i]].name+').');
- if (!G.checkId(ids[i])) return false;
- }
- return true;
- }
- G.Kclass=/^([a-z_]|-[a-z_-])[a-z\d_-]*$/i;
- G.validateClasses=function(str)
- {
- //raise an error if str isn't composed of alphanumeric words separated by spaces
- var bits=str.split(' ');
- for (var i in bits)
- {
- if (!G.Kclass.test(bits[i])) return G.parseError('"'+bits[i]+'" is not a valid CSS class name.');
- }
- return str;
- }
- G.validateVar=function(str)
- {
- if (str.charAt(0)!='$') return G.parseError('"'+str+'" is not a valid variable name.');
- str=str.substring(1);
- if (str.length<=0) return G.parseError('"'+str+'" is too short for a variable name.');
- var fail=false;
- for (var i=0;i<str.length;i++)
- {
- var it=str.charAt(i);
- if (G.Kvar.indexOf(it)==-1) {fail=true;break;}
- }
- str='$'+str;
- if (fail) return G.parseError('"'+str+'" is not a valid variable name as it is not alphanumeric.');
- return str;
- }
- G.makeSafe=function(str)
- {
- //makes the string okay to use in html without triggering any tags, hopefully
- str=str||'';
- str=str.replaceAll('&','&');
- str=str.replaceAll('<','<');
- str=str.replaceAll('>','>');
- str=str.replaceAll('"','"');
- str=str.replaceAll('\'',''');
- return str;
- }
- G.makeUnsafe=function(str)
- {
- str=str||'';
- str=str.replaceAll('<','<');
- str=str.replaceAll('>','>');
- str=str.replaceAll('"','"');
- str=str.replaceAll(''','\'');
- str=str.replaceAll('&','&');
- return str;
- }
- G.makeHTML=function(str)
- {
- //G.makeSafe, plus additional stuff
- //replace pseudotags
- str=G.makeSafe(str);
- str=str.replace(/<\/>/gi,'<br>');
- str=str.replace(/<\/\/>/gi,'</div><div>');
- str=str.replace(/<\.>/gi,'</div><div>• ');
- str=str.replace(/<t>/gi,'<div class="title">').replace(/<\/t>/gi,'</div>');
- str=str.replace(/<b>/gi,'<b>').replace(/<\/b>/gi,'</b>');
- str=str.replace(/<i>/gi,'<i>').replace(/<\/i>/gi,'</i>');
- str=str.replace(/<u>/gi,'<u>').replace(/<\/u>/gi,'</u>');
- str=str.replace(/<q>/gi,'<q>').replace(/<\/q>/gi,'</q>');
- str=str.replace(/<#([0-9A-F]{3,6})>/gi,'<span style="color:#$1;">').replace(/<\/#>/gi,'</span>');
- return str;
- }
- //grabbers
- G.grabThing=function(str)
- {
- //"bunny" will return the Thing with the key "bunny"
- if (str=='this') return 'this';
- else if (str.indexOf(':')!=-1)
- {
- //return G.thingsByTag[???]
- return G.parseError('Selectors ("'+str+'") do not work for this command.');
- }
- else if (!G.thingsByName[str]) return G.parseError('Nothing found with the key "'+str+'".');
- return G.thingsByName[str];
- }
- G.grabThings=function(str)
- {
- //output is always an array
- //"bunny" will return the Thing with the key "bunny"
- //"Upgrades" will return all Things with type "upgrade"
- //"tag:animal" will return all Things with the tag "animal"
- if (str=='this') return ['this'];
- else if (str.indexOf(':')!=-1)//selector
- {
- var bits=str.split(':');
- var type=0;
- var tag=0;
- var notTag=0;
- var specials=[];
- for (var i=0;i<bits.length;i++)
- {
- var me=bits[i];
- if (me=='tag' && bits[i+1]) {i++;tag=G.makeSafe(bits[i]);}
- else if (me=='notTag' && bits[i+1]) {i++;notTag=G.makeSafe(bits[i]);}
- else if (me=='Buttons') type='button';
- else if (me=='Resources') type='res';
- else if (me=='Buildings') type='building';
- else if (me=='Upgrades') type='upgrade';
- else if (me=='Achievements') type='achiev';
- else if (me=='Items') type='itemType';
- else if (me=='Shinies') type='shiny';
- else if (me=='All') {}
- else if ( me=='owned'
- || me=='notOwned'
- ) specials.push(me);
- else if (me=='') {}
- else return G.parseError('In the selector "'+str+'", failed to understand "'+me+'".');
- }
- var arr=[];
- for (var i in G.things)
- {
- var me=G.things[i];
- if (
- (!type || me.type==type)
- && (!tag || me.tags.includes(tag))
- && (!notTag || !me.tags.includes(notTag))
- ) arr.push(me);
- }
- if (specials.length>0)
- {
- var obj={w:arr};
- for (var i in specials){obj[specials[i]]=1;}
- return obj;
- }
- else return arr;
- }
- else if (!G.thingsByName[str]) return G.parseError('Nothing found with the key "'+str+'".');
- return [G.thingsByName[str]];
- }
- G.getThings=function(w,owner)
- {
- if (w.w)
- {
- var arr=[];
- for (var i in w.w)
- {
- var me=w.w[i];
- if ((!w.owned || (me.amount>0 || me.owned))
- && (!w.notOwned || (me.amount<=0 || !me.owned))
- ) arr.push(me);
- }
- return arr;
- }
- else if (w[0]=='this') return [owner];
- //TODO : items
- else return w;
- }
- G.KisFunc=('+-*/=<>()%!,:').split('');
- G.isFunc=function(str)
- {
- //check if the string contains one of the above characters
- for (var i=0;i<G.KisFunc.length;i++){if (str.indexOf(G.KisFunc[i])!=-1) return true;}
- return false;
- }
- G.grabVar=function(str)
- {
- //"4" will return 4
- //"bunny" will return the Thing with the key "bunny"
- //"(4+bunnies*3)" will return a function that executes that operation
- //"$local" will return the local variable named "local" (or 0 if not found)
- if (str=='this') return 'this';
- if (str.charAt(0)!='(' && G.isFunc(str)) str='('+str+')';
- if (str.charAt(0)=='(' && str.charAt(str.length-1)==')')
- {
- var success=G.parseToFunc(str,1);
- if (success=='ok') return {type:'f',f:G.parseToFunc(str)};
- return G.parseError('Syntax error in "'+str+'" : '+success);
- }
- if (str.charAt(0)=='$') return {type:'l',l:str};
- if (isNumber(str)) return parseFloat(str);
- if (!G.thingsByName[str]) return G.parseError('"'+str+'" is not a valid thing, local variable, number, or expression.');
- return G.thingsByName[str];
- }
- G.getVarValue=function(w,thing)
- {
- if (w=='this' && thing) return thing.amount;
- else if (w.type)
- {
- var amount=0;
- if (w.type=='building' || w.type=='res') amount=w.amount;
- else if (w.type=='upgrade' || w.type=='achiev') amount=(w.owned?1:0);
- if (w.type=='f')
- {
- if (!thing) amount=0;
- else
- {
- if (thing.type=='building' || thing.type=='res') amount=thing.amount;
- else if (thing.type=='upgrade' || thing.type=='achiev') amount=(thing.owned?1:0);
- else if (thing.type=='button' || thing.type=='shiny') amount=thing.clicks;
- }
- return w.f(thing,amount);
- }
- else if (w.type=='l')
- {
- if (!G.localVars[w.l]) return 0;
- else return G.localVars[w.l];
- }
- else return amount;
- return 0;
- }
- else return w;
- }
- G.grabText=function(str)
- {
- var bits=[];
- var bit='';
- //chop into bits
- var inTkn=false;
- for (var i=0;i<str.length;i++)
- {
- var c=str.charAt(i);
- var add=false;
- if (c=='[')
- {
- if (inTkn>0) add=true;
- else
- {
- bits.push({t:G.makeHTML(bit)});
- bit='';
- }
- inTkn++;
- }
- else if (c==']' && inTkn>0)
- {
- inTkn--;
- if (inTkn>0) add=true;
- else
- {
- //bits.push({c:bit});
- if (bit.indexOf('?')==0)//[?(test)|(exp1)|(exp2)]=>if (test) is >0, return (exp1), else return (exp2)
- {
- bit=bit.substring(1).split('|');
- var X=bit[0]||0;
- var Y=bit[1]||'';
- var Z=bit[2]||'';
- bits.push({i:G.grabVar(X),w1:G.grabText(Y),w2:G.grabText(Z)});
- }
- else if (bit.indexOf('s?')==0)//[s?(exp)]=>if (exp) is !=1, return "s"
- {
- bit=bit.substring(2);
- var X=bit||0;
- bits.push({s:G.grabVar(X)});
- }
- else if (bit.indexOf('n:')==0)//[n:thingId]=>returns the name of the given thing
- {
- bit=bit.substring(2);
- var X=bit||0;
- if (X=='this') bits.push({ns:true});
- else
- {
- if (!G.thingsByName[X]) return G.parseError('"'+X+'" is not a valid thing.');
- bits.push({n:G.thingsByName[X]});
- }
- }
- else if (bit.indexOf('p:')==0)//[p:thingId]=>returns the plural of the given thing
- {
- bit=bit.substring(2);
- var X=bit||0;
- if (X=='this') bits.push({ps:true});
- else
- {
- if (!G.thingsByName[X]) return G.parseError('"'+X+'" is not a valid thing.');
- bits.push({p:G.thingsByName[X]});
- }
- }
- else bits.push({w:G.grabVar(bit)});//[(exp)]=>value of exp
- bit='';
- }
- }
- else add=true;
- if (add) bit+=c;
- }
- if (bit!='') bits.push({t:G.makeHTML(bit)});
- //console.log(bits);
- return bits;
- }
- G.getTextValue=function(w,thing,wrapMode)
- {
- var str='';
- for (var i in w)
- {
- var bit=w[i];
- if (bit.t) str+=bit.t;//raw text
- else if (bit.i) str+=G.getVarValue(bit.i,thing)?G.getTextValue(bit.w1,thing,1):G.getTextValue(bit.w2,thing,1);//ternary
- else if (bit.s) str+=(G.getVarValue(bit.s,thing)!=1)?'s':'';//s if !=1
- else if (bit.w) str+=B(G.getVarValue(bit.w,thing));//var
- else if (bit.n) str+=bit.n.name;//name
- else if (bit.p) str+=bit.n.plural;//plural
- else if (bit.ns) str+=thing.name;//name self
- else if (bit.ps) str+=thing.plural;//plural self
- }
- if (wrapMode==1) return str;
- else if (wrapMode==2) return '<div style="display:inline;">'+str+'</div>';
- return '<div>'+str+'</div>';
- }
- G.applyIncludes=function(str)
- {
- var out='';
- var lines=[];
- str=str.replaceAll('[i:','[include ');
- str=STR2(str);
- while (str.val.length>0)
- {
- out+=str.gulpUntilSymbol('[include ',true);
- if (str.val.length>0)
- {
- var bit=str.val;
- var incStr='';
- var inInclude=true;
- var inStr=false;
- var trailSpace=false;
- for (var i=0;i<bit.length;i++)
- {
- var it=bit.charAt(i);
- if (it==']' && !inStr) {inInclude=false;if (bit.charAt(i+1)==' '){trailSpace=true;};break;}
- else if (it=='"' && inInclude) inStr=!inStr;
- incStr+=it;
- }
- var bits=STR2(incStr);
- var inc=G.includes[bits.gulp()];
- var incContents=inc.mono?inc.text:inc.lines.join('///NEWLINE///');
- var args={};
- for (var i in inc.args){args[i]=inc.args[i];}
- while(bits.val.length>0)
- {
- var arg=STR2(bits.gulp());
- if (arg.gulpSymbol('%'))
- {
- var name=arg.gulpUntilSymbol('="',true);
- var def=arg.gulpUntilSymbol('"',true);
- if ((name in args) && def) args[name]=def;
- }
- }
- for (var i in args)
- {
- incContents=incContents.replaceAll('[%'+i+']',args[i]||'???');
- }
- if (!inc.mono) {lines=incContents.split('///NEWLINE///');break;}
- else out+=incContents;
- if (trailSpace) out+=' ';
- str.gulpSymbol(incStr+']');
- }
- }
- if (lines.length>0) return lines; else return [out];
- }
- G.parse=function(toParse)
- {
- var game={
- name:'Untitled',
- author:'Anonymous',
- desc:'',
- created:0,
- updated:0,
- version:0,
- forumPost:0,
- costRate:1.15,
- refundRate:0.5,
- css:'',
- stylesheets:[],
- spritesheets:{},
- noParticles:false,
- noBulkParticles:false,
- };
- G.foundError=false;
- G.context='when initializing';
- G.line=0;
- G.includes=[];
- for (var STEP=0;STEP<2;STEP++)//pre-parse on the first step, then turn into proper lines on the second
- {
- if (STEP==0) G.context='when pre-parsing';
- else G.context='when parsing';
- var lines=toParse.split(String.fromCharCode(10));
- var lines2=[];
- var lineNum=0;
- var lineNums=[];
- var inComment=false;//inside a multiline comment
- for (var i in lines)
- {
- var line=lines[i].trim();
- if (line.length>0)
- {
- if (line.substring(0,2)=='/*') {inComment=true;}//multiline comment start
- if (line.slice(-2)=='*/') {inComment=false;line='';/*line.slice(0,-2);*/}//multiline comment end
- if (inComment || line.substring(0,2)=='//') {}//comment
- else lines2.push(line);lineNums.push(lineNum);
- }
- lineNum++;
- }
- lines=lines2;
- G.lineNums=lineNums;
- if (STEP==1)
- {
- var i=0;
- while(i<lines.length)
- {
- if (i<lines.length-1)
- {
- var next=lines[i+1];
- var out=G.applyIncludes(next);
- lines.splice(i+1,1);
- for (var ii=out.length-1;ii>=0;ii--)
- {
- lines.splice(i+1,0,out[ii]);
- }
- }
- i++;
- }
- }
- //chop into blocks
- var blockNames={
- 'Let\'s make a game!': 'main',
- 'Settings': 'settings',
- 'CSS': 'css',
- 'Includes': 'includes',
- 'Layout': 'layout',
- 'Resources': 'resources',
- 'Buttons': 'buttons',
- 'Buildings': 'buildings',
- 'Upgrades': 'upgrades',
- 'Items': 'items',
- 'Achievements': 'achievs',
- 'Shinies': 'shinies',
- };
- var contentBlocks=['Resources','Buttons','Buildings','Upgrades','Items','Achievements','Shinies'];
- var blocksNamesById={};for (var i in blockNames){blocksNamesById[blockNames[i]]=i;}
- var blocks={};
- var block=0;
- var prevBlock=0;
- for (var i in lines)
- {
- var me=lines[i];
- if (blockNames[me])
- {
- prevBlock=block;
- block=blockNames[me];
- if (!blocks[block]) blocks[block]=[];
- if (contentBlocks.includes(me)) blocks[block].push('new block');
- }
- else if (block) {blocks[block].push(me);}
- }
- if (STEP==0 && blocks['includes'])
- {
- var thing=0;
- var i=0;
- //for (var i in blocks['includes'])
- while(i<blocks['includes'].length)
- {
- var line=STR2(blocks['includes'][i]);
- if (line.gulpSymbol('*include '))
- {
- if (thing!=0) G.includes[thing.name]=thing;
- thing={};
- thing.name=line.gulp();
- thing.args={};
- while(line.val.length>0)
- {
- if (line.gulpSymbol(':'))//mono (only one line)
- {
- thing.mono=true;
- thing.text=line.val;
- G.includes[thing.name]=thing;
- thing=0;
- }
- else
- {
- var arg=STR2(line.gulp());
- if (arg.gulpSymbol('%'))
- {
- var name=arg.gulpUntilSymbol('="',true);
- var def=arg.gulpUntilSymbol('"',true);
- if (def) thing.args[name]=def;
- else thing.args[name]=0;
- }
- }
- }
- if (thing)//still in thing therefore not mono
- {
- thing.lines=[];
- }
- }
- else if (thing)
- {
- thing.lines.push(line.val);
- }
- if (i<blocks['includes'].length-1)
- {
- var next=blocks['includes'][i+1];
- var out=G.applyIncludes(next);
- blocks['includes'].splice(i+1,1);
- for (var ii=out.length-1;ii>=0;ii--)
- {
- blocks['includes'].splice(i+1,0,out[ii]);
- }
- }
- i++;
- if (i>1000) {console.log('TOO MUCH INCLUDING HAPPENING!!! THERE IS A BUG SOMEWHERE PROBABLY AAAAAHHHH');i+=10000;}
- }
- if (thing!=0) G.includes[thing.name]=thing;
- }
- delete blocks['includes'];
- if (STEP==0)
- {
- //default layout
- if (!blocks['layout'] || blocks['layout'][0]=='use default' || blocks['layout'].length==0)
- {
- if (blocks['layout'] && blocks['layout'][0]=='use default') blocks['layout'].splice(0,1);
- blocks['layout']=((
- `*main
- contains:res, buttons
- *res
- contains:Resources
- class:fullWidth
- *buttons
- contains:Buttons
- *store
- contains:buildings, upgrades
- *buildings
- contains:BulkDisplay, Buildings
- header:<t>Buildings</t>
- tooltip origin:left
- *upgrades
- contains:Upgrades
- header:<t>Upgrades</t>
- costs:hide
- names:hide`
- ).split(String.fromCharCode(10))).concat(blocks['layout']);
- }
- //paste back into 1 string
- toParse='';
- for (var i in blocks)
- {
- toParse+=blocksNamesById[i]+String.fromCharCode(10);
- toParse+=blocks[i].join(String.fromCharCode(10))+String.fromCharCode(10);
- }
- }
- }
- if (lines[0]!='Let\'s make a game!') return G.parseError('A valid source file must begin with "Let\'s make a game!"');
- var block='main';
- 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
- var virtualLineNum=0;
- var inEffects=false;//inside a thing's effects: / end effects
- var effectsType=0;//the type of the current effect block; can be "click", "tick", "sell", "c:customEffectName" etc
- var ends=0;//+1 everytime we start a new effect or condition block, -1 if we find "end"
- G.baseThing=0;//this is a pseudo-thing which can be defined in a section, making all subsequent Things use it as a base
- for (var block in blocks)
- {
- thing=0;ok=1;G.baseThing=0;
- for (var i in blocks[block])
- {
- G.line=virtualLineNum;
- var me=blocks[block][i];//lines[i];
- //G.context='on line '+(G.lineNums[G.line]||'?')+', "'+G.shorten(me,30)+'"';
- G.context='on line "'+G.shorten(me,30)+'"';//line numbers are unreliable in the current system
- var ok=0;
- //if (me.substring(0,2)=='/*') {ok=1;inComment=true;}//multiline comment start
- //if (me.slice(-2)=='*/') {ok=1;inComment=false;}//multiline comment end
- //if (ok || inComment || me.substring(0,2)=='//') {ok=1;}//comment
- if (true)
- {
- var prop=me.substring(0,me.indexOf(':')).toLowerCase();
- var val=me.substring(me.indexOf(':')+1);
- //we don't run G.makeSafe on the initial val because some commands, such as expressions, may use different handlers
- if (me=='new block') {G.baseThing=0;ok=1;}
- else if (block=='main')
- {
- ok=1;
- if (prop=='name') game.name=G.makeSafe(val);
- else if (prop=='by') game.author=G.makeSafe(val);
- else if (prop=='desc') game.desc=val;
- else if (prop=='created') game.created=G.makeSafe(val);
- else if (prop=='updated') game.updated=G.makeSafe(val);
- else if (prop=='version') game.version=parseFloat(val);
- else if (prop=='forum post') game.forumPost=parseInt(val);
- else ok=0;
- }
- else if (block=='settings')
- {
- ok=1;
- if (prop=='background')
- {
- game.background=G.makeSafe(val);
- }
- else if (prop=='tiling background')
- {
- game.background=G.makeSafe(val);
- game.backgroundTile=true;
- }
- else if (prop=='spritesheet')
- {
- var sprites=G.makeSafe(val).split(', ');
- game.spritesheets[sprites[0]]={name:sprites[0],w:parseInt(sprites[1].split(' by ')[0]),h:parseInt(sprites[1].split(' by ')[1]),url:sprites[2]};
- }
- else if (prop=='stylesheet')
- {
- game.stylesheets.push(val);
- }
- else if (prop=='bloom')
- {
- G.bloomFilter=Math.min(100,Math.max(0,parseFloat(val)))/200;
- }
- else if (prop=='building cost increase') game.costRate=parseFloat(val.replace('%',''))/100;
- else if (prop=='building cost refund') game.refundRate=parseFloat(val.replace('%',''))/100;
- else if (me=='no particles') game.noParticles=true;
- else if (me=='no bulk particles') game.noBulkParticles=true;
- }
- else if (block=='css')
- {
- ok=1;
- if (me) game.css+=''+/*G.makeSafe*/(me)+String.fromCharCode(10);
- }
- else if (block=='layout' || block=='resources' || block=='buttons' || block=='shinies' || block=='buildings' || block=='upgrades' || block=='items' || block=='achievs')
- {
- //create and edit things
- ok=1;
- var effects=thing.effects;
- if (me.charAt(0)=='*')
- {
- G.createThing(thing);
- if (me=='*TEMPLATE'){}
- else if (!G.validateId(me)) return false;
- var type='';
- if (block=='layout') type='box';
- if (block=='resources') type='res';
- else if (block=='buttons') type='button';
- else if (block=='buildings') type='building';
- else if (block=='upgrades') type='upgrade';
- else if (block=='items') type='itemType';
- else if (block=='achievs') type='achiev';
- else if (block=='shinies') type='shiny';
- thing={type:type};
- thing.ids=me;
- if (thing.type=='box')
- {
- thing.showCosts=true;
- thing.showNames=true;
- thing.showIcons=true;
- thing.showPs=true;
- }
- else
- {
- thing.effects={};
- /*
- .effects is groups of effects by effectType
- ie.:
- .effects={
- 'click':['effect text 1','effect text 2'],
- 'tick':['effect text 1','effect text 2'],
- };
- the effect texts are parsed into actual effects later on
- */
- thing.costs=[];
- thing.reqs=[];
- thing.costRate=game.costRate;
- thing.refundRate=game.refundRate;
- if (thing.type=='shiny')
- {
- thing.freq=0;
- thing.freqV=0;
- thing.dur=10;//default to 10 secs duration
- thing.moves=[];
- }
- }
- }
- //boxes only
- else if (!thing) G.parseError('Trying to edit something, but there is nothing to edit.');
- else if (type=='box')
- {
- if (prop=='contains')
- {
- if (!thing.children){thing.children=[];}
- val=G.makeSafe(val).split(', ');
- for (var ii in val){thing.children.push(val[ii]);}
- }
- else if (prop=='in')
- {
- thing.isIn=G.makeSafe(val);
- }
- else if (prop=='costs') thing.showCosts=(val=='show'?true:false);
- else if (prop=='names') thing.showNames=(val=='show'?true:false);
- else if (prop=='icons') thing.showIcons=(val=='show'?true:false);
- else if (prop=='ps') thing.showPs=(val=='show'?true:false);
- else if (prop=='header') thing.header=val;
- else if (prop=='footer') thing.footer=val;
- else if (prop=='class') thing.classes=G.validateClasses(val);
- else if (prop=='tooltip origin' && (val=='top' || val=='bottom' || val=='left' || val=='right')) thing.tooltipOrigin=G.makeSafe(val);
- else if (prop=='tooltip class') thing.tooltipClasses=G.validateClasses(val);
- else if (me=='no tooltip') thing.noTooltip=true;
- else ok=0;
- }
- //general
- else if (prop=='name') {val=val.split('|');thing.name=G.makeSafe(val[0]);thing.plural=(G.makeSafe(val[1])||val[0]);thing.customName=true;}
- else if (prop=='desc') thing.desc=val;
- else if (prop=='text') thing.text=val;
- else if (prop=='tag' || prop=='tags') thing.tags=G.makeSafe(val);
- else if (prop=='class') thing.classes=G.validateClasses(val);
- else if (prop=='icon class') thing.iconClasses=G.validateClasses(val);
- else if (prop=='icon') thing.icon=G.makeSafe(val);
- else if (prop=='start with' && (thing.type=='res' || thing.type=='building')) thing.startWith=parseFloat(val);
- else if ((me=='start with' || me=='owned') && (thing.type=='upgrade' || thing.type=='achiev')) thing.startWith=true;
- else if (prop=='is always' && (thing.type=='res' || thing.type=='building')) {thing.isAlways=val;}
- else if (me=='can be negative' && (thing.type=='res')) {thing.canBeNegative=true;}
- else if (me=='show max' && (thing.type=='res' || thing.type=='building')) thing.showMax=true;
- else if (me=='show earned' && (thing.type=='res')) thing.showEarned=true;
- else if (me=='show clicks' && thing.type=='button') thing.showClicks=true;
- else if (prop=='cost') thing.costs.push((val));
- else if (prop=='cost increase') thing.costRate=parseFloat(val.replace('%',''))/100;
- else if (prop=='cost refund') thing.refundRate=parseFloat(val.replace('%',''))/100;
- else if (prop=='req') thing.reqs.push((val));
- else if (me=='unlocked') thing.unlocked=true;//(val=='true' || val=='yes');
- else if (me=='no text') thing.noText=true;
- else if (me=='no click') thing.noClick=true;
- else if (me=='shown') thing.show=1;
- else if (me=='hidden') thing.show=0;
- else if (me=='lit') thing.lit=1;
- else if (me=='dim') thing.lit=0;
- else if (me=='always hidden') thing.alwaysHidden=1;
- else if (me=='hidden when 0') thing.hiddenWhen0=true;
- else if (me=='no buy') thing.noBuy=true;
- else if (prop=='limit' && thing.type=='building') thing.limit=val;
- else if (prop=='movement' && thing.type=='shiny') thing.moves=G.makeSafe(val).split(' ');
- else if (prop=='frequency' && thing.type=='shiny') thing.freq=parseFloat(val);
- else if (prop=='frequency variation' && thing.type=='shiny') thing.freqV=parseFloat(val);
- else if (prop=='duration' && thing.type=='shiny') thing.dur=parseFloat(val);
- //effects
- else if (prop=='passive' || prop.indexOf('on ')==0)
- {
- if (inEffects || ends>0) G.parseError('Attempted to start an effect inside another effect.');
- else
- {
- ends++;
- inEffects=true;
- var type='tick';//"passive" just redirects to "on tick"
- if (prop!='passive') type=prop.substring(3);
- /*if ( type=='passive'
- || type=='click'
- || type=='tick'
- || type=='start'
- || type=='save'
- || type=='load'
- || type=='earn'
- || type=='lose'
- ) effectsType=type;
- else G.parseError('The effect type "'+type+'" was not recognized.');*/
- if (G.validateId(type)) effectsType=type; else G.parseError('The effect type "'+type+'" is invalid.');
- if (val && val.length>0)//1-line effect
- {
- if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
- thing.effects[effectsType].push(val);
- ends--;inEffects=false;effectsType=0;
- }
- else
- {
- if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
- }
- }
- }
- else if (me=='end')
- {
- ends--;
- if (ends>0 && effectsType)
- {
- //end to a condition inside an effect block
- thing.effects[effectsType].push(me);
- }
- else if (ends==0 && effectsType)
- {
- //end to an effect block
- //if (!thing.effects[effectsType]) thing.effects[effectsType]=[];
- //thing.effects[effectsType].push(val);
- inEffects=false;effectsType=0;
- }
- else if (ends<0) G.parseError('Tried to end conditions or effects when there were none started.');
- }
- else if (inEffects && effectsType)
- {
- if (me.indexOf('if (')==0)
- {
- if (me.charAt(me.length-1)==')') ends++;//only ends++ for multi-line condition blocks
- }
- thing.effects[effectsType].push(me);
- }
- else if (me.indexOf('if (')==0 || me.indexOf('else if (')==0) G.parseError('Tried to start a condition outside of an effect block.');
- //visual
- else if (prop=='tooltip origin' && (val=='top' || val=='bottom' || val=='left' || val=='right')) thing.tooltipOrigin=G.makeSafe(val);
- else if (prop=='tooltip class') thing.tooltipClasses=G.validateClasses(val);
- else if (me=='no tooltip') thing.noTooltip=true;
- else ok=0;
- }
- }
- if (G.foundError || !ok) return G.parseError('Could not parse "'+G.shorten(me,30)+'".');
- virtualLineNum++;
- }
- G.createThing(thing);
- if (inEffects || ends>0) return G.parseError('An effects block was not closed properly.');
- G.baseThing=0;
- }
- G.spritesheets=game.spritesheets||{};
- //turn text into valid text objects
- if (game.desc) game.desc=G.grabText(game.desc);
- var props=['header','footer'];
- for (var i in G.boxes)
- {
- var me=G.boxes[i];
- G.context='when parsing text for the box "'+G.shorten(me.key,30)+'"';
- for (var ii in props)
- {
- if (me[props[ii]]) me[props[ii]]=G.grabText(me[props[ii]]);
- }
- }
- var props=['desc','text'];
- for (var i in G.things)
- {
- var me=G.things[i];
- G.context='when parsing text for the thing "'+G.shorten(me.name,30)+'"';
- for (var ii in props)
- {
- if (me[props[ii]]) me[props[ii]]=G.grabText(me[props[ii]]);
- }
- }
- //clean icons, costs, reqs and effects
- var gulpCond=function(str)
- {
- //get whatever is within the next "if ()", parse it into a function and return that function
- if (str.gulp('if') && str.gulpSymbol('('))
- {
- var parens=1;
- var cond='';
- //find matching parens
- for (var i=0;i<str.val.length;i++)
- {
- var it=str.val.charAt(i);
- if (it=='(') parens++;
- else if (it==')') parens--;
- if (parens==0) {break;}
- cond+=it+'';
- }
- if (cond!='')
- {
- str.gulpSymbol(cond+')');
- return G.grabVar('('+cond+')');
- }
- }
- return false;
- }
- for (var i in G.things)
- {
- var me=G.things[i];
- if (me.icon)
- {
- G.context='when getting the icon for the thing "'+G.shorten(me.name,30)+'"';
- var icons=[];
- var icon=me.icon.split(' ');
- for (var ii in icon)
- {
- if (icon[ii].charAt(icon[ii].length-1)==']')//tile
- {
- var sheet=G.spritesheets[icon[ii].split('[')[0]];
- var coords=icon[ii].substring(icon[ii].indexOf('[')+1,icon[ii].indexOf(']')).split(',');
- icons.push({tile:true,url:sheet.url,x:-parseInt(coords[0])*sheet.w,y:-parseInt(coords[1])*sheet.h});
- }
- else icons.push({url:icon[ii],x:0,y:0});//plain image
- }
- me.icon=icons;
- }
- if (me.type=='shiny')
- {
- G.context='when preparing the shiny "'+G.shorten(me.name,30)+'"';
- var moves={};
- 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(',');
- var wMoves=('onThing,onBox').split(',');//moves that accept a thing instead of a number
- for (var ii in me.moves)
- {
- var it=me.moves[ii].split(':');
- if (recMoves.indexOf(it[0])==-1) G.parseError('"'+it[0]+'" is not a recognized movement type.');
- else if(wMoves.indexOf(it[0])!=-1 && !G.thingsByName[it[1]]) G.parseError('The movement type "'+me.moves[ii]+'" has an invalid parameter.');
- else if(wMoves.indexOf(it[0])!=-1) moves[it[0]]=G.thingsByName[it[1]];
- else moves[it[0]]=parseFloat(it[1]||0);
- }
- me.moves=moves;
- }
- if (me.noClick)
- {
- if (!me.classes) me.classes='noClick';
- else me.classes+=' noClick';
- }
- G.context='when preparing the thing "'+G.shorten(me.name,30)+'"';
- if (me.isAlways) me.isAlways=G.grabVar('('+me.isAlways+')').f;
- if (me.limit) me.limit=G.grabVar('('+me.limit+')').f;
- var condStack=[];//the first one is the condition effect currently being filled
- //breaking up one-line effects and pre-parsing
- for (var effectType in me.effects)
- {
- G.context='when parsing the effect block "'+G.shorten(effectType,30)+'" for the thing "'+G.shorten(me.name,30)+'"';
- var list=[];
- var effs=me.effects[effectType];
- var elses=0;
- for (var ii in effs)
- {
- var cond=0;
- var str=STR2(effs[ii]);
- if (str.val=='end' && elses>0)
- {
- //double ends if we're closing an else
- for (var iii=0;iii<elses;iii++) list.push({type:'?',eff:'end'});
- elses--;
- }
- if (str.val=='else')
- {
- list.push({type:'else',effs:[]});
- str.val='';
- elses++;
- }
- else
- {
- var isElse=str.gulp('else');
- if (isElse) elses++;
- cond=gulpCond(str);
- if (cond.type && cond.type=='f')
- {
- list.push({type:(isElse?'else':'if'),cond:cond.f,effs:[],or:[]});
- }
- }
- if (str.val.length>0)
- {
- list.push({type:'?',eff:str.val});
- if (cond) list.push({type:'?',eff:'end'});
- }
- if (G.foundError) return false;
- }
- me.effects[effectType]=list;
- }
- for (var effectType in me.effects)
- {
- G.context='when parsing the effect block "'+G.shorten(effectType,30)+'" for the thing "'+G.shorten(me.name,30)+'"';
- var list=[];
- var effs=me.effects[effectType];
- for (var ii in effs)
- {
- var eff=effs[ii];
- if (eff.type=='if' || eff.type=='else')
- {
- if (eff.type=='else' && condStack.length>0) condStack[0].or.push(eff);
- else if (condStack.length>0) condStack[0].effs.push(eff);
- else list.push(eff);
- condStack.unshift(eff);
- }
- if (eff.type=='?')
- {
- var str=STR2(eff.eff);
- delete eff.eff;
- var add=true;
- var cmd=str.val;
- if (str.val=='end')
- {
- add=false;
- if (condStack.length>0) condStack.shift();
- eff.type='end';
- }
- else
- {
- var whatDo=0;
- if (str.gulpSymbol('log('))
- {
- var classes=G.validateClasses(str.gulpUntilSymbol(')'));
- eff={type:'log',w:G.grabText(str.gulpUntil()),classes:classes};
- }
- else if (str.gulp('log')) eff={type:'log',w:G.grabText(str.gulpUntil())};
- else if (str.val==('clear log') || str.val==('clean log')) eff={type:'clear log'};
- else if (str.gulp('toast')) eff={type:'toast',w:G.grabText(str.gulpUntil())};
- else if (str.gulp('echo')) eff={type:'echo',w:G.grabText(str.gulpUntil())};
- else if (str.gulp('eval')) eff={type:'eval',w:str.gulpUntil()};
- else if (str.gulp('do'))
- {eff={type:'do'};whatDo='do X with Ys';}
- else if (str.gulp('anim icon')) eff={type:'animicon',w:G.validateClasses(str.gulpUntil())};
- else if (str.gulp('anim')) eff={type:'anim',w:G.validateClasses(str.gulpUntil())};
- else if (str.gulp('light')) {eff={type:'light'};whatDo='do Xs';}
- else if (str.gulp('dim')) {eff={type:'dim'};whatDo='do Xs';}
- else if (str.gulp('show')) {eff={type:'show'};whatDo='do Xs';}
- else if (str.gulp('hide')) {eff={type:'hide'};whatDo='do Xs';}
- else if (str.gulp('spawn'))
- {eff={type:'spawn'};whatDo='do X';}
- else if (str.gulp('yield') || str.gulp('gain') || str.gulp('win') || str.gulp('get'))
- {eff={type:'yield'};whatDo='do X Ys';}
- else if (str.gulp('lose'))
- {eff={type:'lose'};whatDo='do X Ys';}
- else if (str.gulp('grant'))
- {eff={type:'grant'};whatDo='do X Y';}
- else if (str.gulp('increase cost of'))
- {eff={type:'increase cost'};whatDo='change Xs by Y';}
- else if (str.gulp('lower cost of'))
- {eff={type:'lower cost'};whatDo='change Xs by Y';}
- else if (str.gulp('multiply cost of'))
- {eff={type:'multiply cost'};whatDo='change Xs by Y';}
- else if (str.gulp('multiply refund of'))
- {eff={type:'multiply refund'};whatDo='change Xs by Y';}
- else if (str.gulp('increase yield of') || str.gulp('increase gain of'))
- {eff={type:'increase yield'};whatDo='change Xs by Y';}
- else if (str.gulp('lower yield of') || str.gulp('lower gain of'))
- {eff={type:'lower yield'};whatDo='change Xs by Y';}
- else if (str.gulp('multiply yield of') || str.gulp('multiply gain of'))
- {eff={type:'multiply yield'};whatDo='change Xs by Y';}
- else if (str.gulp('multiply duration of'))
- {eff={type:'multiply duration'};whatDo='change Xs by Y';}
- else if (str.gulp('multiply frequency of'))
- {eff={type:'multiply frequency'};whatDo='change Xs by Y';}
- if (eff.type=='?')
- {
- //this one is a bit annoying
- var tmp=STR2(str.val);
- var type=tmp.gulp();
- if (type=='increase' || type=='lower' || type=='multiply')
- {
- var Z=tmp.gulp();
- if (tmp.gulp('yield of') || tmp.gulp('gain of'))
- {
- eff={type:type+' yield for'};whatDo='change Zs for Xs by Y';
- str.val=Z+' for '+tmp.val;
- }
- }
- }
- if (eff.type=='?')//still nothing?
- {
- //test for "X is Y" or "X=Y"
- str.val=str.val.replace(' = ','=');
- str.val=str.val.replace('=',' = ');
- var bits=str.val.split(' ');
- if (bits[1] && (bits[1]=='is' || bits[1]=='are' || bits[1]=='=') && bits[0].length>0 && bits[2])
- {
- bits[1]='=';
- str.val=bits.join(' ');
- if (str.val.charAt(0)=='$') {eff={type:'set var'};whatDo='$X=Y';}
- else {eff={type:'set'};whatDo='X=Y';}
- }
- }
- if (whatDo=='X=Y')
- {
- //X is a selector, Y is a number or variable
- var bits=str.val.split(' = ');
- var X=bits[0]||0;
- var Y=bits[1]||0;
- if (X) eff.w=G.grabThings(X);
- if (Y) eff.v=G.grabVar(Y);
- }
- else if (whatDo=='$X=Y')
- {
- //X is a local var, Y is a number or variable
- var bits=str.val.split(' = ');
- var X=bits[0]||0;
- var Y=bits[1]||0;
- if (X) eff.w=G.validateVar(X);
- if (Y) eff.v=G.grabVar(Y);
- }
- else if (whatDo=='do X')
- {
- //X is a thing
- var X=str.gulp();
- if (X) eff.w=G.grabThing(X);
- }
- else if (whatDo=='do Xs')
- {
- //X is a selector
- var X=str.gulp();
- if (X) eff.w=G.grabThings(X);
- }
- else if (whatDo=='do X Y')
- {
- //X can be a number or a variable, but Y is always a thing
- //X may be absent
- var Y=str.val.split(' ');Y=Y[Y.length-1];
- var X=str.gulpUntil(Y);
- if (X) eff.v=G.grabVar(X);
- if (Y) eff.w=G.grabThing(Y);
- }
- else if (whatDo=='do X Ys')
- {
- //X can be a number or a variable, but Y is always a selector
- //X may be absent
- var Y=str.val.split(' ');Y=Y[Y.length-1];
- var X=str.gulpUntil(Y);
- if (X) eff.v=G.grabVar(X);
- if (Y) eff.w=G.grabThings(Y);
- }
- else if (whatDo=='change X by Y')
- {
- //X is a thing, Y is a number or variable
- eff.w=G.grabThing(str.gulpUntil('by'));
- eff.v=G.grabVar(str.gulpUntil());
- }
- else if (whatDo=='change Xs by Y')
- {
- //X is a selector, Y is a number or variable
- eff.w=G.grabThings(str.gulpUntil('by'));
- eff.v=G.grabVar(str.gulpUntil());
- }
- else if (whatDo=='change Zs for Xs by Y')
- {
- //X is a selector, Y is a number or variable, Z is a selector
- eff.z=G.grabThings(str.gulpUntil('for'));
- eff.w=G.grabThings(str.gulpUntil('by'));
- eff.v=G.grabVar(str.gulpUntil());
- }
- else if (whatDo=='do X with Ys')
- {
- //X is an effect name, Y is a selector
- if (str.val.indexOf(' with ')==-1)
- {
- eff.v=str.val;
- eff.w=G.grabThings('this');
- }
- else
- {
- eff.v=str.gulpUntil('with');
- eff.w=G.grabThings(str.gulpUntil());
- }
- if (!G.validateId(eff.v)) G.parseError('The effect type "'+eff.v+'" is invalid.');
- //else if (eff.w && !eff.w.effects[eff.v]) G.parseError(eff.w.name+' does not have any effects named "'+eff.v+'".');
- }
- }
- if (eff.type=='?') {add=false;G.parseError('Could not parse the effect "'+G.shorten(cmd,30)+'".');}
- if (add)
- {
- if (condStack.length>0) condStack[0].effs.push(eff);
- else list.push(eff);
- }
- }
- if (G.foundError) return false;
- }
- me.effects[effectType]=list;
- }
- G.context='when parsing costs for the thing "'+G.shorten(me.name,30)+'"';
- var list=[];
- for (var ii in me.costs)
- {
- list.push(G.strToList(me.costs[ii]));
- }
- me.costs=list;
- //construct a func that checks reqs
- G.context='when parsing requirements for the thing "'+G.shorten(me.name,30)+'"';
- if (me.reqs.length>0)
- {
- var str=me.reqs.join(', ');
- str=str.replaceAll(', and ',',');
- str=str.replaceAll(' and ',',');
- str=str.replaceAll(', ',',');
- var list=str.split(',');
- var str='';
- for (var ii in list)
- {
- var bits=list[ii].split(' ');
- if (bits[2] && (bits[2]=='clicks' || bits[2]=='click')) {bits[2]=0;bits[1]+=':clicks';}
- if (bits[2] && (bits[2]=='per') && bits[3] && (bits[3]=='second')) {bits[2]=0;bits[3]=0;bits[1]+=':ps';}
- if (bits[0] && bits[1] && !bits[2] && isNumber(bits[0])) str+=bits[1]+' >= '+bits[0];
- else str+=list[ii];
- str+=' and ';
- }
- str='( '+str+' 1 )';
- me.reqFunc=G.grabVar(str).f;
- if (G.foundError) return false;
- }
- }
- //setup game
- G.context='when initializing data';
- for (var i in G.things)
- {
- var me=G.things[i];
- if (me.type!='box')
- {
- me.boostAdd=0;
- me.boostMult=1;
- me.boostAddFor=[];
- me.boostMultFor=[];
- me.costAdd=0;
- me.costMult=1;
- me.refundMult=1;
- me.costAddFor=[];
- me.costMultFor=[];
- me.refundMultFor=[];
- }
- if (me.type=='button' || me.type=='shiny')
- {
- me.clicks=0;
- }
- if (me.type=='shiny')
- {
- me.timeLeft=-1;
- me.durMult=1;
- me.freqMult=1;
- }
- if (me.type=='res' || me.type=='building')
- {
- me.amount=0;
- if (me.startWith) me.amount=me.startWith;
- me.maxAmount=me.amount;
- }
- if (me.type=='res')
- {
- me.ps=0;
- me.amountD=me.amount;
- me.earned=me.amount;
- }
- if (me.type=='upgrade' || me.type=='achiev')
- {
- me.owned=0;
- if (me.startWith) me.owned=1;
- }
- }
- G.startDate=parseInt(Date.now());
- //parse the load data if any
- var loadedData=G.applyLoad();
- //build layout
- if (game.background) G.l.style.backgroundImage='url('+game.background+')';
- if (game.backgroundTile) G.l.classList.add('tilingBackground'); else G.l.classList.remove('tilingBackground');
- G.thingPlacement={
- 'Resources':'none',
- 'Buttons':'none',
- 'Buildings':'none',
- 'Upgrades':'none',
- 'Achievements':'none',
- 'Items':'none',
- };
- G.specialBoxes={
- 'BulkDisplay':`<div id="bulkDisplay" class="box-bit"><div class="box-bit-content"></div></div>`,
- 'Log':`<div id="log" class="box-bit"><div id="logOuter" class="box-bit-content"><div id="logInner"></div></div></div>`,
- };
- //put boxes inside each other
- for (var i in G.boxes)
- {
- var me=G.boxes[i];
- G.context='when placing the box "'+G.shorten(me.key,30)+'"';
- me.name=me.key;
- if (!me.children) me.children=[];
- if (me.isIn)
- {
- var it=me.isIn;
- if (G.thingsByName[it] && G.thingsByName[it].type=='box')
- {
- if (!G.thingsByName[it].children) G.thingsByName[it].children=[];
- G.thingsByName[it].children.push(me.name);
- }
- else return G.parseError('Tried to put the box "'+me.name+'" in the box "'+it+'", but "'+it+'" is not an existing box.');
- }
- }
- for (var i in G.boxes)
- {
- var me=G.boxes[i];
- G.context='when placing the box "'+G.shorten(me.key,30)+'"';
- for (var ii in me.children)
- {
- var it=me.children[ii];
- //generic Thing keyword
- if (G.thingPlacement[it]) G.thingPlacement[it]=me;
- //special predefined elements
- else if (G.specialBoxes[it]) {me.children[ii]={type:'special',str:G.specialBoxes[it]};}
- //tag
- else if (it.indexOf('tag:')==0){
- var tag=it.slice(4);
- me.children[ii]=tag;
- G.thingPlacement[tag]=me;
- }
- //other box
- else if (G.thingsByName[it] && G.thingsByName[it].type=='box') {G.thingsByName[it].parent=me;me.children[ii]=G.thingsByName[it];}
- 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.');
- }
- }
- //create dom
- G.context='when placing boxes';
- var rootBoxes=[];
- for (var i in G.boxes)
- {
- var me=G.boxes[i];
- if (!me.parent) rootBoxes.push(me);
- }
- var getBoxStr=function(box)
- {
- var str='';
- if (box.type && box.type=='special')
- {
- return box.str;
- }
- else if (box.type && box.type=='box')
- {
- for (var i in box.children){str+=getBoxStr(box.children[i]);}
- var content=str;
- var classes='box';
- if (box.classes) classes+=' '+box.classes;
- str='<div class="'+classes+'" id="box-'+box.name+'">';
- if (box.header) str+='<div class="box-header">'+G.getTextValue(box.header)+'</div>';
- if (box.footer) str+='<div class="box-footer">'+G.getTextValue(box.footer)+'</div>';
- str+=content;
- str+='</div>';
- return str;
- }
- else {return '<div class="box-things" id="box-things-'+box+'"></div>';}
- }
- var str='';
- for (var i in rootBoxes)
- {
- str+=getBoxStr(rootBoxes[i]);
- }
- l('content').innerHTML=str;
- for (var i in G.boxes)
- {
- var me=G.boxes[i];
- me.l=l('box-'+me.name)||0;
- me.childrenl={};
- for (var ii in me.children)
- {
- var child=me.children[ii];
- if (child.type && child.type=='box')
- {}
- else {me.childrenl[child]=l('box-things-'+child)||0;}
- }
- }
- //place things
- G.context='when placing things';
- for (var i in G.things)
- {
- G.things[i].createDom();
- }
- for (var i in G.items)
- {
- G.items[i].createDom();
- }
- G.context='in the final stages of initialization';
- //bulk display
- G.bulkDisplay=l('bulkDisplay')||0;
- if (G.bulkDisplay)
- {
- 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>';}});
- G.bulkDisplay=G.bulkDisplay.getElementsByClassName('box-bit-content')[0];
- }
- //log
- G.initLog();
- //info and settings
- var me=l('meta-button-info');
- 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>';}});
- AddEvent(me,'click',function(){G.setMainPopup('info');});
- var me=l('meta-button-settings');
- 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>';}});
- AddEvent(me,'click',function(){G.setMainPopup('settings');});
- G.darkenL=l('darken');
- AddEvent(G.darkenL,'click',function(){G.setMainPopup();});
- //setTimeout(function(){G.setMainPopup('settings');},100);
- G.mainPopup=0;
- G.mainPopupEl=0;
- G.setMainPopup=function(what)
- {
- if (what && what!=G.mainPopup)
- {
- if (G.mainPopupEl) G.closePopup(G.mainPopupEl);
- G.darkenL.className='on';
- if (what=='settings')
- {
- var text=`
- <div class="headerTitle">Settings</div>
- <div style="padding:4px;overflow-y:auto;">
- <div style="display:flex;justify-content:space-evenly;align-items:center;padding:8px;text-align:center;">
- <div>`+
- G.button({
- text:'Save',
- 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.',
- onclick:function(e){
- triggerAnim(e.target,'glow');
- G.save();
- },
- })
- +`<br>`+
- G.button({
- text:'Load',
- tooltip:'Reload your game.',
- onclick:function(e){
- triggerAnim(e.target,'glow');
- G.load();
- },
- })
- +`</div>
- <div>`+
- G.button({
- text:'Export',
- tooltip:'Export your save data to a file.<br>Use this to backup your save or to share it with other players.',
- onclick:function(e){
- triggerAnim(e.target,'glow');
- /*G.popup(0,{text:`
- <div class="headerTitle">Export save</div>
- <div style="padding:4px;overflow-y:auto;overflow-x:hidden;text-align:center;">
- <div>This is your save code.<br>Copy it and keep it somewhere safe!</div>
- <textarea id="textareaPrompt" style="margin-top:8px;width:100%;height:128px;overflow-x:hidden;
- overflow-y:scroll;" readonly>`+(0)+`</textarea>
- <div class="footerTitle hoverShine closesThePopup">Done</div>
- </div>
- `});*/
- G.fileSave();
- },
- })
- +`<br>`+
- G.button({
- 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);"/>',
- tooltip:'Import save data from a file that was previously exported.',
- onclick:function(e){
- triggerAnim(e.target,'glow');
- /*G.popup(0,{text:`
- <div class="headerTitle">Import save</div>
- <div style="padding:4px;overflow-y:auto;overflow-x:hidden;text-align:center;">
- <div>Please paste in the code that was given to you on save export.</div>
- <textarea id="textareaPrompt" style="margin-top:8px;width:100%;height:128px;overflow-x:hidden;
- overflow-y:scroll;">`+(0)+`</textarea>
- <div class="footerTitle hoverShine closesThePopup">Load</div>
- </div>
- `});*/
- },
- })
- +`</div>
- <div>`+
- G.button({
- text:'Wipe',
- classes:'red',
- tooltip:'Wipe your data for this game.<br>You will lose all your progress.<br>This cannot be undone!',
- onclick:function(e){
- triggerAnim(e.target,'glow');
- G.clear();
- },
- })
- +`</div>
- </div>
- <div class="listing b">Autosave : `+G.makeTick({
- val:function(){return G.getSetting('autosave');},
- on:'On',off:'Off',
- func:function(val){G.setSetting('autosave',val);},
- tooltip:'If this is enabled, the game will auto-save every 30 seconds.',
- })+`</div>
- <div class="listing b">Number display : `+G.makeChoices({
- val:function(){return G.getSetting('numdisp');},
- func:function(val){G.setSetting('numdisp',val);},
- list:
- [
- {text:'Shortest',tooltip:'Numbers will be displayed in the form<br><b>1k, 1T, 1UnD</b>.'},
- {text:'Short',tooltip:'Numbers will be displayed in the form<br><b>1 thousand, 1 trillion, 1 undecillion</b>.'},
- {text:'Full',tooltip:'Numbers will be displayed in the form<br><b>1,000, 1,000,000,000,000, 1e+36</b>.'},
- ],
- })+`</div>
- <div class="listing b">Particles : `+G.makeChoices({
- val:function(){return G.getSetting('particles');},
- func:function(val){G.setSetting('particles',val);},
- list:
- [
- {text:'None',tooltip:'No particles will be displayed.'},
- {text:'Low',tooltip:'Particles are displayed in low-performance mode.'},
- {text:'Auto',tooltip:'Particles are displayed in high-performance mode, but switch to low-performance mode in low fps.'},
- {text:'Full',tooltip:'Particles are displayed in high-performance mode.'},
- ],
- })+`</div>
- <div class="listing b">CSS filters : `+G.makeTick({
- val:function(){return G.getSetting('cssfilts');},
- on:'On',off:'Off',
- func:function(val){G.setSetting('cssfilts',val);},
- tooltip:'CSS filters are visual effects such as blur and shadows which may lower performance in some browsers.',
- })+`</div>
- <div class="listing b">Show fps : `+G.makeTick({
- val:function(){return G.getSetting('showFPS');},
- on:'On',off:'Off',
- func:function(val){G.setSetting('showFPS',val);},
- tooltip:'Display the framerate graph in the bottom-left.',
- })+`</div>
- </div>
- `;
- }
- else if (what=='info')
- {
- var text=`
- <div class="headerTitle">Info</div>
- <div style="padding:4px;overflow-y:auto;">
- <div class="sectionTitle">About</div>
- <div class="listing">You are playing <b>`+G.name+`</b>`+(G.version?' v.'+B(G.version):'')+`, by <b>`+(G.author||'Anonymous')+`</b>.
- `+(G.forumPost?'<div><a href="http://forum.dashnet.org/discussion/'+G.forumPost+'" target="_blank">[View this game\'s forum post]</a></div>':'')+`
- </div>
- `+(G.desc?'<div class="listing desc"><div>'+G.getTextValue(G.desc)+'</div></div>':'')+`
- <div class="listing">Game started <b>`+G.selfUpdatingText(function(){return sayTime(parseInt(Date.now())-G.startDate);})+` ago</b>.</div>
- <div class="sectionTitle">Achievements</div>
- <div class="listing b" style="max-width:640px;margin:auto;">
- `+G.selfUpdatingText(function(){
- var str='';
- var owned=0;
- var total=0;
- for (var i in G.achievs)
- {
- var me=G.achievs[i];
- if (me.owned) {str+=me.getQuickDom();owned++;}
- total++;
- }
- str='<div>Owned : '+B(owned)+'/'+B(total)+'</div><div style="padding:8px;">'+str+'</div>';
- return str;
- },5)+`
- </div>
- </div>
- `;
- //TODO : custom stats
- }
- G.mainPopupEl=G.popup(0,{
- text:text+'<div class="footerTitle hoverShine closesThePopup">Close</div>',
- classes:'mainPopup',
- onClose:function(me){G.mainPopup=0;G.mainPopupEl=0;G.darkenL.className='off';},
- });
- }
- else if (G.mainPopupEl) {G.closePopup(G.mainPopupEl);what=0;}
- G.mainPopup=what;
- G.hideTooltip();
- }
- if (loadedData) G.doEffectsForAll('load');
- else G.doEffectsForAll('start');
- //all done!
- for (var i in game){G[i]=game[i];}
- return true;
- }
- }
- G.Reset();
- }
- console.log("initializer for game made");
- G.turnOn=function()
- {
- G.l.classList.add('on');
- G.domReady=true;
- G.playing=true;
- }
- G.turnOff=function()
- {
- G.l.classList.remove('on');
- G.domReady=false;
- G.playing=false;
- }
- console.log("on and off functions made");
- /*=====================================================================================
- BASIC FLOW
- =======================================================================================*/
- G.Logic=function()
- {
- if (G.playing)
- {
- if (G.T%G.fps==1) G.tick();
- for (var i in G.things)
- {
- G.things[i].logic();
- }
- if (G.itemsToRefresh) G.refreshItems();
- //keys
- if (G.keysD[27])//esc
- {
- if (G.popups.length>0) G.closePopup();
- }
- if (G.keys[17] && G.keysD[83])//ctrl-s
- {
- G.save();
- }
- var oldBulk=G.bulk;
- G.bulk=1;
- if (G.keys[16]) G.bulk=50;//shift
- if (G.keys[17]) G.bulk=-G.bulk;//ctrl
- if (G.bulk!=oldBulk && !G.noBulkParticles) G.particleAt(G.l,0,(G.bulk<0?'Selling '+B(-G.bulk):'Buying '+B(G.bulk)));
- if (G.getSetting('autosave') && (G.T+1)%(G.fps*30)==0) G.save();
- }
- G.shiniesLogic();
- G.particlesLogic();
- G.toastLogic();
- }
- console.log("logic made");
- G.Draw=function()
- {
- if (G.playing)
- {
- for (var i in G.things)
- {
- G.things[i].draw();
- }
- if (G.bulkDisplay) G.bulkDisplay.innerHTML=(G.bulk>0?'Buying':'Selling')+' <b>'+B(Math.abs(G.bulk))+'</b>';
- }
- G.shiniesDraw();
- G.popupDraw();
- G.tooltipDraw();
- }
- console.log("drawer made");
- G._Init();
- }
- }
- }
- }});
- }
- </script>
- </body>
- </html>
Add Comment
Please, Sign In to add comment