littlejsengine 1.4.7 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,10 +12,6 @@
12
12
  * @memberof Medals */
13
13
  const medals = [];
14
14
 
15
- /** Set to stop medals from being unlockable (like if cheats are enabled)
16
- * @memberof Medals */
17
- let medalsPreventUnlock;
18
-
19
15
  // Engine internal variables not exposed to documentation
20
16
  let medalsDisplayQueue = [], medalsSaveName, medalsDisplayTimeLast;
21
17
 
@@ -63,9 +59,8 @@ class Medal
63
59
  this.name = name;
64
60
  this.description = description;
65
61
  this.icon = icon;
66
- this.image = new Image();
67
62
  if (src)
68
- this.image.src = src;
63
+ (this.image = new Image).src = src;
69
64
  }
70
65
 
71
66
  /** Unlocks a medal if not already unlocked */
@@ -78,10 +73,7 @@ class Medal
78
73
  ASSERT(medalsSaveName); // save name must be set
79
74
  localStorage[this.storageKey()] = this.unlocked = 1;
80
75
  medalsDisplayQueue.push(this);
81
-
82
- // save for newgrounds and OS13K
83
76
  newgrounds && newgrounds.unlockMedal(this.id);
84
- localStorage['OS13kTrophy,' + this.icon + ',' + medalsSaveName + ',' + this.name] = this.description;
85
77
  }
86
78
 
87
79
  /** Render a medal
@@ -90,27 +82,26 @@ class Medal
90
82
  render(hidePercent=0)
91
83
  {
92
84
  const context = overlayContext;
93
- const width = min(medalDisplayWidth, mainCanvas.width);
85
+ const width = min(medalDisplaySize.x, mainCanvas.width);
94
86
  const x = overlayCanvas.width - width;
95
- const y = -medalDisplayHeight*hidePercent;
87
+ const y = -medalDisplaySize.y*hidePercent;
96
88
 
97
89
  // draw containing rect and clip to that region
98
90
  context.save();
99
91
  context.beginPath();
100
- context.fillStyle = '#ddd'
101
- context.fill(context.rect(x, y, width, medalDisplayHeight));
102
- context.strokeStyle = '#000';
92
+ context.fillStyle = new Color(.9,.9,.9);
93
+ context.strokeStyle = new Color(0,0,0);
103
94
  context.lineWidth = 3;
95
+ context.fill(context.rect(x, y, width, medalDisplaySize.y));
104
96
  context.stroke();
105
97
  context.clip();
106
98
 
107
99
  // draw the icon and text
108
- this.renderIcon(x+15+medalDisplayIconSize/2, y+medalDisplayHeight/2);
109
- context.textAlign = 'left';
110
- context.font = '38px '+ fontDefault;
111
- context.fillText(this.name, x+medalDisplayIconSize+30, y+28);
112
- context.font = '24px '+ fontDefault;
113
- context.fillText(this.description, x+medalDisplayIconSize+30, y+60);
100
+ this.renderIcon(vec2(x+15+medalDisplayIconSize/2, y+medalDisplaySize.y/2));
101
+ const pos = vec2(x+medalDisplayIconSize+30, y+28);
102
+ drawTextScreen(this.name, pos, 38, new Color(0,0,0), 0, 0, 'left');
103
+ pos.y += 32;
104
+ drawTextScreen(this.description, pos, 24, new Color(0,0,0), 0, 0, 'left');
114
105
  context.restore();
115
106
  }
116
107
 
@@ -119,18 +110,13 @@ class Medal
119
110
  * @param {Number} y - Screen space Y position
120
111
  * @param {Number} [size=medalDisplayIconSize] - Screen space size
121
112
  */
122
- renderIcon(x, y, size=medalDisplayIconSize)
113
+ renderIcon(pos, size=medalDisplayIconSize)
123
114
  {
124
115
  // draw the image or icon
125
- const context = overlayContext;
126
- context.fillStyle = '#000';
127
- context.textAlign = 'center';
128
- context.textBaseline = 'middle';
129
- context.font = size*.7 + 'px '+ fontDefault;
130
- if (this.image.src)
131
- context.drawImage(this.image, x-size/2, y-size/2, size, size);
116
+ if (this.image)
117
+ overlayContext.drawImage(this.image, pos.x-size/2, pos.y-size/2, size, size);
132
118
  else
133
- context.fillText(this.icon, x, y); // show icon if there is no image
119
+ drawTextScreen(this.icon, pos, size*.7, new Color(0,0,0));
134
120
  }
