├── .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;
--------------------------------------------------------------------------------