├── .gitignore ├── LICENSE ├── README.md ├── TODO ├── docs ├── 081821.html ├── 081821b.html ├── 082421.html ├── 082721.html ├── 082721b.html ├── 082821.html ├── 082921.html ├── 090121.html ├── 090321.html ├── 090421.html ├── 090421b.html ├── 090521.html ├── 091021.html └── index.html ├── ect ├── package-lock.json ├── package.json ├── roadrolled.js ├── scripts ├── htmlmin.json ├── rollup-plugin-dataurl.js ├── rollup-plugin-roadroller.js ├── rollup.config.js ├── tinify-pngs.js └── zipSize.js └── src ├── img ├── db-analysis.png ├── palette-bigger.webp └── palette.webp ├── index.html └── js ├── Drone.js ├── artifact.js ├── baby.js ├── core ├── RetroBuffer.js ├── Stats.js └── utils.js ├── fuel.js ├── game.js ├── harvester.js ├── musicplayer.js ├── planet.js ├── player.js ├── sector.js ├── sounds ├── absorbray.js ├── babyaction.js ├── babyaction2.js ├── boom1.js ├── bump.js ├── cellComplete.js ├── dronemoan.js ├── harvestermoan.js ├── jet.js ├── sectorget.js ├── song.js └── tada.js └── splode.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | keys.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jerome Lecomte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## JS13K 2021 Untitled Space Game 2 | 3 | 4 | Built using @herebefrogs gamejam boilerplate for build automation. 5 | 6 | https://github.com/herebefrogs/gamejam-boilerplate 7 | 8 | 9 | Getting Started 10 | --------------- 11 | 12 | ``` 13 | npm start 14 | -> build game, open a browser, watch source and livereload browser on changes 15 | 16 | npm run build 17 | -> build game for gamejam submission (no sourcemap and livereload script) 18 | 19 | ``` 20 | 21 | 22 | Special Thanks 23 | -------------- 24 | - Eoin McGrath for his original build script 25 | - [Peters](https://twitter.com/p1100i) and [flo-](https://twitter.com/fl0ptimus_prime) for their pixel font from Glitch Hunter 26 | - [Ryan Malm](https://twitter.com/ryanmalm) for sharing his Twitter message code 27 | - [Maxime Euziere](https://twitter.com/MaximeEuziere) for his switch/case approach to handling game screens in update/render/input handlers 28 | - Florent Cailhol for suggesting Terser in place of UglifyJS 29 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rybar/JS13k2021/5fe724259797370818f6822befe85b06d0ba731c/TODO -------------------------------------------------------------------------------- /docs/082721.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/082721b.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/082821.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/082921.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/090121.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/090321.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/090421.html: -------------------------------------------------------------------------------- 1 |
.js> -------------------------------------------------------------------------------- /docs/090421b.html: -------------------------------------------------------------------------------- 1 |
.js> -------------------------------------------------------------------------------- /docs/090521.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/091021.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /ect: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rybar/JS13k2021/5fe724259797370818f6822befe85b06d0ba731c/ect -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gamejam-boilerplate", 3 | "version": "1.1.0", 4 | "description": "Boilerplate for Game Jams like JS13KGames", 5 | "main": "src/js/game.js", 6 | "scripts": { 7 | "clean": "rm -rf dist && mkdir dist", 8 | "build": "run-s clean build:*", 9 | "build:js": "rollup -c scripts/rollup.config.js --environment MINIFY", 10 | "build:roadroller": "roadroller dist/game-rollup.js -o dist/game.js", 11 | "build:html": "grep -v body src/index.html | html-inline -b dist | html-minifier -c scripts/htmlmin.json -o dist/index.html", 12 | "build:zip": "zip -FS -qjX9 dist/game.zip dist/index.html && advzip -z -4 -i 400 dist/game.zip", 13 | "build:zipSize": "node scripts/zipSize.js", 14 | "start": "npm-run-all -s clean -p dev:*", 15 | "dev:js": "rollup -c scripts/rollup.config.js -w --environment DEBUG", 16 | "dev:serve_watch_html_js": "browser-sync 'dist' 'src' --watch --host 0.0.0.0 --https", 17 | "dev:watch_img": "chokidar src/img -d 0 -c 'npm run dev:js'", 18 | "tinify-pngs": "node scripts/tinify-pngs.js" 19 | }, 20 | "license": "MIT", 21 | "devDependencies": { 22 | "browser-sync": "^2.26.7", 23 | "chokidar-cli": "^2.0.0", 24 | "html-inline": "^1.2.0", 25 | "html-minifier": "^4.0.0", 26 | "npm-run-all": "^4.1.5", 27 | "roadroller": "^2.0.0", 28 | "rollup": "^2.23.0", 29 | "rollup-plugin-terser": "^6.1.0", 30 | "terser": "^4.8.0", 31 | "tinify": "^1.6.0-beta.2" 32 | }, 33 | "dependencies": { 34 | "npm": "^6.14.7", 35 | "stats": "^1.0.0", 36 | "stats.js": "^0.17.0" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "git+https://github.com/herebefrogs/gamejam-boilerplate.git" 41 | }, 42 | "author": "Jerome Lecomte ", 43 | "bugs": { 44 | "url": "https://github.com/herebefrogs/gamejam-boilerplate/issues" 45 | }, 46 | "homepage": "https://github.com/herebefrogs/gamejam-boilerplate#readme" 47 | } 48 | -------------------------------------------------------------------------------- /roadrolled.js: -------------------------------------------------------------------------------- 1 | A='XhM}Y}^dgRe@{E}m{vtghySVAsEegEtx^YHD[b_~rqF`IuMMvT~FZm_MY}xWADzg@HURH?{US^wH[u}iapcyziyskyMIsTxI`qFeGZd]fVE]VjfqT_dQPXSHIha]pgUoGItp|x[[PCPlELvrbiXaojYR^gon~vWGj@wbCDddNd]p~qaYM|d]keGWWIIrHRaH]{tdyXZ|@l`IQX`TzfyLc_X{snJFLNSESdmwijzSH|ewZcqBsEJfevbt@[~Kpho|zhnrtpMT|yqT[jYhU^oDOfKu^@_K^X?QKNiGBXv@`oJ}jOMdpKTbH?]z_D?~d@{nlQeS}GYqKYhrfXdAxoCA`XUSjnfegLIns{sMRNR@hDgjR}rsecaxEx?C~JICT@{Q?NlZZ{{X~@qxyShmkNgFNP|WLuz{nhgZTlwWk@uscqChLz^a{UTQed|vroS_^OffBJG]bMC]|QmuCFxjdZrF]DRs^~xFUnb]c@~H~FEiTomWhkJW[nmopSVktWDGWpTFKajrzTwNjYkJKeQzVNUkX}ych{[XlUyz_KOQj|hRr]edpD]Hm`USZB}EEpvZX`Sm`SiLNIR_tKo?FahkcVDNU]cwAy]vmSXsJha_a`{G}JRT@XCeYLuTuprrq?rNHadL`A~@q~RDV|Vm^Go^ImcM[xv@qchpxyWaqMxt?ZG}Hb]pIitNKyxlmaJnXwkoEMepfEDNPDwTbZqbYAKBrLe@mS~gIyLcSzjbG^PKaYSMTKncSVIK~@B`}izoNFr|Ylz[C~cP[~EO@sUSrdXAmGhY^SK_gVC{BRpnV|tGMTV@iFvdKbiQ~NzX[}no~MiKbswakkj}D`TTnX?IdMMyl?fDWx|J{QUkthLFz@zQsUBQN[XseWyhB{jameDR}NqexjcY_}E}dqVFg[qxYZtWTArpEMgdNq`xgn_^Nzb]vCVogRZ}ueNN|iu`vyyUXGdmartm|ipsTPH~P`IkVIHhOKGydvy`DmYryZYAgh_p?un`U`Ps}c{[JDsh?akEiemAd[aseqjq?{}LHkWYUwHpjW^Q?{pVJiP_p@XE^_ViDo~mM`Yq_fu@qDMfR?IoUHGVJI|jU_S_K_ShfHft`FDqgkVM[mAC}hjCQktxRe~RbNIJIpJXU~WAeuKJmGG^snSvRQpLrESINI]`xMzR{Y}xSj`IKH]hsYGWoq|XnEMa~icZVpxRUlqJWAwopokNCOrR_dxc{oVlf^_{Rm[QuG]H[uXmxP~wjoaK}]OxkbSp]@s?EsNEu}BqO{TnyGE_cGYkYO|}^`s}^jl|q|?vpn@SDmgpdrqaeboMEjsvsSsvyU^Cq@MjhpZfayRiY^SOq{p`TwWfmKnkniyDhGVe}GT~HrzaqfAmPwTyK]dF??nbQ}QvlE^_@kIBaZIan`qSC]uvKFfolTR`D[RbHSv|YnhR]{~r}IHpAQVvUO`ke^A^OyC@^cJyHkqRyMdfvpKOfVS@VhRxb|~xelyPTo}wnYiQjZY|gvpwxBj~J[RfPhyMwZ|AWTo^kIbtqg|EEIOztvlSUr[`Nxmh_|}TmCtYaatgesGbs~vb@NzgoOs|NPLAqDk?@rzfifyvJ]OtkYep{`outQ{xG?YHGBh^Mb_Th?FPK{MMhj?PgnNieGWe~gvYlm}xqyYlhy}_UBDssxvOJ[mh`k^JfG[HOKlclNxgH[MMBqgvzQMKFjM{xJ{fbr[k[FHnIWXlKoVZyjy[fmMA?z{zyfSzzmdsLhbGUTvvYiAyaE]mwFWB[Qh@b?B][dB}EoXFN~Vd^CSiiBvQquuQc|KxzBi{F@]ozGLtNPyHdFaptv?^QdEub}jPbQbJmfngo|of__vKBV`eZHayIrFB}ZsnZWeAMGIcbA[TgrSU?F?cWGAlLuSiRxvsVhg~FIr~d`AkIyVEBNmNZkgD{iFrzyH^r}{Gz][OYlqYdDBFNN}ADjXQ^frWNVywoNI`~JJZl?E|hF[hrG}Z}l|C_dvN{SWOBQZ~WFXdeGxb@LTedV|KAxMg^rZkknYKiPT]?wI}K_A^afzXRibwTLkY{msW~pnYfFKDxSxoGGyaQErow|@ITDzhhpByJJYPO@nRNMHLDRYn^fL~^LQvXkBPFnpTHL|y[XWgtGiUKUcmwz_N?z{t{HcvEhhlB@JLW`SUfYiReAqwm}@TYd_R[zXc~iLc_WyuxzD~e][zidHOkuGjJgphF}|DMiSvc@t@i?afRuGwtItiG`@Bq@MjLnB[Bq|rZgyrkaRDgissqEXsW@GJ~jks_IRjjnbpuIehXR]fVAYLLTYzT@T}VP[CY_IVxAUWHKvjCF|cGkcrNXihORRKhSo~|n_KpgdVNfGYeSumUEyVzSvt}kTrz^vsBcZTUlD^S_T^gjyERb~YM~^RZ[v|xAyXDOLmgAyqAiPK|bePRpeK|vlMrhbxb?xJV@aG[hrkU{^[p{FksCrlxHZ^rK[hdpDd|sVhMWftO{XJSlt^AvfukpH[wUl?]GqHtKkWOXjsdXDMFTi{L}p}CpURBz[bExTe}XF|KqRNqUh}Msg||[t^GRwlACo~^~oB?mVv{x{gEgqkoPzPg}jUA{nsEI^|By]~F^kckDaE~AxHQMNPkH^nQkVfLNZBAuzWa]IYeAeJ^wClKk@Bar^rKoA|OHn{sLFHEs?Vgr_IUvfMK^AXF?LV}?Y[JrhwyIsqBEq|OEpo?IrkVCy~eqMn}oNuoWKh^?}UrvV^SToCQcTEs_q^WSiYSaS[WJ{Z|nF?gSr}B]Bv^iTbqbp]OBpIxgQ}veUp^gXWtUN[KNGzy}yDKzwShx]ApHXz]QhxijTkv`uMEmHxmrPU}XpOrIF[JXtpxYlkD@X|BlYYlhLC?YeAINfHT^cKQEXJU{EmplgtYwxUd|JejzDBcl|v]vIcoJZRcfbrG@_xNUfoTzHIJNeUAtdxfK]lgZmLejZpjGPfLyvXIKEdeA~tAQeSQspXndqLxq`vf~AK}AHshSJMNooSmrfnAexZf?NTLlEmMOssV{@gXk}^NSToqYM[V|@sV[]mRxTOP`U@L]DxPwVewlye^sB@irD@AyYdbtcV|[]y^WfvE_lWjdiuoBlBdK@kZCujcAQVFhm_ofAgTj{x{zcifKXOCwrmF@FjkNrWzthoM~]JXQ^plyAc]StKvo?wv[h?GSvhtuph^jB}Ts}{?uovwmvfD`[yDicx`@Od~D^tEu{Siqv`WcFcE_w{ZOMlhOsCgIHYqrutDCrDfnb}tWzjh[plgrYLN?~h{zW{Exk`kCQuEE]XcC[N`sOoU}eWYt`}iFt[rP}R_CqU_ZIZ|fTLVEqyToLN~OEPUVI~~nStcjhBHKmFp^MgL@R]rPZWLAMcShDEmzf]l^wPwJNGiZv]BdUmQ~J@imdXeMJd]?ZVcBG|uc[TVP^xFnoI_A@pQ}vd[o@hD{@ECDmtqRxEsgFAo}JYNhyWyIXWrgpg?R}pBQd?`@LbUQFVlu{}m`oioGtCymoQZ}Lv{lHNJeBpTbvEnZOvGdp]WFY_k@wQ~RX@?nOzH}}KHRk|OSIMSpQJErklzhStHxcnLCCfxz[RngCxSLEVZi^nC|srn^{EuR_~I|nJVHKVzixDx_AW@^sI~kUY`hbUW?o~uilpFTUZnrqrw_]lAxUhftiI}eSvS|CTP]|RUqeDNQZkkMhjpYZMWkPJyhNYNN{J[L?JDhzcUzsoIFxkmsSyZhBtwwmYJAt[UL[o[NKQLPs~mtt_@PIvZWR[i^Ntc|tsPiSIoxEX}[iHtg}JXFKzhfdW}SnE_jONm[?xAsmxDv^yEiMg_TFtYVuo`]SSzFyRkvzBE~dfUDiiYkHamA~JzXyeUo^aiqYnx^QtZYZsk]K^pJoQPDlYWsbok`R[hB`ld[b|C]TUlxZHgksyipXcFNrkzRevIUKNbBd{u@fFFfZ`srZTGYlqKVQTk|HwuMsICpOg{wORMgQJS[_}FOnON?mGBz}VEB}f}_VJFa~QDGthZLLUmOJ]paCMtvojkNCA{puOY~@EV~iY[tB|XjzJwZ|WKbGfly[[@KXGdIGLYZ?bU?ywixkpNUUS]vEmBWvristh}]@[mjJZ{laRiJsq_xSuNf@eAgs`kznR}K`ilJEY_jWNJRyqD~uQQP]inGYZOuWuoZR{UJjIAkaQkHmwet]^`_?zDIz]DTrD`]EsUooR_g`ELiQZYLKm[d|krXzEQzkmJUsvUZNLWOzhPzcLKTrjhll|ByNLqhn_JEB{mNsVsPjCuBvcAla}|M@jRJ{jzzo^OclbdKmwjVSi@iGYeT^rH|YauQcYgovFXQc]G{BdqhE[FwKptf|Uda?xC|BnUiF}@e{HSxhMqoDmC^rHrh}IiFsQkVOXZgqBTyJQXgvDL[hxViZK^ati{MiMMDa~I]]myJbU_ecIgPFpTZjH^alHVjIxvPByTox{KAr[EI{DpqS[yIokobOKN{PRDWnJMq?HFgDu]{ligjA][?doXNDU]]BpMVHq{MkrqKn`pmjxHlZJc@vxTFRA_zJHYrRnirTufLiU[P]DnIGDsL^]{dpwPSrgdDmSsEZtoULaxm_LVmZIExfJux~MCNr?pl@PU^zvRwsOi?hoiDRLDf~SRh^Iu@ai[d~wXz}e}[JqrRiEhykb{yGlR[dYs~v~@yqhBdoELM}}AbJ}aKZYoIjiQZQLtyeKGRFY_DejmLgHGGICrYRxLYk^DiaunlnFqfuun[qKaBfQqZL^s?^{nHFLm[DEATgLo{|k}H|FPxu[X^jgHcCaQwK|AYqPCPJq^Fp~gb?iRzX??zEVi`f{q|d{~_vyYlTGf`GcbJibwuudKPE`mydCzbD]P^]hqxhcuiE[lev{ypTj?^cfOmJPstzOWI~xxfw_SwyWWUQsQDqSApusSa|`Iny|LtqVnYIpIoow]MAcHRuFS{|IDsma|j?HUaB`CM]sPyJounsmUC|JrH^cnqK@K_~BYXgUM_Eoe]WMaG@vi?cu}OBvjEFEOuQSP^~Q_fTcqBcBH~}||E|iNTj}_jouxLsIKvLJiIHbeRU}KufkucungBuVAe?ezb?y`DApvsOoP^beaiOpZRerEcmTYt|o^t|JnJGWSqzCEf[Xre^S{eC_OfYQeqnRBxGEpyjbUfCFFnax[xHueJPtD_b@LGd[koDK{cknPF`B{^xrDobT~XOlivtHjOG{{sDfDvwxAPAcM`]b|jU?cBMZXPXf[^BfeUPO@@p?YiwaKJgBvM??auOesEGHSYK{@paeyHJp_cUkVS?|s|iEWcRvTIVjRmE}lNJQ~F}hEqd@Wf[_e^}WXGCtuIpoO}FUHl@~X{dJApG`vBVA|gCqet_eCxGdnCMpaNbjANF[IEHjsppiSYk}`QEqbVVHZra|A^?bvLLZNq^ntRTCYcjXOCQUsHGPh[aUYK~uAi|D^DHSQji{aHOyxb|KH|U_Gxa~}gNsPUM{AAeA{i{izQVW^VeC^xiRMPujLRG@|ZnvRtVv{h~QQ_GjFYS|x_}aPqQ_Ql`TryChNtCq~PnSRXTnYiRl~QpO|DbLrxaQlt[mje`JBEL`ZWhb`Ekm}{}LtGxJ]oCA`ZQJAkLIluhZ_Sn}?bfavHy^@YkHczQatVORyu`Y{fPs}GjHKsPic|LM{EzSHi}~]oPXMX}[OPgdgLkL@HnMxvckoX^lVStUKDSviYwW]hs`XTV`}hw?h`TUGYFZhhCFPWndxb?VE^tSVbsNWjExK~hcVVfjvTRuDNrYaTyKdA|FlWuQjQts_]gREwcw{YEfAjizvm}OENdvDXx{F`iYmavvKRKJ^wbhwo~ObwroIWrbqFg`oY[_^LCEyVQqujmFvYiJfHwzznzdX_dMg[DHm}mTrDjcgnOxpzuBixdXGTrWF[|ccnId?RtoiJ@]`OqPzd}EOVvwbWABKuZrj{knvaiXKJRkTv]?lywxpZQDkoRdJABjMuQGebHUqawolIltGn~nJRwtFFrDug{[mARGMHJCtuf}gJ}]OSQCnBLCk_QoSUb~yotCuoMEBUE`tcAZHsSin`agqmNWk{WunzMuxoiSkYyTAgRt]_VtMadyW}g_Ua{{J{VUWPVsQoHccjPvp]YVGgcKj_wdXdMA~vDrVlItzfbzbeQl[iUdI{Zsyvcgrb}qW~IFJo|~xfTnIGhR`YQb`mHv[PbmDobC~^qkEAZHz[`]kRJdcqwdQNI{FrFlUWSGZL`hMsD][`}AhjPgdW^sGKeWShCalgozrLkGl]eBJ_}uIN[K|uaa{BfpzaVytZCsLy}z~QTXSXpP]kdNuT^_U{GyPhx|vozh}cSc|~{EU^?LJLCx|@XxQ@Qa_Bvn~^Cz`nzfYZUJzOUOhCHODsGoiqtrk]nsekNas{hdKbW[BbCkUahlMm_RGnGDOweh?l?ZbEZOeRQGK[{c}bmf~s?upAlZzjuOCEDXl@^Gk`G|k~jrSZ`hMjTi|JstW]vVeOzI{~w`MiqsrGSjpnSNlDpe?Y|EHp^ody]aqk|IrvJ`{dzL?qVKLJxEK{KA?ffs~lRZ|MK[~wKY{cN@QiCSnja?SLqu@|MzXUUh?|^PBCBkA_JeNQUdkT]kCrNfxLkK`amf_U@NbBlREpNgsQRVdm|y_]Vtt^Dker_[uFZeBcguuEuspzrrWh^b_oMzBBtzuYE[B]|LYBoAgh||`wOppRo?pCT@lQ_^[UEjtmJpiGZ|FcKObgo?vqr]ddGfyZXI@kV|}^dKc}~qN~BMCHswRpXrSaZUU{]aMGp?UcyPZAmJya@OgG_^zuQ@Tz[wstcHhOJEhbM[jYhCnA_xd]tFuSkm]Gcabfodh[PbinlFuENxRr`V|BHrjVHxl{teRO{plouqt~IxzVSQN{D_xYA_ZAMc?{XL_VGIfnRtmyN[@@gPOp]ePj{}MFaD`K^faeWNZYm}`^sBBwJg{TddBwJPutwVf}gO?Z^`lfi}F]Bab_[xFLNrnHxd^TDS`]CV?]|p?ZoCG}mmrmGsRo{FlgG]CeI?AC@{Jv@rnrkc^n_PoC[Q]`epJk`F[zbmyShQFyVReuQlS~Dr]|^UfBueH_}lutJ`mynkWHKaE{T`elCxyn_P{WtRImmgwCprqPxVd?ts_a@O_BQ[BpEbEjaimj^R}~IoWrsViZG|MoNUGELSPuCx]JsqbWJr[yxD@PySegycqCFUy~A?s~wpupfN@_}[vopMf}wKNZOHr`EnRluWcHOOiiAPFr|fE}aurflAXU[N|mb}kEfkBI{ctpGEAebiq_eYzqvY_fXK}cSnYzqDG?HhM[pst?l}OjZw~wxJBmLp[ED_XbZ]M}rk?BwNoww@qHv|amFvNWrBEphqFYLrY]wvCDWbPgsUo`VaEJt^HxPwQeAdDhImE]z|bJPYCmQeFi?einWmzsNzFPVmSXddly?`Bf~_NGX__qrwxKdNSLmuKAg_o@jfpvUWlrkT[Eb@YrZOC^pNDzHH_LmqhYoorUCexjakOC?CCkA|~NuGNVDM{XiD`^r^jVVrarF}LPLTJCySe|{ytiURkdb{BFHm{uZWUhC~g]umrHuEiRtyuwm@wJgVOojjtNLM}klpTw?`jZIHdjC}EsjvrAeUmcr`Wc^LZf]sMI`ytT{o^K[WQxfnOTTLGNVJERNvB|AZ?LJbDoobPMAnkOeZKxOAOyDdgjtfr[X[?]nArasDpHN]DbmDplAR}ZGthN_PBSSQ[^urVEsR[gbVeP{FNhdILe?tqtICW}FjmzmPPF|WosN`wAQDNOP{nsnQyAy?Sc?nSKMwJtXOyhquoHm@QfLxuZaDQZQtSKiEZMZ}fdh@F^zGfD}ZNmSs`ZumHrCw?zPClAhhdFqt@vEdBR]RN}]RXL{QnjkDOMC@CCZKT^^HYZvqZfSOtMxBVTRWMGAMKAwfqWcvvcDi`iXrCHn}D]EdChNO}?SU~vQVLdF_dk[|wmPrRBP?vhNz{KK~cXEmbWzV_rYQdaSjrJZGdpAVQ{UlPAXqHiYrTmOb}cTlZVJdroqmTZHv^mtjsFpvBe[mqObPkvY@nRY}L@FBTDreQSaYZrXf^doTW}JtLIfvM^h~ozu}uEcS`uACX~aIuiW|~DtHaxLipk_eJQHn]E?XdTc]PbhmbdiaYRT}N{H@KXGAVsvozaxStnh_Eh_zrzTjJBNo@`~kizVuUZeHqcY_zg_vqptIk{TlNtGo?UHXTAUNZv`bliFAmScYc^CeUkZpwDTneUsAWH~OQr~S~QDfDCgIvvPTdb@x@U|EAKorI|VAbqNeJGTyGTZIP?E}_iq{PEF@B?LF?ThVDe[nPyeHx}SFqe}DSJrseBvsKgkusAAHX]S{rQA]]DjF~UudMEzI_Gm?FXUIZYyoYfpCOZKOn~K`uodtcBlIj^BzUODG{r^ksR`iIChgrptN~YZTHcOacWR}HEo?{{}TidtDSFWJ[O@IELd?naCAkb`M?FWBBalpD}l_srVP_SCJf}Em[MT' 2 | t=48044915;M=1<<17;w=[0,0,0,0,0,0,0,0,0,0,0,0];p=new Uint16Array(12<<22).fill(M/4);c=new Uint8Array(12<<22).fill(1);for(o=[r=l=f=0];l<17159;o[l++]=a-=128,f=f?a-f&&f:(a==34|a==96)&&a)for(a=1;a<128;u.map((C,i)=>(y=p[C]+=(e*M/2-p[C]<<14)/(c[C]+=2*(c[C]<10))>>13,w[i]+=x[i]*(e-m/M))),a=a*2+e)for(u='010202103203210431053105410642065206541'.split(m=0).map((C,i)=>(y=0,[...C].map((C,i)=>(y=y*997+(o[l-C]|0)|0)),M*32-1&y*997+a+!!f*129)*12+i),x=u.map((C,i)=>(y=p[C]*2+1,y=Math.log(y/(M-y)),m-=w[i]*y,y/500)),m=~-M/(1+Math.exp(m))|1,e=t%M>17)-!e*m;t ({ 13 | name: 'rollup-plugin-dataurl', 14 | 15 | transform: (source, id) => { 16 | let transformedCode = source; 17 | 18 | // find all DATAURL placeholders, capture the filepaths 19 | const matches = [...source.matchAll(/ (.*) = 'DATAURL:(.*)'/g)]; 20 | 21 | matches.forEach(([, variable, imageFilePath]) => { 22 | console.log('found ', variable, imageFilePath); 23 | // read the image binary content 24 | const data = readFileSync(`./${imageFilePath}`); 25 | // replace the placeholder by a base64 encoded dataurl of the image 26 | transformedCode = transformedCode.replace( 27 | ` ${variable} = 'DATAURL:${imageFilePath}'`, 28 | ` ${variable} = 'data:image/webp;base64,${data.toString('base64')}'` 29 | ); 30 | }); 31 | 32 | console.log('dataurl plugin done with', id); 33 | return { 34 | code: transformedCode, 35 | map: { mappings: ''} 36 | }; 37 | } 38 | }); 39 | 40 | export default { 41 | dataurl 42 | }; 43 | -------------------------------------------------------------------------------- /scripts/rollup-plugin-roadroller.js: -------------------------------------------------------------------------------- 1 | //const inputs = [ 2 | // { 3 | // data: 'console.log("Hello, world!");', 4 | // type: 'js', 5 | // action: 'eval', 6 | // }, 7 | //]; 8 | 9 | //const options = { 10 | // maxMemoryMB: 150, 11 | //}; 12 | 13 | //const packer = new Packer(inputs, options); 14 | 15 | // this typically takes about a minute or two, can be omitted if you want. 16 | //await packer.optimize(); 17 | 18 | //const { firstLine, secondLine } = packer.makeDecoder(); 19 | //console.log(firstLine + '\n' + secondLine); 20 | 21 | import { Packer } from 'roadroller' 22 | 23 | 24 | export const roadrolled = () => ({ 25 | name: 'rollup-plugin-roadroller', 26 | 27 | transform: (source, id) => { 28 | let transformedCode = source; 29 | const inputs = [ 30 | { 31 | data: transformedCode, 32 | type: 'js', 33 | action: 'eval', 34 | }, 35 | ]; 36 | 37 | const options = { 38 | maxMemoryMB: 150, 39 | }; 40 | 41 | const packer = new Packer(inputs, options); 42 | const { firstLine, secondLine } = packer.makeDecoder(); 43 | transformedCode = firstLine + '\n' + secondLine; 44 | 45 | console.log(id, ' has been successfully roadrolled'); 46 | 47 | return { 48 | code: transformedCode, 49 | map: { mappings: ''} 50 | }; 51 | 52 | 53 | 54 | } 55 | }); 56 | 57 | export default { 58 | roadrolled 59 | }; 60 | -------------------------------------------------------------------------------- /scripts/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { terser } from 'rollup-plugin-terser'; 2 | import { dataurl } from './rollup-plugin-dataurl'; 3 | import { roadrolled } from './rollup-plugin-roadroller'; 4 | 5 | const { DEBUG = false, MINIFY = false } = process.env 6 | 7 | export default { 8 | // bundle all imported modules together, discarding unused code 9 | input: './src/js/game.js', 10 | onwarn(warning, warn) { 11 | // suppress eval warnings 12 | if (warning.code === 'EVAL') return 13 | if (warning.code === 'SOURCEMAP_ERROR') return 14 | warn(warning) 15 | }, 16 | output: { 17 | file: './dist/game-rollup.js', 18 | // wrap global variables/functions into in IIFE so terser will rename them 19 | format: 'iife', 20 | freeze: DEBUG, 21 | indent: DEBUG ? ' ' : false, 22 | preferConst: true, 23 | // generate sourcemaps (development mode only) 24 | //sourcemap: DEBUG, 25 | // allow the use of onresize=onrotate=... and other space saver hacks 26 | strict: false, 27 | useStrict: false, 28 | }, 29 | plugins: [ 30 | // embed images into source files as data URI 31 | dataurl(), 32 | // TODO shouldn't I always run terser to debug the game I use? 33 | MINIFY && 34 | terser({ 35 | ecma: 2016, 36 | module: true, 37 | toplevel: true, 38 | compress: { 39 | keep_fargs: false, 40 | passes: 4, 41 | pure_funcs: ['assert', 'debug'], 42 | pure_getters: true, 43 | unsafe: true, 44 | unsafe_arrows: true, 45 | unsafe_comps: true, 46 | unsafe_math: true, 47 | unsafe_methods: true, 48 | }, 49 | mangle: true 50 | // TODO sourceMap??? 51 | }), 52 | ], 53 | } -------------------------------------------------------------------------------- /scripts/tinify-pngs.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const { existsSync, readFileSync, statSync } = require('fs'); 3 | const tinify = require('tinify'); 4 | 5 | // retreive TinyPNG API key 6 | if (!existsSync('./keys.json')) { 7 | return; 8 | } 9 | 10 | const keys = JSON.parse(readFileSync('./keys.json').toString()); 11 | if (!keys.TINIFY_API_KEY) { 12 | return; 13 | } 14 | 15 | tinify.key = keys.TINIFY_API_KEY; 16 | 17 | // filter PNGs from staged files being committed 18 | const pngs = execSync('git diff --cached --name-only | grep "png$" | cut -d ":" -f 2').toString(); 19 | 20 | pngs 21 | .split('\n') 22 | .map(png => png.trim()) 23 | .filter(png => png !== '') 24 | .forEach(async (png) => { 25 | const original = statSync(png).size; 26 | 27 | // TinyPNG 28 | const image = tinify.fromFile(png); 29 | await image.toFile(png); 30 | const optimized = statSync(png).size; 31 | 32 | // AdvPNG 33 | execSync(`advpng -l -4 ${png}`); 34 | const recompressed = statSync(png).size 35 | 36 | console.log(`${png} ${original} bytes -> TinyPNG ${optimized} bytes -> AdvPNG ${recompressed}`); 37 | 38 | // stage optimized PNG for inclusion in commit 39 | execSync(`git add ${png}`); 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /scripts/zipSize.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | // report zip size and remaining bytes 4 | const size = fs.statSync('dist/game.zip').size; 5 | const limit = 1024 * 13; 6 | const remaining = limit - size; 7 | const percentage = Math.round((remaining / limit) * 100 * 100) / 100; 8 | console.log('\n-------------'); 9 | console.log(`USED: ${size} BYTES`); 10 | console.log(`REMAINING: ${remaining} BYTES (${percentage}% of 13k budget)`); 11 | console.log('-------------\n'); 12 | 13 | -------------------------------------------------------------------------------- /src/img/db-analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rybar/JS13k2021/5fe724259797370818f6822befe85b06d0ba731c/src/img/db-analysis.png -------------------------------------------------------------------------------- /src/img/palette-bigger.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rybar/JS13k2021/5fe724259797370818f6822befe85b06d0ba731c/src/img/palette-bigger.webp -------------------------------------------------------------------------------- /src/img/palette.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rybar/JS13k2021/5fe724259797370818f6822befe85b06d0ba731c/src/img/palette.webp -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 13 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/js/Drone.js: -------------------------------------------------------------------------------- 1 | import { inView, playSound, lerp } from './core/utils.js'; 2 | import Fuel from './fuel.js'; 3 | import Splode from './splode.js'; 4 | 5 | function Drone(angle, planet){ 6 | 7 | 8 | this.x = 0; 9 | this.y = 0; 10 | this.targetX = 0; 11 | this.targetY = 0; 12 | this.radius = 8; 13 | this.hit = false; 14 | this.hitCount = 0; 15 | this.alive = true 16 | this.health = 600; 17 | this.reaching = false; 18 | this.angle = angle; 19 | this.planet = planet; 20 | this.harvesters = 3; 21 | this.apex = {x:0, y:0} 22 | this.browL = {x:0, y:0} 23 | this.browR = {x:0, y:0} 24 | this.armSpeedFactor = 60; 25 | this.armSpeedTarget = 0; 26 | this.underAttack = false; 27 | 28 | 29 | return this; 30 | } 31 | 32 | Drone.prototype.draw = function(){ 33 | 34 | if(inView(this, 10)){ 35 | r.pat = r.dither[0] 36 | 37 | for(let a = 0; a < 2 * Math.PI; a+= 0.7){ 38 | for(let rad = this.radius; rad < this.radius + 30; rad += 4){ 39 | 40 | let v = a + .4 * Math.sin(a*8-rad/20+(t/this.armSpeedTarget) ); 41 | r.fillCircle(( this.x-view.x+rad*Math.cos(v)), this.y-view.y+rad*Math.sin(v), ( (this.radius/3)-rad/11)|0, 3 ); 42 | r.circle(( this.x-view.x+rad*Math.cos(v)), this.y-view.y+rad*Math.sin(v), ( (this.radius/3)-rad/11)|0, 4 ); 43 | 44 | } 45 | } 46 | 47 | r.fillCircle(this.x-view.x, this.y-view.y, this.radius, 3); 48 | r.fillCircle(this.x-view.x, this.y-view.y, 15, 22); 49 | r.fillCircle(this.x-view.x+Math.cos(this.angle+Math.PI)*7, this.y-view.y+Math.sin(this.angle+Math.PI)*7, 3, 1); 50 | //r.fillTriangle(this.browL, this.browR, this.apex, 3); 51 | r.circle(this.x-view.x, this.y-view.y, this.radius, 4); 52 | 53 | 54 | if(this.reaching){ 55 | this.armSpeedTarget=10; 56 | let i = 10; 57 | while(i--){ 58 | r.pat = r.dither[i]; 59 | r.line(this.x - view.x + (Math.random()-0.5) * this.radius*2, 60 | this.y - view.y + (Math.random()-0.5) * this.radius*2, 61 | p.x - view.x, 62 | p.y - view.y, 63 | 3+Math.random()*5); 64 | } 65 | }else{this.armSpeedTarget=60;} 66 | } 67 | 68 | } 69 | Drone.prototype.update = function(){ 70 | 71 | 72 | 73 | if(inView(this, -20)){ 74 | p.enemiesInView.push(this); 75 | } 76 | if(inView(this, 60)){ 77 | this.targetX = p.x; 78 | this.targetY = p.y; 79 | this.x = lerp(this.x, this.targetX, 0.005); 80 | this.y = lerp(this.y, this.targetY, 0.005); 81 | this.armSpeedFactor = lerp(this.armSpeedFactor, this.armSpeedTarget, 0.2); 82 | 83 | this.apex.x = this.x - view.x; 84 | this.apex.y = this.y - view.y - 3; 85 | 86 | this.browL.x = this.x - view.x - 24; 87 | this.browL.y = this.y - view.y - 18; 88 | 89 | this.browR.x = this.x - view.x + 24; 90 | this.browR.y = this.y - view.y - 18; 91 | 92 | 93 | let distx = this.x - p.x; 94 | let disty = this.y - p.y; 95 | 96 | this.angle = Math.atan2(disty, distx); 97 | 98 | let dist = Math.sqrt(distx*distx + disty*disty); 99 | 100 | if( dist <= this.radius + p.radius + 100){ 101 | p.fuel -= 0.6; 102 | this.reaching = true; 103 | p.draining = true; 104 | harverterSuckSound.volume.gain.value = 0.1; 105 | if(dist <= this.radius + p.radius){ 106 | p.fuel -= 150; 107 | if(Math.abs(p.xVel + p.yVel)/2 < 2){ 108 | if(p.fuel > 100){ 109 | 110 | p.score += 1; 111 | this.hit = true; 112 | this.hitCount += 1; 113 | splodes.push(new Splode(this.x, this.y, 40, 5)); 114 | playSound(sounds.boom1, 3, 0, 0.05, false); 115 | 116 | p.xVel = Math.cos(p.angle) * 3; 117 | p.yVel = Math.sin(p.angle) * 3; 118 | }else { 119 | splodes.push(new Splode(this.x, this.y, 20, 22)); 120 | p.xVel = Math.cos(p.angle) * 5; 121 | p.yVel = Math.sin(p.angle) * 5; 122 | playSound(sounds.bump, 1, 0, 0.4, false); 123 | } 124 | 125 | 126 | } 127 | 128 | 129 | 130 | } 131 | }else {this.reaching = false; p.draining = false;} 132 | if(this.health <= 0){ 133 | this.alive = false; 134 | p.draining = false; 135 | drones.splice(drones.indexOf(this), 1); 136 | splodes.push(new Splode(this.x, this.y, 50, 6)); 137 | splodes.push(new Splode(this.x+Math.random()*5, this.y+Math.random()*5, 60, 7)); 138 | splodes.push(new Splode(this.x+Math.random()*5, this.y+Math.random()*5, 70, 5)); 139 | for(let i = 0; i < 5; i++){ 140 | let f = new Fuel(this.x, this.y, 2); 141 | f.targetX = this.x + (Math.random()-0.5)*150; 142 | f.targetY = this.y + (Math.random()-0.5)*150; 143 | Fuelrocks.push(f); 144 | } 145 | playSound(sounds.boom1, 1, 0, 0.1, false); 146 | 147 | } 148 | 149 | 150 | 151 | 152 | } 153 | // if(this.radius <= 0){ 154 | // this.alive = false; 155 | // } 156 | 157 | 158 | } 159 | 160 | export default Drone; -------------------------------------------------------------------------------- /src/js/artifact.js: -------------------------------------------------------------------------------- 1 | import { inView } from './core/utils.js'; 2 | import Splode from './splode.js'; 3 | 4 | function Artifact(x,y, radius, color){ 5 | this.x = x; 6 | this.y = y; 7 | this.radius = radius; 8 | this.alive = true; 9 | 10 | this.color = color; 11 | this.drawStack = []; 12 | 13 | //cache this in r.Spritesheet page 14 | for(let i = 0; i < 20; i++){ 15 | this.drawStack.push({ 16 | x: this.x + (Math.random()*2-1) * this.radius, 17 | y: this.y + (Math.random()*2-1) * this.radius, 18 | w: Math.random() * this.radius, 19 | h: Math.random() * this.radius, 20 | color: this.color + Math.random()*3 21 | }) 22 | } 23 | 24 | return this; 25 | } 26 | Artifact.prototype.draw = function(){ 27 | 28 | if(inView(this, 200)){ 29 | r.pat = r.dither[0]; 30 | this.drawStack.forEach(function(d){ 31 | r.fillRect(d.x-view.x, d.y-view.y, d.w, d.h, d.color); 32 | }) 33 | } 34 | } 35 | Artifact.prototype.update = function(){ 36 | 37 | if(inView(this, 200)){ 38 | 39 | let distx = this.x - p.x; 40 | let disty = this.y - p.y; 41 | 42 | let dist = Math.sqrt(distx*distx + disty*disty); 43 | if( dist <= this.radius + p.radius ){ 44 | 45 | this.alive = false; 46 | for(let i = 10; i > 0; i--){ 47 | splodes.push(new Splode(p.x+Math.random()*20-10, p.y+Math.random()*20-10, Math.random()*70, 20*Math.random()*5) ); 48 | } 49 | playSound(sounds.cellComplete) 50 | collected.push(this); 51 | 52 | } 53 | } 54 | 55 | 56 | } 57 | 58 | export default Artifact; -------------------------------------------------------------------------------- /src/js/baby.js: -------------------------------------------------------------------------------- 1 | import { inView, playSound, lerp, choice } from './core/utils.js'; 2 | import Splode from './splode.js'; 3 | const HOME = 0; 4 | const SEEKING = 1; 5 | const ORBITING = 3; 6 | const ATTACKING = 2; 7 | 8 | function Baby(x,y){ 9 | 10 | 11 | 12 | this.state = 0; 13 | this.previousState = 0; 14 | this.angle = 0; 15 | this.targetAngle = 0; 16 | 17 | this.x = x; 18 | this.y = y; 19 | this.targetX = 0; 20 | this.targetY = 0; 21 | 22 | this.alive = true; 23 | 24 | this.updateOrbits(); 25 | 26 | return this; 27 | } 28 | 29 | Baby.prototype.draw = function(){ 30 | 31 | if(inView(this, 10)){ 32 | 33 | switch(this.state){ 34 | case HOME: 35 | case ORBITING: 36 | r.fillCircle(this.x-view.x, this.y-view.y, 2, 0); 37 | r.circle(this.x-view.x, this.y-view.y, 2, choice([19,20,22]) ); 38 | splodes.push( new Splode(this.x, this.y, 5, choice([19,20,22]) ) ); 39 | break; 40 | case ATTACKING: 41 | r.fillCircle(this.x-view.x, this.y-view.y, 2, 0); 42 | r.circle(this.x-view.x, this.y-view.y, 2, choice([5,6,7]) ); 43 | splodes.push( new Splode(this.x, this.y, 5, choice([5,6,7]) ) ); 44 | splodes.push( new Splode(this.enemyTarget.x+(Math.random()*2-1)*this.enemyTarget.radius, 45 | this.enemyTarget.y+(Math.random()*2-1)*this.enemyTarget.radius, 5, choice([5,6,7]) ) ); 46 | break; 47 | } 48 | } 49 | 50 | 51 | } 52 | Baby.prototype.update = function(){ 53 | 54 | if(this.state != this.previousState){ 55 | //playSound(sounds.babyaction2, 1, 0, 0.1, false); 56 | this.previousState = this.state; 57 | } 58 | 59 | if(!inView(this, 20)){ 60 | this.state = HOME; 61 | } 62 | 63 | this.targetAngle += 0.015; 64 | if(p.enemiesInView.length > 0 && this.state != ATTACKING){ 65 | this.enemyTarget = choice(p.enemiesInView); 66 | this.state = ATTACKING; 67 | }else if(p.enemiesInView.length == 0) { 68 | this.state = HOME; 69 | if(p.withinPlanetGravity){ 70 | this.state = ORBITING; 71 | } 72 | } 73 | 74 | switch(this.state){ 75 | 76 | case HOME: 77 | this.targetX = p.x + Math.cos(this.angle) * (p.radius +babies.length-p.xVel*2)+Math.cos(this.angle*7)*20; 78 | this.targetY = p.y + Math.sin(this.angle) * (p.radius +babies.length-p.yVel*2)+Math.sin(this.angle*7.1)*20; 79 | if(this.enemyTarget){ 80 | this.enemyTarget.attacked = false; 81 | } 82 | 83 | break; 84 | 85 | case ORBITING: 86 | this.targetX = p.planet.x + Math.cos(this.angle) * (p.planet.radius + 7+babies.length-p.xVel*2); 87 | this.targetY = p.planet.y + Math.sin(this.angle) * (p.planet.radius + 7+babies.length-p.yVel*2); 88 | break; 89 | 90 | case ATTACKING: 91 | this.targetX = this.enemyTarget.x + Math.cos(this.angle) * (this.enemyTarget.radius + 5); 92 | this.targetY = this.enemyTarget.y + Math.sin(this.angle) * (this.enemyTarget.radius + 5); 93 | this.enemyTarget.health -= 0.2; 94 | //this.enemyTarget.underAttack = true; 95 | if(!this.enemyTarget.alive){ 96 | //playSound(sounds.babyaction1, 1, 0, 0.1, false); 97 | this.enemyTarget = choice(p.enemiesInView); 98 | } 99 | break; 100 | } 101 | 102 | 103 | this.x = lerp(this.x, this.targetX, 0.15); 104 | this.y = lerp(this.y, this.targetY, 0.15); 105 | this.angle = lerp(this.angle, this.targetAngle, 0.15); 106 | 107 | if(inView(this, 10)){ 108 | 109 | // let distx = this.x - p.x; 110 | // let disty = this.y - p.y; 111 | 112 | // let dist = Math.sqrt(distx*distx + disty*disty); 113 | 114 | } 115 | 116 | } 117 | 118 | Baby.prototype.updateOrbits = function(){ 119 | babies.forEach(function(e,i, a){ 120 | e.targetAngle = Math.PI*2/(a.length+1) * (i+1); 121 | }) 122 | } 123 | 124 | export default Baby; -------------------------------------------------------------------------------- /src/js/core/Stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var Stats = function () { 6 | 7 | var mode = 0; 8 | 9 | var container = document.createElement( 'div' ); 10 | container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000'; 11 | container.addEventListener( 'click', function ( event ) { 12 | 13 | event.preventDefault(); 14 | showPanel( ++ mode % container.children.length ); 15 | 16 | }, false ); 17 | 18 | // 19 | 20 | function addPanel( panel ) { 21 | 22 | container.appendChild( panel.dom ); 23 | return panel; 24 | 25 | } 26 | 27 | function showPanel( id ) { 28 | 29 | for ( var i = 0; i < container.children.length; i ++ ) { 30 | 31 | container.children[ i ].style.display = i === id ? 'block' : 'none'; 32 | 33 | } 34 | 35 | mode = id; 36 | 37 | } 38 | 39 | // 40 | 41 | var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0; 42 | 43 | var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) ); 44 | var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) ); 45 | 46 | if ( self.performance && self.performance.memory ) { 47 | 48 | var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) ); 49 | 50 | } 51 | 52 | showPanel( 0 ); 53 | 54 | return { 55 | 56 | REVISION: 16, 57 | 58 | dom: container, 59 | 60 | addPanel: addPanel, 61 | showPanel: showPanel, 62 | 63 | begin: function () { 64 | 65 | beginTime = ( performance || Date ).now(); 66 | 67 | }, 68 | 69 | end: function () { 70 | 71 | frames ++; 72 | 73 | var time = ( performance || Date ).now(); 74 | 75 | msPanel.update( time - beginTime, 200 ); 76 | 77 | if ( time > prevTime + 1000 ) { 78 | 79 | fpsPanel.update( ( frames * 1000 ) / ( time - prevTime ), 100 ); 80 | 81 | prevTime = time; 82 | frames = 0; 83 | 84 | if ( memPanel ) { 85 | 86 | var memory = performance.memory; 87 | memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 ); 88 | 89 | } 90 | 91 | } 92 | 93 | return time; 94 | 95 | }, 96 | 97 | update: function () { 98 | 99 | beginTime = this.end(); 100 | 101 | }, 102 | 103 | // Backwards Compatibility 104 | 105 | domElement: container, 106 | setMode: showPanel 107 | 108 | }; 109 | 110 | }; 111 | 112 | Stats.Panel = function ( name, fg, bg ) { 113 | 114 | var min = Infinity, max = 0, round = Math.round; 115 | var PR = round( window.devicePixelRatio || 1 ); 116 | 117 | var WIDTH = 80 * PR, HEIGHT = 48 * PR, 118 | TEXT_X = 3 * PR, TEXT_Y = 2 * PR, 119 | GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR, 120 | GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR; 121 | 122 | var canvas = document.createElement( 'canvas' ); 123 | canvas.width = WIDTH; 124 | canvas.height = HEIGHT; 125 | canvas.style.cssText = 'width:80px;height:48px'; 126 | 127 | var context = canvas.getContext( '2d' ); 128 | context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif'; 129 | context.textBaseline = 'top'; 130 | 131 | context.fillStyle = bg; 132 | context.fillRect( 0, 0, WIDTH, HEIGHT ); 133 | 134 | context.fillStyle = fg; 135 | context.fillText( name, TEXT_X, TEXT_Y ); 136 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); 137 | 138 | context.fillStyle = bg; 139 | context.globalAlpha = 0.9; 140 | context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT ); 141 | 142 | return { 143 | 144 | dom: canvas, 145 | 146 | update: function ( value, maxValue ) { 147 | 148 | min = Math.min( min, value ); 149 | max = Math.max( max, value ); 150 | 151 | context.fillStyle = bg; 152 | context.globalAlpha = 1; 153 | context.fillRect( 0, 0, WIDTH, GRAPH_Y ); 154 | context.fillStyle = fg; 155 | context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y ); 156 | 157 | context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT ); 158 | 159 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT ); 160 | 161 | context.fillStyle = bg; 162 | context.globalAlpha = 0.9; 163 | context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) ); 164 | 165 | } 166 | 167 | }; 168 | 169 | }; 170 | 171 | export { Stats as default }; 172 | -------------------------------------------------------------------------------- /src/js/core/utils.js: -------------------------------------------------------------------------------- 1 | export function rand(min, max) { 2 | return Math.floor(Math.random() * (max + 1 - min) + min); 3 | }; 4 | 5 | export function choice(values) { 6 | return values[rand(0, values.length - 1)]; 7 | }; 8 | export function lerp(a, b, x){ 9 | return a + (b -a ) * x; 10 | } 11 | export function planetCollision(circleA, circleB){ 12 | let distX = circleA.x - circleB.x, 13 | distY = circleA.y - circleA.y, 14 | distance = Math.sqrt( (distX*distX) + (distY*distY) ); 15 | return distance < circleA.field + circleB.field; 16 | } 17 | 18 | export function doesPlanetHaveCollision(planet, spaceBetween) { 19 | for(var i = 0; i < planets.length; i++) { 20 | var otherPlanet = planets[i]; 21 | var a = planet.field + otherPlanet.field + spaceBetween; 22 | var x = planet.x - otherPlanet.x; 23 | var y = planet.y - otherPlanet.y; 24 | 25 | if (a >= Math.sqrt((x*x) + (y*y))) { 26 | return true; 27 | } 28 | } 29 | 30 | if(planet.x + planet.field >= Ww || 31 | planet.x - planet.field <= 0) { 32 | return true; 33 | } 34 | 35 | if(planet.y + planet.field >= Wh || 36 | planet.y - planet.field <= 0) { 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | export function inView(o, padding=0){ 44 | return o.x - view.x + padding > 0 && 45 | o.y - view.y + padding > 0 && 46 | o.x - view.x - padding < w && 47 | o.y - view.y - padding < h 48 | } 49 | 50 | export function playSound(buffer, playbackRate = 1, pan = 0, volume = .5, loop = false) { 51 | 52 | var source = window.audioCtx.createBufferSource(); 53 | var gainNode = window.audioCtx.createGain(); 54 | var panNode = window.audioCtx.createStereoPanner(); 55 | 56 | source.buffer = buffer; 57 | source.connect(panNode); 58 | panNode.connect(gainNode); 59 | gainNode.connect(audioMaster); 60 | 61 | source.playbackRate.value = playbackRate; 62 | source.loop = loop; 63 | gainNode.gain.value = volume; 64 | panNode.pan.value = pan; 65 | source.start(); 66 | return {volume: gainNode, sound: source}; 67 | 68 | } 69 | 70 | export const Key = { 71 | 72 | _pressed: {}, 73 | _released: {}, 74 | 75 | LEFT: 37, 76 | UP: 38, 77 | RIGHT: 39, 78 | DOWN: 40, 79 | SPACE: 32, 80 | ONE: 49, 81 | TWO: 50, 82 | THREE: 51, 83 | FOUR: 52, 84 | a: 65, 85 | c: 67, 86 | w: 87, 87 | s: 83, 88 | d: 68, 89 | z: 90, 90 | x: 88, 91 | f: 70, 92 | p: 80, 93 | r: 82, 94 | m: 77, 95 | h: 72, 96 | 97 | isDown(keyCode) { 98 | return this._pressed[keyCode]; 99 | }, 100 | 101 | justReleased(keyCode) { 102 | return this._released[keyCode]; 103 | }, 104 | 105 | onKeydown(event) { 106 | this._pressed[event.keyCode] = true; 107 | }, 108 | 109 | onKeyup(event) { 110 | this._released[event.keyCode] = true; 111 | delete this._pressed[event.keyCode]; 112 | 113 | }, 114 | 115 | update() { 116 | this._released = {}; 117 | } 118 | }; 119 | 120 | -------------------------------------------------------------------------------- /src/js/fuel.js: -------------------------------------------------------------------------------- 1 | import { inView, lerp, playSound } from './core/utils.js'; 2 | import Splode from './splode.js'; 3 | 4 | function Fuel(x,y, radius){ 5 | this.x = x; 6 | this.y = y; 7 | this.radius = radius; 8 | this.alive = true 9 | this.reaching = false; 10 | this.targetX = 0; 11 | this.targetY = 0; 12 | 13 | 14 | return this; 15 | } 16 | Fuel.prototype.draw = function(){ 17 | 18 | if(inView(this, 10)){ 19 | r.pat = r.dither[Math.random() < -0.5 ? 8 : 9]; 20 | r.fillCircle(this.x- view.x, this.y-view.y, this.radius+5, 14); 21 | r.pat = r.dither[0]; 22 | r.fillCircle(this.x-view.x, this.y-view.y, this.radius, 11); 23 | if(this.reaching){ 24 | let i = 10; 25 | while(i--){ 26 | r.pat = r.dither[i]; 27 | r.line(this.x - view.x + (Math.random()-0.5) * this.radius*2, 28 | this.y - view.y + (Math.random()-0.5) * this.radius*2, 29 | p.x - view.x, 30 | p.y - view.y, 31 | 10+Math.random()*5); 32 | } 33 | } 34 | } 35 | } 36 | Fuel.prototype.update = function(){ 37 | 38 | if(inView(this, 10)){ 39 | if(this.targetX > 0){ 40 | this.x = lerp(this.x, this.targetX, 0.2); 41 | this.y = lerp(this.y, this.targetY, 0.2); 42 | } 43 | 44 | let distx = this.x - p.x; 45 | let disty = this.y - p.y; 46 | 47 | this.dist = Math.sqrt(distx*distx + disty*disty); 48 | if( this.dist <= this.radius + p.radius + 40){ 49 | if(p.fuel < p.maxFuel){ 50 | this.radius -= 0.1; 51 | p.fuel += 1; 52 | p.absorbing = true; 53 | this.reaching = true; 54 | absorbSound.volume.gain.value = 0.1; 55 | }else if(p.fuel == p.maxFuel){ 56 | absorbSound.volume.gain.value = 0; 57 | p.absorbing = false; 58 | } 59 | 60 | }else {this.reaching = false;} 61 | 62 | } 63 | 64 | if(this.radius <= 0){ 65 | playSound(sounds.sectorComplete); 66 | splodes.push(new Splode(this.x, this.y, 120, 9)); 67 | this.alive = false; 68 | 69 | } 70 | 71 | 72 | } 73 | 74 | export default Fuel; -------------------------------------------------------------------------------- /src/js/harvester.js: -------------------------------------------------------------------------------- 1 | import { inView, playSound, lerp } from './core/utils.js'; 2 | import Splode from './splode.js'; 3 | import Fuel from './fuel'; 4 | 5 | function Harvester(angle, planet){ 6 | this.x = 0; 7 | this.y = 0; 8 | this.radius = 10; 9 | this.hit = false; 10 | this.hitCount = 0; 11 | this.alive = true 12 | this.health = 100; 13 | this.reaching = false; 14 | this.angle = angle; 15 | this.planet = planet; 16 | this.attacked = false; 17 | 18 | return this; 19 | } 20 | 21 | Harvester.prototype.draw = function(){ 22 | 23 | if(inView(this, 10)){ 24 | r.fillCircle(this.x-view.x, this.y-view.y, this.radius, 4); 25 | if(this.reaching){ 26 | let i = 10; 27 | while(i--){ 28 | r.pat = r.dither[i]; 29 | r.line(this.x - view.x + (Math.random()-0.5) * this.radius*2, 30 | this.y - view.y + (Math.random()-0.5) * this.radius*2, 31 | p.x - view.x, 32 | p.y - view.y, 33 | 3+Math.random()*5); 34 | } 35 | } 36 | } 37 | 38 | } 39 | Harvester.prototype.update = function(){ 40 | this.x = this.planet.x + Math.cos(this.angle) * (this.planet.radius + Math.sin(t/5)*5); 41 | this.y = this.planet.y + Math.sin(this.angle) * (this.planet.radius + Math.sin(t/5)*5); 42 | this.angle += 0.01; 43 | if(inView(this, -20)){ 44 | p.enemiesInView.push(this); 45 | } 46 | if(inView(this, 10)){ 47 | 48 | let distx = this.x - p.x; 49 | let disty = this.y - p.y; 50 | 51 | let dist = Math.sqrt(distx*distx + disty*disty); 52 | 53 | if( dist <= this.radius + p.radius + 20){ 54 | p.fuel -= 0.6; 55 | p.draining = true; 56 | this.reaching = true; 57 | harverterSuckSound.volume.gain.value = 0.1; 58 | if(dist <= this.radius + p.radius){ 59 | 60 | if(Math.abs(p.xVel + p.yVel)/2 < 2){ 61 | splodes.push(new Splode(this.x, this.y, 20, 22)); 62 | p.xVel = Math.cos(p.angle) * 4; 63 | p.yVel = Math.sin(p.angle) * 4; 64 | playSound(sounds.bump, 1, 0, 0.4, false); 65 | } 66 | } 67 | }else {this.reaching = false; p.draining = false;} 68 | if(this.health < 100 && !this.attacked){ 69 | playSound(sounds.harvestermoan, 2, 0, 0.2, false); 70 | this.attacked = true; 71 | } 72 | if(this.health <= 0){ 73 | this.alive = false; 74 | p.draining = false; 75 | this.planet.harvesters--; 76 | harvesters.splice(harvesters.indexOf(this), 1); 77 | splodes.push(new Splode(this.x, this.y, 50, 6)); 78 | splodes.push(new Splode(this.x+Math.random()*5, this.y+Math.random()*5, 60, 7)); 79 | splodes.push(new Splode(this.x+Math.random()*5, this.y+Math.random()*5, 70, 5)); 80 | playSound(sounds.boom1, 1, 0, 0.1, false); 81 | for(let i = 0; i < 2; i++){ 82 | let f = new Fuel(this.x, this.y, 2); 83 | f.targetX = this.x + Math.random()*90; 84 | f.targetY = this.y + Math.random()*90; 85 | Fuelrocks.push(f); 86 | } 87 | 88 | } 89 | 90 | 91 | 92 | 93 | } 94 | // if(this.radius <= 0){ 95 | // this.alive = false; 96 | // } 97 | 98 | 99 | } 100 | 101 | export default Harvester; -------------------------------------------------------------------------------- /src/js/musicplayer.js: -------------------------------------------------------------------------------- 1 | /* -*- mode: javascript; tab-width: 4; indent-tabs-mode: nil; -*- 2 | * 3 | * Copyright (c) 2011-2013 Marcus Geelnard 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software 15 | * in a product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must not be 19 | * misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source 22 | * distribution. 23 | * 24 | */ 25 | 26 | //"use strict"; 27 | 28 | // Some general notes and recommendations: 29 | // * This code uses modern ECMAScript features, such as ** instead of 30 | // Math.pow(). You may have to modify the code to make it work on older 31 | // browsers. 32 | // * If you're not using all the functionality (e.g. not all oscillator types, 33 | // or certain effects), you can reduce the size of the player routine even 34 | // further by deleting the code. 35 | 36 | 37 | var MusicPlayer = function() { 38 | 39 | //-------------------------------------------------------------------------- 40 | // Private methods 41 | //-------------------------------------------------------------------------- 42 | 43 | // Oscillators 44 | var osc_sin = function (value) { 45 | return Math.sin(value * 6.283184); 46 | }; 47 | 48 | var osc_saw = function (value) { 49 | return 2 * (value % 1) - 1; 50 | }; 51 | 52 | var osc_square = function (value) { 53 | return (value % 1) < 0.5 ? 1 : -1; 54 | }; 55 | 56 | var osc_tri = function (value) { 57 | var v2 = (value % 1) * 4; 58 | if(v2 < 2) return v2 - 1; 59 | return 3 - v2; 60 | }; 61 | 62 | var getnotefreq = function (n) { 63 | // 174.61.. / 44100 = 0.003959503758 (F3) 64 | return 0.003959503758 * (2 ** ((n - 128) / 12)); 65 | }; 66 | 67 | var createNote = function (instr, n, rowLen) { 68 | var osc1 = mOscillators[instr.i[0]], 69 | o1vol = instr.i[1], 70 | o1xenv = instr.i[3]/32, 71 | osc2 = mOscillators[instr.i[4]], 72 | o2vol = instr.i[5], 73 | o2xenv = instr.i[8]/32, 74 | noiseVol = instr.i[9], 75 | attack = instr.i[10] * instr.i[10] * 4, 76 | sustain = instr.i[11] * instr.i[11] * 4, 77 | release = instr.i[12] * instr.i[12] * 4, 78 | releaseInv = 1 / release, 79 | expDecay = -instr.i[13]/16, 80 | arp = instr.i[14], 81 | arpInterval = rowLen * (2 **(2 - instr.i[15])); 82 | 83 | var noteBuf = new Int32Array(attack + sustain + release); 84 | 85 | // Re-trig oscillators 86 | var c1 = 0, c2 = 0; 87 | 88 | // Local variables. 89 | var j, j2, e, t, rsample, o1t, o2t; 90 | 91 | // Generate one note (attack + sustain + release) 92 | for (j = 0, j2 = 0; j < attack + sustain + release; j++, j2++) { 93 | if (j2 >= 0) { 94 | // Switch arpeggio note. 95 | arp = (arp >> 8) | ((arp & 255) << 4); 96 | j2 -= arpInterval; 97 | 98 | // Calculate note frequencies for the oscillators 99 | o1t = getnotefreq(n + (arp & 15) + instr.i[2] - 128); 100 | o2t = getnotefreq(n + (arp & 15) + instr.i[6] - 128) * (1 + 0.0008 * instr.i[7]); 101 | } 102 | 103 | // Envelope 104 | e = 1; 105 | if (j < attack) { 106 | e = j / attack; 107 | } else if (j >= attack + sustain) { 108 | e = (j - attack - sustain) * releaseInv; 109 | e = (1 - e) * (3 ** (expDecay * e)); 110 | } 111 | 112 | // Oscillator 1 113 | c1 += o1t * e ** o1xenv; 114 | rsample = osc1(c1) * o1vol; 115 | 116 | // Oscillator 2 117 | c2 += o2t * e ** o2xenv; 118 | rsample += osc2(c2) * o2vol; 119 | 120 | // Noise oscillator 121 | if (noiseVol) { 122 | rsample += (2 * Math.random() - 1) * noiseVol; 123 | } 124 | 125 | // Add to (mono) channel buffer 126 | noteBuf[j] = (80 * rsample * e) | 0; 127 | } 128 | 129 | return noteBuf; 130 | }; 131 | 132 | 133 | //-------------------------------------------------------------------------- 134 | // Private members 135 | //-------------------------------------------------------------------------- 136 | 137 | // Array of oscillator functions 138 | var mOscillators = [ 139 | osc_sin, 140 | osc_square, 141 | osc_saw, 142 | osc_tri 143 | ]; 144 | 145 | // Private variables set up by init() 146 | var mSong, mLastRow, mCurrentCol, mNumWords, mMixBuf; 147 | 148 | 149 | //-------------------------------------------------------------------------- 150 | // Initialization 151 | //-------------------------------------------------------------------------- 152 | 153 | this.init = function (song) { 154 | // Define the song 155 | mSong = song; 156 | 157 | // Init iteration state variables 158 | mLastRow = song.endPattern; 159 | mCurrentCol = 0; 160 | 161 | // Prepare song info 162 | mNumWords = song.rowLen * song.patternLen * (mLastRow + 1) * 2; 163 | 164 | // Create work buffer (initially cleared) 165 | mMixBuf = new Int32Array(mNumWords); 166 | }; 167 | 168 | 169 | //-------------------------------------------------------------------------- 170 | // Public methods 171 | //-------------------------------------------------------------------------- 172 | 173 | // Generate audio data for a single track 174 | this.generate = function () { 175 | // Local variables 176 | var i, j, b, p, row, col, n, cp, 177 | k, t, lfor, e, x, rsample, rowStartSample, f, da; 178 | 179 | // Put performance critical items in local variables 180 | var chnBuf = new Int32Array(mNumWords), 181 | instr = mSong.songData[mCurrentCol], 182 | rowLen = mSong.rowLen, 183 | patternLen = mSong.patternLen; 184 | 185 | // Clear effect state 186 | var low = 0, band = 0, high; 187 | var lsample, filterActive = false; 188 | 189 | // Clear note cache. 190 | var noteCache = []; 191 | 192 | // Patterns 193 | for (p = 0; p <= mLastRow; ++p) { 194 | cp = instr.p[p]; 195 | 196 | // Pattern rows 197 | for (row = 0; row < patternLen; ++row) { 198 | // Execute effect command. 199 | var cmdNo = cp ? instr.c[cp - 1].f[row] : 0; 200 | if (cmdNo) { 201 | instr.i[cmdNo - 1] = instr.c[cp - 1].f[row + patternLen] || 0; 202 | 203 | // Clear the note cache since the instrument has changed. 204 | if (cmdNo < 17) { 205 | noteCache = []; 206 | } 207 | } 208 | 209 | // Put performance critical instrument properties in local variables 210 | var oscLFO = mOscillators[instr.i[16]], 211 | lfoAmt = instr.i[17] / 512, 212 | lfoFreq = (2 ** (instr.i[18] - 9)) / rowLen, 213 | fxLFO = instr.i[19], 214 | fxFilter = instr.i[20], 215 | fxFreq = instr.i[21] * 43.23529 * 3.141592 / 44100, 216 | q = 1 - instr.i[22] / 255, 217 | dist = instr.i[23] * 1e-5, 218 | drive = instr.i[24] / 32, 219 | panAmt = instr.i[25] / 512, 220 | panFreq = 6.283184 * (2 ** (instr.i[26] - 9)) / rowLen, 221 | dlyAmt = instr.i[27] / 255, 222 | dly = instr.i[28] * rowLen & ~1; // Must be an even number 223 | 224 | // Calculate start sample number for this row in the pattern 225 | rowStartSample = (p * patternLen + row) * rowLen; 226 | 227 | // Generate notes for this pattern row 228 | for (col = 0; col < 4; ++col) { 229 | n = cp ? instr.c[cp - 1].n[row + col * patternLen] : 0; 230 | if (n) { 231 | if (!noteCache[n]) { 232 | noteCache[n] = createNote(instr, n, rowLen); 233 | } 234 | 235 | // Copy note from the note cache 236 | var noteBuf = noteCache[n]; 237 | for (j = 0, i = rowStartSample * 2; j < noteBuf.length; j++, i += 2) { 238 | chnBuf[i] += noteBuf[j]; 239 | } 240 | } 241 | } 242 | 243 | // Perform effects for this pattern row 244 | for (j = 0; j < rowLen; j++) { 245 | // Dry mono-sample 246 | k = (rowStartSample + j) * 2; 247 | rsample = chnBuf[k]; 248 | 249 | // We only do effects if we have some sound input 250 | if (rsample || filterActive) { 251 | // State variable filter 252 | f = fxFreq; 253 | if (fxLFO) { 254 | f *= oscLFO(lfoFreq * k) * lfoAmt + 0.5; 255 | } 256 | f = 1.5 * Math.sin(f); 257 | low += f * band; 258 | high = q * (rsample - band) - low; 259 | band += f * high; 260 | rsample = fxFilter == 3 ? band : fxFilter == 1 ? high : low; 261 | 262 | // Distortion 263 | if (dist) { 264 | rsample *= dist; 265 | rsample = rsample < 1 ? rsample > -1 ? osc_sin(rsample*.25) : -1 : 1; 266 | rsample /= dist; 267 | } 268 | 269 | // Drive 270 | rsample *= drive; 271 | 272 | // Is the filter active (i.e. still audiable)? 273 | filterActive = rsample * rsample > 1e-5; 274 | 275 | // Panning 276 | t = Math.sin(panFreq * k) * panAmt + 0.5; 277 | lsample = rsample * (1 - t); 278 | rsample *= t; 279 | } else { 280 | lsample = 0; 281 | } 282 | 283 | // Delay is always done, since it does not need sound input 284 | if (k >= dly) { 285 | // Left channel = left + right[-p] * t 286 | lsample += chnBuf[k-dly+1] * dlyAmt; 287 | 288 | // Right channel = right + left[-p] * t 289 | rsample += chnBuf[k-dly] * dlyAmt; 290 | } 291 | 292 | // Store in stereo channel buffer (needed for the delay effect) 293 | chnBuf[k] = lsample | 0; 294 | chnBuf[k+1] = rsample | 0; 295 | 296 | // ...and add to stereo mix buffer 297 | mMixBuf[k] += lsample | 0; 298 | mMixBuf[k+1] += rsample | 0; 299 | } 300 | } 301 | } 302 | 303 | // Next iteration. Return progress (1.0 == done!). 304 | mCurrentCol++; 305 | return mCurrentCol / mSong.numChannels; 306 | }; 307 | 308 | // Create a AudioBuffer from the generated audio data 309 | this.createAudioBuffer = function(context) { 310 | var buffer = context.createBuffer(2, mNumWords / 2, 44100); 311 | for (var i = 0; i < 2; i ++) { 312 | var data = buffer.getChannelData(i); 313 | for (var j = i; j < mNumWords; j += 2) { 314 | data[j >> 1] = mMixBuf[j] / 65536; 315 | } 316 | } 317 | return buffer; 318 | }; 319 | 320 | // Create a WAVE formatted Uint8Array from the generated audio data 321 | this.createWave = function() { 322 | // Create WAVE header 323 | var headerLen = 44; 324 | var l1 = headerLen + mNumWords * 2 - 8; 325 | var l2 = l1 - 36; 326 | var wave = new Uint8Array(headerLen + mNumWords * 2); 327 | wave.set( 328 | [82,73,70,70, 329 | l1 & 255,(l1 >> 8) & 255,(l1 >> 16) & 255,(l1 >> 24) & 255, 330 | 87,65,86,69,102,109,116,32,16,0,0,0,1,0,2,0, 331 | 68,172,0,0,16,177,2,0,4,0,16,0,100,97,116,97, 332 | l2 & 255,(l2 >> 8) & 255,(l2 >> 16) & 255,(l2 >> 24) & 255] 333 | ); 334 | 335 | // Append actual wave data 336 | for (var i = 0, idx = headerLen; i < mNumWords; ++i) { 337 | // Note: We clamp here 338 | var y = mMixBuf[i]; 339 | y = y < -32767 ? -32767 : (y > 32767 ? 32767 : y); 340 | wave[idx++] = y & 255; 341 | wave[idx++] = (y >> 8) & 255; 342 | } 343 | 344 | // Return the WAVE formatted typed array 345 | return wave; 346 | }; 347 | 348 | // Get n samples of wave data at time t [s]. Wave data in range [-2,2]. 349 | this.getData = function(t, n) { 350 | var i = 2 * Math.floor(t * 44100); 351 | var d = new Array(n); 352 | for (var j = 0; j < 2*n; j += 1) { 353 | var k = i + j; 354 | d[j] = t > 0 && k < mMixBuf.length ? mMixBuf[k] / 32768 : 0; 355 | } 356 | return d; 357 | }; 358 | }; 359 | 360 | export default MusicPlayer; -------------------------------------------------------------------------------- /src/js/planet.js: -------------------------------------------------------------------------------- 1 | import Splode from './splode.js'; 2 | import { inView, choice, playSound} from './core/utils.js'; 3 | import Sector from './sector.js'; 4 | import Harvester from './harvester.js'; 5 | import Baby from './baby.js'; 6 | 7 | 8 | function Planet(){ 9 | this.x = 0; 10 | this.y = 0; 11 | this.radius = 25; 12 | this.field = this.radius + 30; 13 | this.color = 22; 14 | this.haloColor = 19; 15 | this.palette = [0,1,2,3,4,5,6] 16 | this.gravity = 0.15; 17 | this.sectors = 1; 18 | this.harvesters = 0; 19 | 20 | this.sectorsRemaining = this.sectors; 21 | this.completeFlag = false; 22 | this.populated = false; 23 | this.drawColor = 37; 24 | this.disease = []; 25 | 26 | return this; 27 | } 28 | Planet.prototype.draw = function(){ 29 | r.renderSource = r.PAGE_2; 30 | 31 | if(inView(this, 2500) && !inView(this,50)){ 32 | //radar HUD 33 | let ax = this.x - p.x, 34 | ay = this.y - p.y, 35 | arrowAngle = Math.atan2(ay, ax), 36 | drawX = p.x-view.x + Math.cos(arrowAngle) * (h/2 - 20); 37 | drawY = p.y-view.y + Math.sin(arrowAngle) * (h/2 - 20); 38 | let dist = Math.sqrt(ax*ax + ay*ay); 39 | 40 | if(dist/100 < 12){ 41 | r.pat=r.dither[8] 42 | r.circle(drawX, drawY, 12-( Math.min(12, Math.floor(dist/100)) ), this.completeFlag ? 19 : 7); 43 | r.pat=r.dither[0] 44 | } 45 | } 46 | 47 | if(inView(this, 200)){ 48 | //only draw the pretty stuff if the planet is fully pollinated 49 | if(this.sectorsRemaining == 0){ 50 | //oooh its pretty atmosphere halo time 51 | //outer haze 52 | r.pat = r.dither[15]; 53 | r.fillCircle(this.x - view.x, this.y - view.y, this.radius+30, this.haloColor); 54 | //a bit thicker now 55 | r.pat = r.dither[13]; 56 | r.fillCircle(this.x - view.x, this.y - view.y, this.radius+20, this.haloColor); 57 | r.pat = r.dither[11]; 58 | //moar thick 59 | r.fillCircle(this.x - view.x, this.y - view.y, this.radius+8, this.haloColor); 60 | r.pat = r.dither[8]; 61 | //that neat bright blue bit near the horizon 62 | r.fillCircle(this.x - view.x, this.y - view.y, this.radius+3, this.haloColor); 63 | r.fillCircle(this.x - view.x, this.y - view.y, this.radius+2, this.haloColor+1); 64 | 65 | if(this.reaching){ 66 | 67 | r.pat = r.dither[12] 68 | let i = 10; 69 | while(i--){ 70 | r.line(this.x - view.x + (Math.random()-0.5) * this.radius*2, 71 | this.y - view.y + (Math.random()-0.5) * this.radius*2, 72 | p.x - view.x + (Math.random()-0.5) * p.radius, 73 | p.y - view.y + (Math.random()-0.5) * p.radius, 74 | 18+Math.random()*4); 75 | } 76 | r.pat = r.dither[0]; 77 | } 78 | 79 | } 80 | 81 | r.pat = r.dither[0]; 82 | //the planet itself 83 | r.tfillCircle(this.x - view.x, this.y - view.y, this.radius, this.drawColor); 84 | 85 | //icky gray stuff 86 | r.pat = r.dither[8]; 87 | this.disease.forEach(function(d){ 88 | r.pat = r.dither[d.dither]; 89 | // let px = (d.x) 90 | // let py = (d.y) 91 | // let rotatedX = px * Math.cos(3) - py * Math.sin(3); 92 | // let rotatedY = px * Math.sin(3) + py * Math.cos(3); 93 | // let newX = px + this.x; 94 | // let newY = py + this.y; 95 | // r.fillCircle(newX, newY, d.radius, 42); 96 | r.fillCircle(d.x - view.x, d.y - view.y, d.radius, 42); 97 | }); 98 | 99 | //a bright cyan line to finish it off --dark red if its not pollinated 100 | r.circle(this.x - view.x, this.y - view.y, this.radius, this.sectorsRemaining == 0 ? 19 : 4); 101 | 102 | //the planet gravity field 103 | r.circle(this.x - view.x, this.y - view.y, this.field, 1); 104 | 105 | 106 | } 107 | 108 | } 109 | Planet.prototype.update = function(){ 110 | 111 | if(inView(this, 200)){ 112 | this.reaching = false; 113 | if(!this.populated){ 114 | planetsDiscovered++; 115 | this.field = this.radius + 45; 116 | this.haloColor=choice([18,15,29]); 117 | this.sectors = Math.round(this.radius/10); 118 | this.sectorsRemaining = this.sectors; 119 | 120 | for(let i = 0; i < this.sectors; i++){ 121 | let x = this.x + this.radius * Math.cos(i*(2*Math.PI)/this.sectors); 122 | let y = this.y + this.radius * Math.sin(i*(2*Math.PI)/this.sectors); 123 | //r.circle(x - view.x, y - view.y, 2, 22); 124 | planetSectors.push(new Sector(x, y, this) ); 125 | } 126 | for(let i = 0; i < this.harvesters; i++){ 127 | let angle = 2*Math.PI/this.harvesters * i; 128 | harvesters.push(new Harvester(angle, this) ); 129 | } 130 | for(let i = 0; i < 300; i++){ 131 | this.disease.push( 132 | { 133 | x: this.x + Math.cos(Math.random()*3.14159*2) * Math.random()*this.radius, 134 | y: this.y + Math.sin(Math.random()*3.14159*2) * Math.random()*this.radius, 135 | radius: Math.random()*12, 136 | dither: Math.floor(8+Math.random()*4) 137 | }); 138 | } 139 | this.populated = true; 140 | } 141 | if(this.sectorsRemaining == 0){ 142 | this.drawColor = this.color; 143 | if(!this.completeFlag){ 144 | this.completeFlag = true; 145 | planetsComplete++; 146 | playSound(sounds.tada); 147 | this.disease = []; 148 | let i = 80; 149 | while(i--){ 150 | let a = Math.random()*3.14159*2; 151 | let r = Math.random()*this.radius; 152 | splodes.push(new Splode(this.x + r*Math.cos(a), this.y + r*Math.sin(a), 20+Math.random()*50, choice([20,21,22]) ) ); 153 | } 154 | babies.push(new Baby(this.x, this.y)); 155 | } 156 | } 157 | 158 | let distx = this.x - p.x; 159 | let disty = this.y - p.y; 160 | 161 | let dist = Math.sqrt(distx*distx + disty*disty); 162 | if( dist <= this.field + p.radius + 100){ 163 | this.reaching = true; 164 | 165 | } 166 | if(this.completeFlag && this.reaching){ 167 | p.fuel += 0.12; 168 | } 169 | if( dist <= this.field + p.radius ){ 170 | 171 | if(!p.withinPlanetGravity){ 172 | p.onPlanet(this); 173 | } 174 | 175 | } 176 | } 177 | 178 | 179 | } 180 | 181 | export default Planet; -------------------------------------------------------------------------------- /src/js/player.js: -------------------------------------------------------------------------------- 1 | import Splode from "./splode"; 2 | import { Key, choice, playSound } from "./core/utils"; 3 | 4 | /* 5 | TODO: implement double jump or thrust after jump to escape from gravity 6 | */ 7 | Player = { 8 | x: 0, 9 | y: 0, 10 | radius: 10, 11 | color: 4, 12 | colliding: false, 13 | withinPlanetGravity: false, 14 | hitPlanet: false, 15 | inAir: false, 16 | chargingJump: false, 17 | canJump: false, 18 | canDoubleJump: false, 19 | planet: null, 20 | py: 0, 21 | px: 0, 22 | angle: 0, 23 | bodyAngle: 0, 24 | armThrust: 0, 25 | legThrust: 0, 26 | planetAngle: 0, 27 | runSpeed: 2.1, 28 | turnSpeed: 0.15, 29 | baseJumpSpeed: 2.7, 30 | jumpSpeed: 2.7, 31 | jumpSpeedIncrement: 0.4, 32 | thrust: 0.1, 33 | yVel: 0, 34 | xVel: 0, 35 | maxXVel: 3.5, 36 | maxYVel: 3.5, 37 | fuel: 150, 38 | maxFuel: 300, 39 | fuelDecay: 0.025, 40 | draining: false, 41 | div12: Math.PI/6, 42 | forwardX: 0, 43 | forwardy: 0, 44 | forwardXend: 0, 45 | forwardyend: 0, 46 | headx: 0, 47 | heady: 0, 48 | neckx: 0, 49 | necky: 0, 50 | foot1x: 0, 51 | foot1y: 0, 52 | foot2x: 0, 53 | foot2y: 0, 54 | arm1x: 0, 55 | arm1y: 0, 56 | arm2x: 0, 57 | arm2y: 0, 58 | jetnoise: {}, 59 | babies:0, 60 | init: false, 61 | absorbing: false, 62 | enemiesInView: [], 63 | 64 | 65 | 66 | draw: function(){ 67 | 68 | r.pat=r.dither[0]; 69 | let sx = this.x - view.x, 70 | sy = this.y - view.y, 71 | div12 = this.div12, 72 | 73 | forwardX = sx + Math.cos(this.angle) * (this.radius + 5), 74 | forwardy = sy + Math.sin(this.angle) * (this.radius + 5), 75 | forwardXend = sx + Math.cos(this.angle) * (this.radius + 7), 76 | forwardyend = sy + Math.sin(this.angle) * (this.radius + 7), 77 | 78 | headx = sx + Math.cos(this.bodyAngle) * (this.radius), 79 | heady = sy + Math.sin(this.bodyAngle) * (this.radius), 80 | neckx = sx + Math.cos(this.bodyAngle) * (this.radius/2), 81 | necky = sy + Math.sin(this.bodyAngle) * (this.radius/2); 82 | 83 | 84 | this.arm1x = arm1x = neckx + Math.cos(this.bodyAngle + div12*3.5+this.armThrust ) * (this.radius); 85 | this.arm1y = arm1y = necky + Math.sin(this.bodyAngle + div12*3.5+this.armThrust ) * (this.radius); 86 | this.arm2x = arm2x = neckx + Math.cos(this.bodyAngle - div12*3.5-this.armThrust ) * (this.radius); 87 | this.arm2y = arm2y = necky + Math.sin(this.bodyAngle - div12*3.5-this.armThrust ) * (this.radius); 88 | 89 | this.foot1x = foot1x = sx + Math.cos(this.bodyAngle + div12*5+this.legThrust ) * (this.radius); 90 | this.foot1y = foot1y = sy + Math.sin(this.bodyAngle + div12*5+this.legThrust ) * (this.radius); 91 | this.foot2x = foot2x = sx + Math.cos(this.bodyAngle - div12*5-this.legThrust ) * (this.radius); 92 | this.foot2y = foot2y = sy + Math.sin(this.bodyAngle - div12*5-this.legThrust ) * (this.radius); 93 | 94 | //r.circle(sx, sy, this.radius, 1); //collide circle 95 | //r.line(forwardX, forwardy, forwardXend, forwardyend, 7); //forward line 96 | 97 | r.fillCircle(headx, heady, 2, 22); //head 98 | r.circle(foot1x, foot1y, 1, 22); //foot1 99 | r.circle(foot2x, foot2y, 1, 22); //foot2 100 | r.line(sx, sy, headx, heady, 22); //torso/neck line 101 | r.line(sx, sy, foot1x, foot1y, 22); //leg1 102 | r.line(sx, sy, foot2x, foot2y, 22); //leg2 103 | r.line(sx, sy, foot1x, foot1y, 22); //leg1 104 | r.line(sx, sy, foot2x, foot2y, 22); //leg2r.line(sx, sy, headx, heady, 22); //torso/neck line 105 | r.line(neckx, necky, arm1x, arm1y, 22); //arm1 106 | r.line(neckx, necky, arm2x, arm2y, 22); //arm2 107 | 108 | if(this.draining){ 109 | 110 | for(let i = 0; i < 100; i++){ 111 | let ra = Math.random()*2*Math.PI; 112 | r.pset( sx + Math.cos(ra) * (this.radius + 5 + Math.random()*5), 113 | sy + Math.sin(ra) * (this.radius + 5 + Math.random()*5), 114 | choice([3,4])) 115 | } 116 | } 117 | 118 | 119 | 120 | // debugTxt = 121 | // // `${this.withinPlanetGravity}\n 122 | // // XV ${this.xVel}\n 123 | // // YV ${this.yVel}\n 124 | // `JS ${this.jumpSpeed}\n 125 | // FUEL: ${this.fuel}\n 126 | // ARM: ${this.armThrust}\nYV: ${this.yVel}\nXV: ${this.xVel}\n` 127 | // //VX ${view.x} VY ${view.y}\n 128 | // .toUpperCase(); 129 | 130 | // r.text([debugTxt, 5, 5, 1, 1, 'left', 'top', 1, 22]); 131 | }, 132 | 133 | update: function(){ 134 | this.fuel -= this.fuelDecay; 135 | if(!this.init){ 136 | this.jetnoise = playSound(sounds.jet, 0.6, 0, 0.1, true); 137 | this.jetnoise.volume.gain.value = 0; 138 | this.init = true; 139 | }else{this.jetnoise.volume.gain.value = 0;} 140 | this.yVel = this.yVel > this.maxYVel ? this.maxYVel : this.yVel; 141 | this.xVel = this.xVel > this.maxXVel ? this.maxXVel : this.xVel; 142 | this.yVel = this.yVel < -this.maxYVel ? -this.maxYVel : this.yVel; 143 | this.xVel = this.xVel < -this.maxXVel ? -this.maxXVel : this.xVel; 144 | this.y += this.yVel; 145 | this.x += this.xVel; 146 | if(this.x > Ww){this.x = 0;}else if(this.x < 0){this.x = Ww;} 147 | if(this.y > Wh){this.y = 0;}else if(this.y < 0){this.y = Wh;} 148 | 149 | this.bodyAngle = this.angle// - Math.PI/2; 150 | 151 | if(!this.colliding){ 152 | 153 | let velAngle = Math.atan2(this.yVel, this.xVel); 154 | 155 | this.armThrust = this.legThrust = Math.cos(this.bodyAngle - velAngle); 156 | } 157 | 158 | if(this.withinPlanetGravity){ 159 | this.angle = this.planetAngle; 160 | this.legThrust = Math.cos(t/10); 161 | } 162 | 163 | 164 | 165 | 166 | if(this.fuel <= 0){ 167 | this.fuel = 0; 168 | } 169 | 170 | if(this.fuel > this.maxFuel){ 171 | this.fuel = this.maxFuel; 172 | } 173 | 174 | if(this.planet){ 175 | let distx = this.planet.x - p.x; 176 | let disty = this.planet.y - p.y; 177 | let dist = Math.sqrt(distx*distx + disty*disty); 178 | if( dist >= this.planet.radius + this.radius ){ 179 | this.colliding = false; 180 | 181 | } 182 | if( dist >= this.planet.field + this.radius ){ 183 | this.withinPlanetGravity = false; 184 | } 185 | if(dist <= this.planet.radius){ 186 | this.colliding=true; 187 | } 188 | if(this.withinPlanetGravity){ 189 | //gravity towards planet 190 | this.py = this.y - this.planet.y; 191 | this.px = this.x - this.planet.x; 192 | this.planetAngle = Math.atan2(this.py, this.px); 193 | this.bodyAngle = this.planetAngle; 194 | this.xVel -= Math.cos(this.planetAngle) * this.planet.gravity; 195 | this.yVel -= Math.sin(this.planetAngle) * this.planet.gravity; 196 | } 197 | } 198 | 199 | if(this.colliding){ 200 | this.yVel = 0; 201 | this.xVel = 0; 202 | 203 | } 204 | 205 | if( Key.isDown(Key.LEFT) || Key.isDown(Key.q) || Key.isDown(Key.a) ){ this.moveLeft() } 206 | else if(Key.isDown(Key.RIGHT) || Key.isDown(Key.d) ){ this.moveRight() } 207 | 208 | if(Key.isDown(Key.UP) || Key.isDown(Key.z) || Key.isDown(Key.w) ){ this.moveUp() } 209 | else if(Key.isDown(Key.DOWN) || Key.isDown(Key.w) ){ this.moveDown() } 210 | 211 | }, 212 | 213 | moveLeft: function(){ 214 | if(this.colliding){ 215 | this.xVel -= Math.cos(this.planetAngle + Math.PI/2) * this.runSpeed; 216 | this.yVel -= Math.sin(this.planetAngle + Math.PI/2) * this.runSpeed; 217 | 218 | }else{ 219 | this.angle -= this.turnSpeed; 220 | this.angle = this.angle % (Math.PI*2); 221 | if(!this.withinPlanetGravity){ 222 | splodes.push( new Splode(this.arm1x+view.x, this.arm1y+view.y, 5, choice([19,20,22]) ) ); 223 | this.jetnoise.volume.gain.value = 0.03; 224 | } 225 | } 226 | }, 227 | 228 | moveRight: function(){ 229 | if(this.colliding){ 230 | this.xVel -= Math.cos(this.planetAngle - Math.PI/2) * this.runSpeed; 231 | this.yVel -= Math.sin(this.planetAngle - Math.PI/2) * this.runSpeed; 232 | }else{ 233 | this.angle += this.turnSpeed; 234 | this.angle = this.angle % (Math.PI*2); 235 | if(!this.withinPlanetGravity){ 236 | splodes.push( new Splode(this.arm2x+view.x, this.arm2y+view.y, 5, choice([19,20,22]) ) ); 237 | this.jetnoise.volume.gain.value = 0.03; 238 | } 239 | } 240 | }, 241 | 242 | moveUp: function(){ 243 | if(this.colliding){ 244 | this.xVel += Math.cos(this.planetAngle) * this.jumpSpeed; 245 | this.yVel += Math.sin(this.planetAngle) * this.jumpSpeed; 246 | 247 | 248 | }else{ 249 | this.xVel += Math.cos(this.angle) * this.thrust; 250 | this.yVel += Math.sin(this.angle) * this.thrust; 251 | 252 | 253 | if(!this.withinPlanetGravity){ 254 | splodes.push( new Splode(this.foot1x+view.x, this.foot1y+view.y, 5, choice([7,8,22]) ) ); 255 | splodes.push( new Splode(this.foot2x+view.x, this.foot2y+view.y, 5, choice([7,8,22]) ) ); 256 | this.jetnoise.volume.gain.value = 0.03; 257 | } 258 | } 259 | }, 260 | 261 | moveDown: function(){ 262 | if(this.colliding){ 263 | //this.chargingJump = true 264 | //this.jumpSpeed += 0.01; 265 | }else{ 266 | this.xVel -= Math.cos(this.angle) * this.thrust; 267 | this.yVel -= Math.sin(this.angle) * this.thrust; 268 | 269 | if(!this.withinPlanetGravity){ 270 | splodes.push( new Splode(this.arm1x+view.x, this.arm1y+view.y, 5, choice([19,20,22]) ) ); 271 | splodes.push( new Splode(this.arm2x+view.x, this.arm2y+view.y, 5, choice([19,20,22]) ) ); 272 | this.jetnoise.volume.gain.value = 0.03; 273 | } 274 | } 275 | 276 | }, 277 | 278 | onPlanet: function(planet){; 279 | this.planet = planet; 280 | this.py = this.y - this.planet.y; 281 | this.px = this.x - this.planet.x; 282 | this.angle = Math.atan2(this.py, this.px); 283 | this.withinPlanetGravity = true; 284 | 285 | }, 286 | 287 | reset: function(){ 288 | this.fuel = 100; 289 | this.xVel = this.yVel = 0; 290 | p.x = Ww/2; 291 | p.y = Wh/2; 292 | babies = []; 293 | } 294 | 295 | 296 | } 297 | 298 | export default Player; -------------------------------------------------------------------------------- /src/js/sector.js: -------------------------------------------------------------------------------- 1 | import { inView, playSound } from './core/utils.js'; 2 | import Splode from './splode.js'; 3 | 4 | function Sector(x,y, planet){ 5 | this.x = x; 6 | this.y = y; 7 | this.radius = 0.1; 8 | this.maxRadius = 15; 9 | this.alive = true 10 | this.reaching = false; 11 | this.angle = 0; 12 | this.planet = planet; 13 | this.complete = false; 14 | 15 | return this; 16 | } 17 | Sector.prototype.draw = function(){ 18 | 19 | if(inView(this, 10)){ 20 | r.pat = r.dither[Math.random() < -0.5 ? 8 : 9]; 21 | if(!this.complete){ 22 | r.fillCircle(this.x- view.x, this.y-view.y, this.radius+5, 14); 23 | r.pat = r.dither[0]; 24 | r.fillCircle(this.x-view.x, this.y-view.y, this.radius, 11); 25 | if(this.reaching){ 26 | let i = 10; 27 | while(i--){ 28 | r.pat = r.dither[i]; 29 | r.line(this.x - view.x + (Math.random()-0.5) * this.radius*2, 30 | this.y - view.y + (Math.random()-0.5) * this.radius*2, 31 | p.x - view.x, 32 | p.y - view.y, 33 | 10+Math.random()*5); 34 | } 35 | } 36 | }else{ 37 | r.circle(this.x- view.x, this.y-view.y, this.radius+5, 19); 38 | } 39 | 40 | } 41 | 42 | } 43 | Sector.prototype.update = function(){ 44 | 45 | if(inView(this, 10)){ 46 | 47 | let distx = this.x - p.x; 48 | let disty = this.y - p.y; 49 | 50 | let dist = Math.sqrt(distx*distx + disty*disty); 51 | 52 | if( dist <= this.radius + p.radius + 40 && p.fuel > 0){ 53 | if(this.radius < this.maxRadius){ 54 | this.radius += 0.3; 55 | p.fuel -= 0.6; 56 | this.planet.disease.splice(0,1); 57 | this.reaching = true; 58 | sectorFillSound.volume.gain.value = 0.1; 59 | }else{this.reaching = false;} 60 | 61 | }else {this.reaching = false;} 62 | 63 | if(this.radius > this.maxRadius){ 64 | this.radius = this.maxRadius; 65 | if(!this.complete){ 66 | this.complete = true; 67 | playSound(sounds.sectorget); 68 | this.reaching = false; 69 | this.planet.sectorsRemaining--; 70 | splodes.push(new Splode(this.x+Math.random()*10, this.y+Math.random()*10, 30, 14)); 71 | splodes.push(new Splode(this.x+Math.random()*10, this.y+Math.random()*10, 40, 15)); 72 | splodes.push(new Splode(this.x+Math.random()*10, this.y+Math.random()*10, 50, 16)); 73 | } 74 | } 75 | 76 | if(this.planet.sectorsRemaining == 0){ 77 | this.alive = false 78 | } 79 | 80 | } 81 | // if(this.radius <= 0){ 82 | // this.alive = false; 83 | // } 84 | 85 | 86 | } 87 | 88 | export default Sector; -------------------------------------------------------------------------------- /src/js/sounds/absorbray.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var absorbray = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 1, // OSC1_WAVEFORM 13 | 255, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 1, // OSC2_WAVEFORM 17 | 154, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 9, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 7, // ENV_ATTACK 23 | 5, // ENV_SUSTAIN 24 | 52, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 14, // FX_FREQ 34 | 47, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 4, // FX_PAN_AMT 38 | 5, // FX_PAN_FREQ 39 | 24, // FX_DELAY_AMT 40 | 2 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [135,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,147,149,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152,151,152], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 2756, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default absorbray; -------------------------------------------------------------------------------- /src/js/sounds/babyaction.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var babyaction = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 255, // OSC1_VOL 14 | 116, // OSC1_SEMI 15 | 9, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 255, // OSC2_VOL 18 | 116, // OSC2_SEMI 19 | 0, // OSC2_DETUNE 20 | 5, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 4, // ENV_ATTACK 23 | 6, // ENV_SUSTAIN 24 | 68, // ENV_RELEASE 25 | 52, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 14, // FX_FREQ 34 | 0, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 0, // FX_PAN_AMT 38 | 0, // FX_PAN_FREQ 39 | 52, // FX_DELAY_AMT 40 | 2 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [171,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,175], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default babyaction -------------------------------------------------------------------------------- /src/js/sounds/babyaction2.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var babyaction2 = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 91, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 95, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 12, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 12, // ENV_ATTACK 23 | 0, // ENV_SUSTAIN 24 | 72, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 255, // FX_FREQ 34 | 0, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 83, // FX_PAN_AMT 38 | 3, // FX_PAN_FREQ 39 | 43, // FX_DELAY_AMT 40 | 4 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [159,156], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | export default babyaction2 -------------------------------------------------------------------------------- /src/js/sounds/boom1.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var boom1 = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 255, // OSC1_VOL 14 | 116, // OSC1_SEMI 15 | 64, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 255, // OSC2_VOL 18 | 108, // OSC2_SEMI 19 | 2, // OSC2_DETUNE 20 | 64, // OSC2_XENV 21 | 182, // NOISE_VOL 22 | 4, // ENV_ATTACK 23 | 6, // ENV_SUSTAIN 24 | 154, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 14, // FX_FREQ 34 | 33, // FX_RESONANCE 35 | 10, // FX_DIST 36 | 68, // FX_DRIVE 37 | 0, // FX_PAN_AMT 38 | 0, // FX_PAN_FREQ 39 | 0, // FX_DELAY_AMT 40 | 0 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [147], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default boom1 -------------------------------------------------------------------------------- /src/js/sounds/bump.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var bump = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 255, // OSC1_VOL 14 | 116, // OSC1_SEMI 15 | 60, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 255, // OSC2_VOL 18 | 116, // OSC2_SEMI 19 | 0, // OSC2_DETUNE 20 | 104, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 0, // ENV_ATTACK 23 | 6, // ENV_SUSTAIN 24 | 125, // ENV_RELEASE 25 | 52, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 48, // FX_FREQ 34 | 0, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 35, // FX_DRIVE 37 | 0, // FX_PAN_AMT 38 | 0, // FX_PAN_FREQ 39 | 57, // FX_DELAY_AMT 40 | 2 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [159], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | export default bump -------------------------------------------------------------------------------- /src/js/sounds/cellComplete.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var cellComplete = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 2, // OSC1_WAVEFORM 13 | 100, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 3, // OSC2_WAVEFORM 17 | 201, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 0, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 5, // ENV_ATTACK 23 | 6, // ENV_SUSTAIN 24 | 58, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 195, // LFO_AMT 30 | 6, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 135, // FX_FREQ 34 | 0, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 147, // FX_PAN_AMT 38 | 6, // FX_PAN_FREQ 39 | 28, // FX_DELAY_AMT 40 | 6 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [147,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,152,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,154], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5088, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default cellComplete -------------------------------------------------------------------------------- /src/js/sounds/dronemoan.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var dronemoan = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 3, // OSC1_WAVEFORM 13 | 146, // OSC1_VOL 14 | 140, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 1, // OSC2_WAVEFORM 17 | 224, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 3, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 92, // ENV_ATTACK 23 | 0, // ENV_SUSTAIN 24 | 44, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 3, // LFO_WAVEFORM 29 | 179, // LFO_AMT 30 | 5, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 124, // FX_FREQ 34 | 135, // FX_RESONANCE 35 | 11, // FX_DIST 36 | 32, // FX_DRIVE 37 | 150, // FX_PAN_AMT 38 | 3, // FX_PAN_FREQ 39 | 125, // FX_DELAY_AMT 40 | 6 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [99,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default dronemoan -------------------------------------------------------------------------------- /src/js/sounds/harvestermoan.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var harvestermoan = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 3, // OSC1_WAVEFORM 13 | 146, // OSC1_VOL 14 | 140, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 1, // OSC2_WAVEFORM 17 | 224, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 3, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 60, // ENV_ATTACK 23 | 0, // ENV_SUSTAIN 24 | 49, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 3, // LFO_WAVEFORM 29 | 179, // LFO_AMT 30 | 5, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 124, // FX_FREQ 34 | 135, // FX_RESONANCE 35 | 11, // FX_DIST 36 | 32, // FX_DRIVE 37 | 150, // FX_PAN_AMT 38 | 3, // FX_PAN_FREQ 39 | 125, // FX_DELAY_AMT 40 | 3 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [111,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,121], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default harvestermoan -------------------------------------------------------------------------------- /src/js/sounds/jet.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var jet = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 0, // OSC1_VOL 14 | 140, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 0, // OSC2_VOL 18 | 140, // OSC2_SEMI 19 | 0, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 255, // NOISE_VOL 22 | 0, // ENV_ATTACK 23 | 255, // ENV_SUSTAIN 24 | 255, // ENV_RELEASE 25 | 19, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 0, // LFO_AMT 30 | 0, // LFO_FREQ 31 | 0, // LFO_FX_FREQ 32 | 3, // FX_FILTER 33 | 3, // FX_FREQ 34 | 57, // FX_RESONANCE 35 | 20, // FX_DIST 36 | 59, // FX_DRIVE 37 | 0, // FX_PAN_AMT 38 | 0, // FX_PAN_FREQ 39 | 157, // FX_DELAY_AMT 40 | 2 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [123,,,,,111,,,,111,,,,,,,135,,,,,,,,,111,,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,139,,,,,,,,137,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,140,,,,,,111,,,,,,,,,,139,,,,111,,,,,,111,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,142,,,,,,,111,,,,140,,,,,,,,,,,,,111,,,139], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5513, // In sample lengths 52 | patternLen: 64, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default jet -------------------------------------------------------------------------------- /src/js/sounds/sectorget.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var sectorget = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 1, // OSC1_WAVEFORM 13 | 192, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 1, // OSC2_WAVEFORM 17 | 191, // OSC2_VOL 18 | 116, // OSC2_SEMI 19 | 9, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 6, // ENV_ATTACK 23 | 22, // ENV_SUSTAIN 24 | 34, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 69, // LFO_AMT 30 | 3, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 1, // FX_FILTER 33 | 23, // FX_FREQ 34 | 167, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 77, // FX_PAN_AMT 38 | 6, // FX_PAN_FREQ 39 | 25, // FX_DELAY_AMT 40 | 6 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [163,159,161,166], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 5011, // In sample lengths 52 | patternLen: 12, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default sectorget -------------------------------------------------------------------------------- /src/js/sounds/song.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var song = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 0, // OSC1_WAVEFORM 13 | 192, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 0, // OSC2_WAVEFORM 17 | 191, // OSC2_VOL 18 | 116, // OSC2_SEMI 19 | 9, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 6, // ENV_ATTACK 23 | 22, // ENV_SUSTAIN 24 | 34, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 69, // LFO_AMT 30 | 3, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 1, // FX_FILTER 33 | 0, // FX_FREQ 34 | 16, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 16, // FX_DRIVE 37 | 170, // FX_PAN_AMT 38 | 6, // FX_PAN_FREQ 39 | 25, // FX_DELAY_AMT 40 | 6 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1,2,1,2,1,2,1,2,1,1], 44 | // Columns 45 | c: [ 46 | {n: [123,,,,,,135,,,,,,,,133,,,,133,,,,133,,,,130,,133,,135], 47 | f: []}, 48 | {n: [123,,,,,,135,,,,,,,,133], 49 | f: []} 50 | ] 51 | }, 52 | { // Instrument 1 53 | i: [ 54 | 0, // OSC1_WAVEFORM 55 | 91, // OSC1_VOL 56 | 128, // OSC1_SEMI 57 | 0, // OSC1_XENV 58 | 0, // OSC2_WAVEFORM 59 | 95, // OSC2_VOL 60 | 128, // OSC2_SEMI 61 | 12, // OSC2_DETUNE 62 | 0, // OSC2_XENV 63 | 0, // NOISE_VOL 64 | 12, // ENV_ATTACK 65 | 0, // ENV_SUSTAIN 66 | 72, // ENV_RELEASE 67 | 0, // ENV_EXP_DECAY 68 | 0, // ARP_CHORD 69 | 0, // ARP_SPEED 70 | 0, // LFO_WAVEFORM 71 | 0, // LFO_AMT 72 | 0, // LFO_FREQ 73 | 0, // LFO_FX_FREQ 74 | 2, // FX_FILTER 75 | 255, // FX_FREQ 76 | 0, // FX_RESONANCE 77 | 0, // FX_DIST 78 | 32, // FX_DRIVE 79 | 83, // FX_PAN_AMT 80 | 3, // FX_PAN_FREQ 81 | 130, // FX_DELAY_AMT 82 | 4 // FX_DELAY_TIME 83 | ], 84 | // Patterns 85 | p: [,,1,,1,1,1,1,1,1], 86 | // Columns 87 | c: [ 88 | {n: [135,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,139], 89 | f: []} 90 | ] 91 | }, 92 | { // Instrument 2 93 | i: [ 94 | 3, // OSC1_WAVEFORM 95 | 194, // OSC1_VOL 96 | 128, // OSC1_SEMI 97 | 0, // OSC1_XENV 98 | 2, // OSC2_WAVEFORM 99 | 198, // OSC2_VOL 100 | 128, // OSC2_SEMI 101 | 6, // OSC2_DETUNE 102 | 0, // OSC2_XENV 103 | 0, // NOISE_VOL 104 | 12, // ENV_ATTACK 105 | 12, // ENV_SUSTAIN 106 | 33, // ENV_RELEASE 107 | 0, // ENV_EXP_DECAY 108 | 0, // ARP_CHORD 109 | 0, // ARP_SPEED 110 | 0, // LFO_WAVEFORM 111 | 61, // LFO_AMT 112 | 4, // LFO_FREQ 113 | 1, // LFO_FX_FREQ 114 | 2, // FX_FILTER 115 | 109, // FX_FREQ 116 | 86, // FX_RESONANCE 117 | 7, // FX_DIST 118 | 32, // FX_DRIVE 119 | 112, // FX_PAN_AMT 120 | 3, // FX_PAN_FREQ 121 | 67, // FX_DELAY_AMT 122 | 2 // FX_DELAY_TIME 123 | ], 124 | // Patterns 125 | p: [,,,,1,2,1,2,3,4], 126 | // Columns 127 | c: [ 128 | {n: [,,,,130,,,,,,130,,,,,,,,128,,,,,,,,,,,,,,,,,,135,,,,,,135,,,,,,,,133], 129 | f: []}, 130 | {n: [,,,,123,,,,,,123,,,,,,,,123,,,,,,,,,,,,,,,,,,127,,,,,,127,,,,,,,,128], 131 | f: []}, 132 | {n: [,,,,123,,,,,,130,,,,,,,,123,,,,,,,,,,,,,,,,,,127,,,,,,127,,,,,,,,128], 133 | f: []}, 134 | {n: [,,,,130,,,,,,130,,,,,,,,130,,,,,,,,,,,,,,,,,,135,,,,,,135,,,,,,,,135], 135 | f: []} 136 | ] 137 | }, 138 | ], 139 | rowLen: 5513, // In sample lengths 140 | patternLen: 32, // Rows per pattern 141 | endPattern: 9, // End pattern 142 | numChannels: 3 // Number of channels 143 | }; 144 | export default song; -------------------------------------------------------------------------------- /src/js/sounds/tada.js: -------------------------------------------------------------------------------- 1 | // This music has been exported by SoundBox. You can use it with 2 | // http://sb.bitsnbites.eu/player-small.js in your own product. 3 | 4 | // See http://sb.bitsnbites.eu/demo.html for an example of how to 5 | // use it in a demo. 6 | 7 | // Song data 8 | var tada = { 9 | songData: [ 10 | { // Instrument 0 11 | i: [ 12 | 2, // OSC1_WAVEFORM 13 | 100, // OSC1_VOL 14 | 128, // OSC1_SEMI 15 | 0, // OSC1_XENV 16 | 3, // OSC2_WAVEFORM 17 | 201, // OSC2_VOL 18 | 128, // OSC2_SEMI 19 | 0, // OSC2_DETUNE 20 | 0, // OSC2_XENV 21 | 0, // NOISE_VOL 22 | 5, // ENV_ATTACK 23 | 6, // ENV_SUSTAIN 24 | 58, // ENV_RELEASE 25 | 0, // ENV_EXP_DECAY 26 | 0, // ARP_CHORD 27 | 0, // ARP_SPEED 28 | 0, // LFO_WAVEFORM 29 | 195, // LFO_AMT 30 | 6, // LFO_FREQ 31 | 1, // LFO_FX_FREQ 32 | 2, // FX_FILTER 33 | 135, // FX_FREQ 34 | 0, // FX_RESONANCE 35 | 0, // FX_DIST 36 | 32, // FX_DRIVE 37 | 147, // FX_PAN_AMT 38 | 6, // FX_PAN_FREQ 39 | 55, // FX_DELAY_AMT 40 | 6 // FX_DELAY_TIME 41 | ], 42 | // Patterns 43 | p: [1], 44 | // Columns 45 | c: [ 46 | {n: [151,151,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,154,154,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,159,159,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,135,135], 47 | f: []} 48 | ] 49 | }, 50 | ], 51 | rowLen: 6014, // In sample lengths 52 | patternLen: 32, // Rows per pattern 53 | endPattern: 0, // End pattern 54 | numChannels: 1 // Number of channels 55 | }; 56 | 57 | export default tada -------------------------------------------------------------------------------- /src/js/splode.js: -------------------------------------------------------------------------------- 1 | function Splode(x,y,life, color){ 2 | this.x = x; 3 | this.y = y; 4 | this.lifeMax = life; 5 | this.life = life; 6 | this.alive = true; 7 | this.color = color; 8 | } 9 | Splode.prototype.draw = function(){ 10 | 11 | r.pat = r.dither[15- Math.floor( (this.life/this.lifeMax) * 15)] 12 | for(let i = Math.floor(this.life/10); i > 0; i--){ 13 | r.circle(this.x-view.x, this.y-view.y, this.lifeMax-this.life-i, this.color); 14 | }r.circle(this.x-view.x, this.y-view.y, this.lifeMax-this.life, this.color); 15 | r.pat = r.dither[0]; 16 | } 17 | Splode.prototype.update = function(){ 18 | if(!this.alive){ 19 | return 20 | } 21 | if(this.life > 0){ 22 | this.life-=1; 23 | } 24 | else { 25 | this.alive = false; 26 | } 27 | } 28 | 29 | export default Splode; --------------------------------------------------------------------------------