135
121
 
136
122
  // Get local storage key used by the medal
@@ -188,18 +174,20 @@ class Newgrounds
188
174
  constructor(app_id, cipher)
189
175
  {
190
176
  ASSERT(!newgrounds && app_id);
177
+
191
178
  this.app_id = app_id;
192
179
  this.cipher = cipher;
193
180
  this.host = location ? location.hostname : '';
194
-
181
+
195
182
  // create an instance of CryptoJS for encrypted calls
196
- cipher && (this.cryptoJS = CryptoJS());
183
+ if (cipher)
184
+ this.cryptoJS = this.CryptoJS();
197
185
 
198
186
  // get session id from url search params
199
187
  const url = new URL(location.href);
200
- this.session_id = url.searchParams.get('ngio_session_id') || 0;
188
+ this.session_id = url.searchParams.get('ngio_session_id');
201
189
 
202
- if (this.session_id == 0)
190
+ if (!this.session_id)
203
191
  return; // only use newgrounds when logged in
204
192
 
205
193
  // get medals
@@ -212,6 +200,7 @@ class Newgrounds
212
200
  if (medal)
213
201
  {
214
202
  // copy newgrounds medal data
203
+ medal.image = new Image;
215
204
  medal.image.src = newgroundsMedal['icon'];
216
205
  medal.name = newgroundsMedal['name'];
217
206
  medal.description = newgroundsMedal['description'];
@@ -296,10 +285,13 @@ class Newgrounds
296
285
  debugMedals && console.log(xmlHttp.responseText);
297
286
  return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
298
287
  }
299
- }
300
288
 
301
- ///////////////////////////////////////////////////////////////////////////////
302
- // Crypto-JS - https://github.com/brix/crypto-js [The MIT License (MIT)]
303
- // Copyright (c) 2009-2013 Jeff Mott Copyright (c) 2013-2016 Evan Vosberg
289
+ CryptoJS()
290
+ {
291
+ ///////////////////////////////////////////////////////////////////////////////
292
+ // Crypto-JS - https://github.com/brix/crypto-js [The MIT License (MIT)]
293
+ // Copyright (c) 2009-2013 Jeff Mott Copyright (c) 2013-2016 Evan Vosberg
304
294
 
305
- const CryptoJS=()=>eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",...']charCodeAtUinyxpf',"for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],1<<17,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(1<<15),new Uint8Array(51e6),0,0,0,0));
295
+ return eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",...']charCodeAtUinyxpf',"for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],1<<17,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(1<<15),new Uint8Array(51e6),0,0,0,0));
296
+ }
297
+ }
@@ -43,7 +43,7 @@ class EngineObject
43
43
  constructor(pos=vec2(), size=objectDefaultSize, tileIndex=-1, tileSize=tileSizeDefault, angle=0, color, renderOrder=0)
44
44
  {
45
45
  // set passed in params
46
- ASSERT(pos && pos.x != undefined && size.x != undefined); // ensure pos and size are vec2s
46
+ ASSERT(isVector2(pos) && isVector2(size)); // ensure pos and size are vec2s
47
47
 
48
48
  /** @property {Vector2} - World space position of the object */
49
49
  this.pos = pos.copy();
@@ -109,9 +109,9 @@ class EngineObject
109
109
 
110
110
  // apply physics
111
111
  const oldPos = this.pos.copy();
112
- this.pos.x += this.velocity.x = this.damping * this.velocity.x;
113
- this.pos.y += this.velocity.y = this.damping * this.velocity.y + gravity * this.gravityScale;
114
- this.angle += this.angleVelocity *= this.angleDamping;
112
+ this.velocity.y += gravity * this.gravityScale;
113
+ this.pos.x += this.velocity.x *= this.damping;
114
+ this.pos.y += this.velocity.y *= this.damping;
115
115
 
116
116
  // physics sanity checks
117
117
  ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
@@ -133,7 +133,7 @@ class EngineObject
133
133
  if (this.collideSolidObjects)
134
134
  {
135
135
  // check collisions against solid objects
136
- const epsilon = 1e-3; // necessary to push slightly outside of the collision
136
+ const epsilon = .001; // necessary to push slightly outside of the collision
137
137
  for (const o of engineObjectsCollide)
138
138
  {
139
139
  // non solid objects don't collide with eachother
@@ -169,7 +169,7 @@ class EngineObject
169
169
  const isBlockedX = abs(oldPos.y - o.pos.y)*2 < sizeBoth.y;
170
170
  const isBlockedY = abs(oldPos.x - o.pos.x)*2 < sizeBoth.x;
171
171
 
172
- if (smallStepUp || isBlockedY || !isBlockedX) // resolve y collision
172
+ if (smallStepUp | isBlockedY | !isBlockedX) // resolve y collision
173
173
  {
174
174
  // push outside object collision
175
175
  this.pos.y = o.pos.y + (sizeBoth.y/2 + epsilon) * sign(oldPos.y - o.pos.y);
@@ -199,7 +199,7 @@ class EngineObject
199
199
  o.velocity.y = lerp(elasticity, inelastic, elastic1);
200
200
  }
201
201
  }
202
- if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
202
+ if (!smallStepUp & isBlockedX) // resolve x collision
203
203
  {
204
204
  // push outside collision
205
205
  this.pos.x = o.pos.x + (sizeBoth.x/2 + epsilon) * sign(oldPos.x - o.pos.x);
@@ -237,7 +237,7 @@ class EngineObject
237
237
  // test which side we bounced off (or both if a corner)
238
238
  const isBlockedY = tileCollisionTest(new Vector2(oldPos.x, this.pos.y), this.size, this);
239
239
  const isBlockedX = tileCollisionTest(new Vector2(this.pos.x, oldPos.y), this.size, this);
240
- if (isBlockedY || !isBlockedX)
240
+ if (isBlockedY | !isBlockedX)
241
241
  {
242
242
  // set if landed on ground
243
243
  this.groundObject = wasMovingDown;
@@ -155,7 +155,7 @@ class ParticleEmitter extends EngineObject
155
155
  this.parent && super.update();
156
156
 
157
157
  // update emitter
158
- if (!this.emitTime || this.getAliveTime() <= this.emitTime)
158
+ if (!this.emitTime | this.getAliveTime() <= this.emitTime)
159
159
  {
160
160
  // emit particles
161
161
  if (this.emitRate * particleEmitRateScale)
@@ -179,7 +179,7 @@ class ParticleEmitter extends EngineObject
179
179
  let pos = this.emitSize.x != undefined ? // check if vec2 was used for size
180
180
  (new Vector2(rand(-.5,.5), rand(-.5,.5)))
181
181
  .multiply(this.emitSize).rotate(this.angle) // box emitter
182
- : randInCircle(this.emitSize * .5); // circle emitter
182
+ : randInCircle(this.emitSize/2); // circle emitter
183
183
  let angle = rand(this.particleConeAngle, -this.particleConeAngle);
184
184
  if (!this.localSpace)
185
185
  {
@@ -222,7 +222,7 @@ class ParticleEmitter extends EngineObject
222
222
  particle.additive = this.additive;
223
223
  particle.renderOrder = this.renderOrder;
224
224
  particle.trailScale = this.trailScale;
225
- particle.mirror = rand()<.5;
225
+ particle.mirror = randInt(2);
226
226
  particle.localSpaceEmitter = this.localSpace && this;
227
227
 
228
228
  // setup callbacks for particles
@@ -251,7 +251,8 @@ class Particle extends EngineObject
251
251
  * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
252
252
  * @param {Number} [angle=0] - Angle to rotate the particle
253
253
  */
254
- constructor(pos, tileIndex, tileSize, angle) { super(pos, new Vector2, tileIndex, tileSize, angle); }
254
+ constructor(pos, tileIndex, tileSize, angle)
255
+ { super(pos, new Vector2, tileIndex, tileSize, angle); }
255
256
 
256
257
  /** Render the particle, automatically called each frame, sorted by renderOrder */
257
258
  render()
@@ -5,6 +5,20 @@
5
5
 
6
6
  'use strict';
7
7
 
8
+ ///////////////////////////////////////////////////////////////////////////////
9
+ // Camera settings
10
+
11
+ /** Position of camera in world space
12
+ * @type {Vector2}
13
+ * @default
14
+ * @memberof Settings */
15
+ let cameraPos = vec2();
16
+
17
+ /** Scale of camera in world space
18
+ * @default
19
+ * @memberof Settings */
20
+ let cameraScale = 32;
21
+
8
22
  ///////////////////////////////////////////////////////////////////////////////
9
23
  // Display settings
10
24
 
@@ -31,6 +45,19 @@ let cavasPixelated = 1;
31
45
  * @memberof Settings */
32
46
  let fontDefault = 'arial';
33
47
 
48
+ ///////////////////////////////////////////////////////////////////////////////
49
+ // WebGL settings
50
+
51
+ /** Enable webgl rendering, webgl can be disabled and removed from build (with some features disabled)
52
+ * @default
53
+ * @memberof Settings */
54
+ let glEnable = 1;
55
+
56
+ /** Fixes slow rendering in some browsers by not compositing the WebGL canvas
57
+ * @default
58
+ * @memberof Settings */
59
+ let glOverlay = 1;
60
+
34
61
  ///////////////////////////////////////////////////////////////////////////////
35
62
  // Tile sheet settings
36
63
 
@@ -99,33 +126,6 @@ let gravity = 0;
99
126
  * @memberof Settings */
100
127
  let particleEmitRateScale = 1;
101
128
 
102
- ///////////////////////////////////////////////////////////////////////////////
103
- // Camera settings
104
-
105
- /** Position of camera in world space
106
- * @type {Vector2}
107
- * @default
108
- * @memberof Settings */
109
- let cameraPos = vec2();
110
-
111
- /** Scale of camera in world space
112
- * @default
113
- * @memberof Settings */
114
- let cameraScale = max(tileSizeDefault.x, tileSizeDefault.y);
115
-
116
- ///////////////////////////////////////////////////////////////////////////////
117
- // WebGL settings
118
-
119
- /** Enable webgl rendering, webgl can be disabled and removed from build (with some features disabled)
120
- * @default
121
- * @memberof Settings */
122
- let glEnable = 1;
123
-
124
- /** Fixes slow rendering in some browsers by not compositing the WebGL canvas
125
- * @default
126
- * @memberof Settings */
127
- let glOverlay = 1;
128
-
129
129
  ///////////////////////////////////////////////////////////////////////////////
130
130
  // Input settings
131
131
 
@@ -174,20 +174,20 @@ let vibrateEnable = 1;
174
174
  ///////////////////////////////////////////////////////////////////////////////
175
175
  // Audio settings
176
176
 
177
- /** Volume scale to apply to all sound, music and speech
177
+ /** All audio code can be disabled and removed from build
178
178
  * @default
179
179
  * @memberof Settings */
180
- let soundVolume = .5;
180
+ let soundEnable = 1;
181
181
 
182
- /** All audio code can be disabled and removed from build
182
+ /** Volume scale to apply to all sound, music and speech
183
183
  * @default
184
184
  * @memberof Settings */
185
- let soundEnable = 1;
185
+ let soundVolume = .5;
186
186
 
187
187
  /** Default range where sound no longer plays
188
188
  * @default
189
189
  * @memberof Settings */
190
- let soundDefaultRange = 30;
190
+ let soundDefaultRange = 40;
191
191
 
192
192
  /** Default range percent to start tapering off sound (0-1)
193
193
  * @default
@@ -207,17 +207,17 @@ let medalDisplayTime = 5;
207
207
  * @memberof Settings */
208
208
  let medalDisplaySlideTime = .5;
209
209
 
210
- /** Width of medal display
210
+ /** Size of medal display
211
211
  * @default
212
212
  * @memberof Settings */
213
- let medalDisplayWidth = 640;
213
+ let medalDisplaySize = vec2(640, 80);
214
214
 
215
- /** Height of medal display
215
+ /** Size of icon in medal display
216
216
  * @default
217
217
  * @memberof Settings */
218
- let medalDisplayHeight = 80;
218
+ let medalDisplayIconSize = 50;
219
219
 
220
- /** Size of icon in medal display
220
+ /** Set to stop medals from being unlockable (like if cheats are enabled)
221
221
  * @default
222
222
  * @memberof Settings */
223
- let medalDisplayIconSize = 50;
223
+ let medalsPreventUnlock;
@@ -77,14 +77,11 @@ function tileCollisionRaycast(posStart, posEnd, object)
77
77
  {
78
78
  // test if a ray collides with tiles from start to end
79
79
  // todo: a way to get the exact hit point, it must still register as inside the hit tile
80
- posStart = posStart.floor();
81
- posEnd = posEnd.floor();
82
- const posDelta = posEnd.subtract(posStart);
80
+ const posDelta = (posEnd = posEnd.floor()).subtract(posStart = posStart.floor());
83
81
  const dx = abs(posDelta.x), dy = -abs(posDelta.y);
84
82
  const sx = sign(posDelta.x), sy = sign(posDelta.y);
85
- let e = dx + dy;
86
83
 
87
- for (let x = posStart.x, y = posStart.y;;)
84
+ for (let x = posStart.x, y = posStart.y, e = dx + dy;;)
88
85
  {
89
86
  const tileData = getTileCollisionData(vec2(x,y));
90
87
  if (tileData && (object ? object.collideWithTileRaycast(tileData, new Vector2(x, y)) : tileData > 0))
@@ -176,7 +173,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
176
173
  // init tile data
177
174
  this.data = [];
178
175
  for (let j = this.size.area(); j--;)
179
- this.data.push(new TileLayerData());
176
+ this.data.push(new TileLayerData);
180
177
  }
181
178
 
182
179
  /** Set data at a given position in the array
@@ -232,21 +229,23 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
232
229
  * @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
233
230
  redrawStart(clear = 0)
234
231
  {
235
- if (clear)
236
- {
237
- // clear and set size
238
- this.canvas.width = this.size.x * this.tileSize.x;
239
- this.canvas.height = this.size.y * this.tileSize.y;
240
- }
241
-
242
232
  // save current render settings
243
- this.savedRenderSettings = [mainCanvas, mainContext, cameraPos, cameraScale];
233
+ this.savedRenderSettings = [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale];
244
234
 
245
- // use normal rendering system to render the tiles
235
+ // hack: use normal rendering system to render the tiles
246
236
  mainCanvas = this.canvas;
247
237
  mainContext = this.context;
248
238
  cameraPos = this.size.scale(.5);
249
239
  cameraScale = this.tileSize.x;
240
+
241
+ if (clear)
242
+ {
243
+ // clear and set size
244
+ mainCanvas.width = this.size.x * this.tileSize.x;
245
+ mainCanvas.height = this.size.y * this.tileSize.y;
246
+ }
247
+
248
+ // begin a new render for the tile canvas
250
249
  enginePreRender();
251
250
  }
252
251
 
@@ -258,7 +257,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
258
257
  //debugSaveCanvas(this.canvas);
259
258
 
260
259
  // set stuff back to normal
261
- [mainCanvas, mainContext, cameraPos, cameraScale] = this.savedRenderSettings;
260
+ [mainCanvas, mainContext, mainCanvasSize, cameraPos, cameraScale] = this.savedRenderSettings;
262
261
  }
263
262
 
264
263
  /** Draw the tile at a given position
@@ -104,7 +104,7 @@ const wave = (frequency=1, amplitude=1, t=time)=> amplitude/2 * (1 - Math.cos(t*
104
104
  * @param {Number} t - time in seconds
105
105
  * @return {String}
106
106
  * @memberof Utilities */
107
- const formatTime = (t)=> (t/60|0)+':'+(t%60<10?'0':'')+(t%60|0);
107
+ const formatTime = (t)=> (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0);
108
108
 
109
109
  ///////////////////////////////////////////////////////////////////////////////
110
110
 
@@ -128,7 +128,7 @@ const randInt = (a=1, b=0)=> rand(a,b)|0;
128
128
  /** Randomly returns either -1 or 1
129
129
  * @return {Number}
130
130
  * @memberof Random */
131
- const randSign = ()=> (rand(2)|0) * 2 - 1;
131
+ const randSign = ()=> randInt(2) * 2 - 1;
132
132
 
133
133
  /** Returns a random Vector2 within a circular shape
134
134
  * @param {Number} [radius=1]
@@ -152,10 +152,15 @@ const randVector = (length=1)=> new Vector2().setAngle(rand(2*PI), length);
152
152
  const randColor = (cA = new Color, cB = new Color(0,0,0,1), linear)=>
153
153
  linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a));
154
154
 
155
- /** The seed used by the randSeeded function, should not be 0
155
+ /** Seed used by the randSeeded function
156
156
  * @memberof Random */
157
157
  let randSeed = 1;
158
158
 
159
+ /** Set seed used by the randSeeded function, should not be 0
160
+ * @param {Number} seed
161
+ * @memberof Random */
162
+ const setRandSeed = (seed)=> randSeed = seed;
163
+
159
164
  /** Returns a seeded random value between the two values passed in using randSeed
160
165
  * @param {Number} [valueA=1]
161
166
  * @param {Number} [valueB=0]
@@ -183,6 +188,14 @@ const randSeeded = (a=1, b=0)=>
183
188
  */
184
189
  const vec2 = (x=0, y)=> x.x == undefined ? new Vector2(x, y == undefined? x : y) : new Vector2(x.x, x.y);
185
190
 
191
+ /**
192
+ * Check if object is a valid Vector2
193
+ * @param {Vector2} vector
194
+ * @return {Boolean}
195
+ * @memberof Utilities
196
+ */
197
+ const isVector2 = (v)=> !isNaN(v.x) && !isNaN(v.y);
198
+
186
199
  /**
187
200
  * 2D Vector object with vector math library
188
201
  * <br> - Functions do not change this so they can be chained together
@@ -212,27 +225,27 @@ class Vector2
212
225
  /** Returns a copy of this vector plus the vector passed in
213
226
  * @param {Vector2} vector
214
227
  * @return {Vector2} */
215
- add(v) { ASSERT(v.x!=undefined); return new Vector2(this.x + v.x, this.y + v.y); }
228
+ add(v) { ASSERT(isVector2(v)); return new Vector2(this.x + v.x, this.y + v.y); }
216
229
 
217
230
  /** Returns a copy of this vector minus the vector passed in
218
231
  * @param {Vector2} vector
219
232
  * @return {Vector2} */
220
- subtract(v) { ASSERT(v.x!=undefined); return new Vector2(this.x - v.x, this.y - v.y); }
233
+ subtract(v) { ASSERT(isVector2(v)); return new Vector2(this.x - v.x, this.y - v.y); }
221
234
 
222
235
  /** Returns a copy of this vector times the vector passed in
223
236
  * @param {Vector2} vector
224
237
  * @return {Vector2} */
225
- multiply(v) { ASSERT(v.x!=undefined); return new Vector2(this.x * v.x, this.y * v.y); }
238
+ multiply(v) { ASSERT(isVector2(v)); return new Vector2(this.x * v.x, this.y * v.y); }
226
239
 
227
240
  /** Returns a copy of this vector divided by the vector passed in
228
241
  * @param {Vector2} vector
229
242
  * @return {Vector2} */
230
- divide(v) { ASSERT(v.x!=undefined); return new Vector2(this.x / v.x, this.y / v.y); }
243
+ divide(v) { ASSERT(isVector2(v)); return new Vector2(this.x / v.x, this.y / v.y); }
231
244
 
232
245
  /** Returns a copy of this vector scaled by the vector passed in
233
246
  * @param {Number} scale
234
247
  * @return {Vector2} */
235
- scale(s) { ASSERT(s.x==undefined); return new Vector2(this.x * s, this.y * s); }
248
+ scale(s) { ASSERT(!isVector2(s)); return new Vector2(this.x * s, this.y * s); }
236
249
 
237
250
  /** Returns the length of this vector
238
251
  * @return {Number} */
@@ -265,12 +278,12 @@ class Vector2
265
278
  /** Returns the dot product of this and the vector passed in
266
279
  * @param {Vector2} vector
267
280
  * @return {Number} */
268
- dot(v) { ASSERT(v.x!=undefined); return this.x*v.x + this.y*v.y; }
281
+ dot(v) { ASSERT(isVector2(v)); return this.x*v.x + this.y*v.y; }
269
282
 
270
283
  /** Returns the cross product of this and the vector passed in
271
284
  * @param {Vector2} vector
272
285
  * @return {Number} */
273
- cross(v) { ASSERT(v.x!=undefined); return this.x*v.y - this.y*v.x; }
286
+ cross(v) { ASSERT(isVector2(v)); return this.x*v.y - this.y*v.x; }
274
287
 
275
288
  /** Returns the angle of this vector, up is angle 0
276
289
  * @return {Number} */
@@ -306,7 +319,7 @@ class Vector2
306
319
  * @param {Vector2} vector
307
320
  * @param {Number} percent
308
321
  * @return {Vector2} */
309
- lerp(v, p) { ASSERT(v.x!=undefined); return this.add(v.subtract(this).scale(clamp(p))); }
322
+ lerp(v, p) { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(p))); }
310
323
 
311
324
  /** Returns true if this vector is within the bounds of an array size passed in
312
325
  * @param {Vector2} arraySize
@@ -317,7 +330,7 @@ class Vector2
317
330
  * @param {float} digits - precision to display
318
331
  * @return {String} */
319
332
  toString(digits=3)
320
- { return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
333
+ { if (debug) { return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }}
321
334
  }
322
335
 
323
336
  ///////////////////////////////////////////////////////////////////////////////
@@ -555,7 +568,7 @@ class Timer
555
568
 
556
569
  /** Returns this timer expressed as a string
557
570
  * @return {String} */
558
- toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); } }
571
+ toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }}
559
572
 
560
573
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
561
574
  * @return {Number} */