├── wav ├── 1.wav ├── 2.wav ├── 3.wav ├── 4.wav ├── 5.wav ├── 6.wav └── s1.wav ├── NotCourierSans.otf ├── README.md ├── .gitattributes ├── index.html ├── .gitignore ├── game.js ├── lib.js ├── circles_2.js └── circles.js /wav/1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/1.wav -------------------------------------------------------------------------------- /wav/2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/2.wav -------------------------------------------------------------------------------- /wav/3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/3.wav -------------------------------------------------------------------------------- /wav/4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/4.wav -------------------------------------------------------------------------------- /wav/5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/5.wav -------------------------------------------------------------------------------- /wav/6.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/6.wav -------------------------------------------------------------------------------- /wav/s1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/wav/s1.wav -------------------------------------------------------------------------------- /NotCourierSans.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARE/circles/master/NotCourierSans.otf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Circles 2 | ======= 3 | 4 | My Ludum Dare 26 entry. Just clone and open `index.html` in your browser or go to https://are1000.github.com/circles/index.html. 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circles v2 5 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /game.js: -------------------------------------------------------------------------------- 1 | function GameController(game, opts) { 2 | var options = opts || {}, 3 | self = this; 4 | EmptyElement.call(this, opts); 5 | 6 | this.current = null; 7 | this.currentName = ""; 8 | this.levels = {}; 9 | this.add = function(name, lvl) { 10 | if (lvl instanceof GameLevel) { 11 | this.levels[name] = lvl; 12 | } 13 | }; 14 | 15 | this.create = function(name, opts) { 16 | this.levels[name] = new GameLevel(opts); 17 | } 18 | 19 | this.score = 0; 20 | this.waves = 0; 21 | 22 | game.controllers.game = this; 23 | 24 | this.load = function(lvl, game, res, cb) { 25 | if (lvl in this.levels) { 26 | this.reset(game); 27 | this.current = this.levels[lvl]; 28 | this.currentName = lvl; 29 | this.waves = this.current.waves; 30 | 31 | if (this.current._descriptor) { 32 | this.current._reload(); 33 | game.elements.push.apply(game.elements, this.current.elements); 34 | } else { 35 | for (var index = 0; index < this.current.elements.length; index += 2) { 36 | var cls = this.current.elements[index], 37 | args = this.current.elements[index + 1]; 38 | game.add(new cls(args)); 39 | } 40 | } 41 | 42 | game.provide(res); 43 | if (cb) cb(game.elements); 44 | } 45 | }; 46 | 47 | this.reset = function(game) { 48 | this.score = 0; 49 | this.waves = 0; 50 | game.elements.filter(function(element) { 51 | return [ WaveElement, NoteElement, TextElement, BlockerElement ].some(function(cls) { 52 | return element instanceof cls; 53 | }); 54 | }).forEach(function(element) { 55 | game.remove(element); 56 | }); 57 | }; 58 | 59 | this.wasPressed = false; 60 | 61 | this.emitWave = function(opts, game) { 62 | var waven = new WaveElement(opts); 63 | waven.on('update', function(time, game) { 64 | if (this.alpha > 0) this.collidingWith(game, NoteElement, function(note) { 65 | note.hit(this.alpha, this, game, function(score) { 66 | self.score += score; 67 | }); 68 | 69 | }); 70 | }); 71 | game.add(waven); 72 | } 73 | 74 | this.on('update', function(time, game) { 75 | var mouse = game.controllers.mouse; 76 | if (!this.wasPressed && mouse.pressed && this.waves > 0) { 77 | var isfree = game.get(BlockerElement).some(function(el) { 78 | return game.buffer.isPointInPath(el.path, mouse.x, mouse.y, 'evenodd'); 79 | }); 80 | 81 | if (!isfree) { 82 | var wave = new WaveElement({ brightness: 100, x: mouse.x, y: mouse.y, force: self.current.wavesPower, speed: self.current.wavesSpeed }); 83 | wave.on('update', function(time, game) { 84 | 85 | if (this.alpha > 0) this.collidingWith(game, NoteElement, function(note) { 86 | 87 | note.hit(this.alpha, this, game, function(score) { 88 | self.score += score; 89 | }); 90 | }); 91 | }); 92 | 93 | 94 | game.add(wave); 95 | this.waves--; 96 | 97 | this.wasPressed = true; 98 | } 99 | } else if (this.wasPressed && !game.controllers.mouse.pressed) { 100 | this.wasPressed = false; 101 | } 102 | 103 | if (32 in game.controllers.keyboard) { 104 | self.load.call(self, self.currentName, game, game.resources) 105 | } 106 | 107 | 108 | if (this.current !== null) { 109 | if (this.score >= this.current.score) 110 | self.load.call(self, self.current.nextLvl, game, game.resources); 111 | 112 | } 113 | }); 114 | 115 | this.on('draw', function(ctx, game) { 116 | var mouse = game.controllers.mouse; 117 | ctx.lineWidth = 1; 118 | ctx.strokeStyle = '#ffffff'; 119 | ctx.beginPath(); 120 | var wavesLeft = (Math.PI * 2); 121 | if (self.current) wavesLeft *= (self.waves / self.current.waves); 122 | 123 | ctx.lineWidth = 2; 124 | ctx.arc(mouse.x, mouse.y, 5, 0, wavesLeft); 125 | ctx.moveTo(mouse.x, mouse.y); 126 | ctx.stroke(); 127 | 128 | ctx.beginPath(); 129 | ctx.lineWidth = 1; 130 | ctx.arc(mouse.x, mouse.y, 1, 0, Math.PI*2); 131 | ctx.stroke(); 132 | }); 133 | } 134 | 135 | 136 | var game = new Circles({ output: 'game-cont' }), 137 | gameController = new GameController(game, {}), 138 | background = new BackgroundElement({ hue: 5, saturation: 100, brightness: ZERO }), 139 | title = new TextElement({ brightness: 100, text: "circles", x: 100, y: 100 }); 140 | 141 | new MouseController(game); 142 | new KeyboardController(game); 143 | 144 | var lvl_start = new GameLevel({ score: 1, waves: 100, power: 25, next: '1', elements: [ 145 | FadingOutTextElement, { x: 50, y: 50, text: 'Circles', font: 'Minimal', fontSize: 50, step: 0.001, brightness: 100, update: function(time, game) { this.centerX(game); } }, 146 | TextElement, { x: 330, y: 270, text: 'This is a note.', font: 'Minimal', fontSize: 16, brightness: 100 }, 147 | NoteElement, { x: 320, y: 240, hits: 1, score: 1, resources: [ 'note1' ], brightness: 50, hue: 2, saturation: 100 } 148 | ] }); 149 | 150 | var lvl_1 = new GameLevel({ score: 5, waves: 5, power: 50, next: '2', elements: [ 151 | TextElement, { x: 200, y: 270, text: 'This is a pentatonic scale.', font: 'Minimal', fontSize: 16, brightness: 100 }, 152 | NoteElement, { x: 160, y: 240, hits: 1, score: 1, resources: [ 'note1' ], brightness: 50, hue: 0, saturation: 100 }, 153 | NoteElement, { x: 240, y: 240, hits: 1, score: 1, resources: [ 'note2' ], brightness: 50, hue: 15, saturation: 100 }, 154 | NoteElement, { x: 320, y: 240, hits: 1, score: 1, resources: [ 'note3' ], brightness: 50, hue: 30, saturation: 100 }, 155 | NoteElement, { x: 400, y: 240, hits: 1, score: 1, resources: [ 'note4' ], brightness: 50, hue: 45, saturation: 100 }, 156 | NoteElement, { x: 480, y: 240, hits: 1, score: 1, resources: [ 'note5' ], brightness: 50, hue: 60, saturation: 100 } 157 | ] }); 158 | 159 | var lvl_2 = new GameLevel({ score: 8, waves: 8, power: 50, speed: 100, next: '3', elements: [ 160 | BlockerElement, { x: 120, y: 200, width: 360, height: 80, alpha: 0.7, brightness: 10, hue: 0, saturation: 50, path: function(path, mode) { 161 | mode.rectOut(path); 162 | } }, 163 | TextElement, { x: 120, y: 300, text: 'No new waves allowed on the red field!', font: 'Minimal', fontSize: 16, brightness: 100 }, 164 | NoteElement, { x: 160, y: 240, hits: 1, score: 1, resources: [ 'note1' ], brightness: 50, hue: 0, saturation: 100 }, 165 | NoteElement, { x: 200, y: 240, hits: 1, score: 1, resources: [ 'note5' ], brightness: 50, hue: 60, saturation: 100 }, 166 | NoteElement, { x: 240, y: 240, hits: 1, score: 1, resources: [ 'note4' ], brightness: 50, hue: 45, saturation: 100 }, 167 | NoteElement, { x: 280, y: 240, hits: 1, score: 1, resources: [ 'note3' ], brightness: 50, hue: 30, saturation: 100 }, 168 | NoteElement, { x: 320, y: 240, hits: 1, score: 1, resources: [ 'note5' ], brightness: 50, hue: 60, saturation: 100 }, 169 | NoteElement, { x: 360, y: 240, hits: 1, score: 1, resources: [ 'note4' ], brightness: 50, hue: 45, saturation: 100 }, 170 | NoteElement, { x: 400, y: 240, hits: 1, score: 1, resources: [ 'note1' ], brightness: 50, hue: 0, saturation: 100 }, 171 | NoteElement, { x: 440, y: 240, hits: 1, score: 1, resources: [ 'note2' ], brightness: 50, hue: 15, saturation: 100 }, 172 | 173 | ] }); 174 | 175 | var lvl_3 = new GameLevel({ score: 4, waves: 4, power: 50, speed: 100, next: '4', elements: [ 176 | BlockerElement, { x: 300, y: 220, radius: 20, fill: 'nonzero', alpha: 0.7, brightness: 10, hue: 0, saturation: 50, path: function(path, mode) { 177 | mode.circleOut(path); 178 | }, update: function(time, game) { 179 | var dx = game.controllers.mouse.x - this.x, 180 | dy = game.controllers.mouse.y - this.y, 181 | dist = Math.sqrt(dx*dx + dy*dy); 182 | 183 | if (dist > this.radius) { 184 | dx /= Math.log10(dist); 185 | dy /= Math.log10(dist); 186 | 187 | dx /= 3; 188 | dy /= 3; 189 | 190 | this.x += dx; 191 | this.y += dy; 192 | } else { 193 | this.x = game.controllers.mouse.x; 194 | this.y = game.controllers.mouse.y; 195 | } 196 | 197 | this.path = new Path2D(); 198 | this.paths.circleOut(this.path); 199 | } }, 200 | NoteElement, { x: 40, y: 40, hits: 1, score: 1, resources: [ 'note1' ], brightness: 50, hue: 0, saturation: 100 }, 201 | NoteElement, { x: 600, y: 40, hits: 1, score: 1, resources: [ 'note5' ], brightness: 50, hue: 60, saturation: 100 }, 202 | NoteElement, { x: 40, y: 440, hits: 1, score: 1, resources: [ 'note4' ], brightness: 50, hue: 45, saturation: 100 }, 203 | NoteElement, { x: 600, y: 440, hits: 1, score: 1, resources: [ 'note3' ], brightness: 50, hue: 30, saturation: 100 } 204 | ] }); 205 | 206 | 207 | game.add(background, title); 208 | game.add(gameController); 209 | 210 | gameController.add('start', lvl_start); 211 | gameController.add('1', lvl_1); 212 | gameController.add('2', lvl_2); 213 | gameController.add('3', lvl_3); 214 | gameController.create('4', { score: 4, waves: 2, power: 80, speed: 40, next: '5', elements: [ 215 | NoteElement, { disabled: true, id: 'n1', x: 100, y: 100, hits: 1, score: 1, resources: ['note1'], brightness: 50, hue: 0, saturation: 100 }, 216 | NoteTransElement, { id: 'n2', target: 'n1', x: 400, y: 100, hits: 1, score: 1, resources: ['note1'], brightness: 50, hue: 0, saturation: 100 }, 217 | NoteTransElement, { disabled: true, id: 'n3', target: 'n3', x: 100, y: 170, hits: 1, score: 1, resources: ['note1'], brightness: 50, hue: 0, saturation: 100 }, 218 | NoteElement, { x: 400, y: 170, hits: 1, score: 1, resources: ['note1'], brightness: 50, hue: 0, saturation: 100 }, 219 | NoteEventElement, { x: 400, y: 400, hits: -1, score: 0, resources: ['hihat1'], brightness: 50, hue: 320, saturation: 100, event: function(force, wave, game) { 220 | var self = this; 221 | game.get(NoteElement).forEach(function(note) { 222 | if (note !== self) { 223 | if (note.disabled) note.disabled = false; 224 | else note.disabled = true; 225 | note.force = 0.1; 226 | } 227 | }); 228 | }, descriptor: function() { 229 | this.on('draw', function(ctx, game) { 230 | 231 | ctx.strokeStyle = this.getColor(); 232 | ctx.beginPath(); 233 | ctx.moveTo(this.x - 4, this.y - 4); 234 | ctx.lineTo(this.x + this.width + 4, this.y - 4); 235 | ctx.lineTo(this.x + this.width + 4, this.y +this.height+ 4); 236 | ctx.lineTo(this.x- 4, this.y +this.height+ 4); 237 | ctx.closePath(); 238 | 239 | ctx.stroke(); 240 | }); 241 | }} 242 | ] }); 243 | 244 | game.load({ hihat1: './wav/s1.wav', note1: './wav/5.wav', note2: './wav/4.wav', note3: './wav/3.wav', note4: './wav/2.wav', note5: './wav/1.wav' }, function(res) { 245 | 246 | 247 | game.start(); 248 | 249 | gameController.load('start', game, res); 250 | }); 251 | 252 | -------------------------------------------------------------------------------- /lib.js: -------------------------------------------------------------------------------- 1 | function rgbToHex(r, g, b) { 2 | return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); 3 | } 4 | owl = (function() { 5 | 6 | // the re-usable constructor function used by clone(). 7 | function Clone() {} 8 | 9 | // clone objects, skip other types. 10 | function clone(target) { 11 | if ( typeof target == 'object' ) { 12 | Clone.prototype = target; 13 | return new Clone(); 14 | } else { 15 | return target; 16 | } 17 | } 18 | 19 | 20 | // Shallow Copy 21 | function copy(target) { 22 | if (typeof target !== 'object' ) { 23 | return target; // non-object have value sematics, so target is already a copy. 24 | } else { 25 | var value = target.valueOf(); 26 | if (target != value) { 27 | // the object is a standard object wrapper for a native type, say String. 28 | // we can make a copy by instantiating a new object around the value. 29 | return new target.constructor(value); 30 | } else { 31 | // ok, we have a normal object. If possible, we'll clone the original's prototype 32 | // (not the original) to get an empty object with the same prototype chain as 33 | // the original. If just copy the instance properties. Otherwise, we have to 34 | // copy the whole thing, property-by-property. 35 | if ( target instanceof target.constructor && target.constructor !== Object ) { 36 | var c = clone(target.constructor.prototype); 37 | 38 | // give the copy all the instance properties of target. It has the same 39 | // prototype as target, so inherited properties are already there. 40 | for ( var property in target) { 41 | if (target.hasOwnProperty(property)) { 42 | c[property] = target[property]; 43 | } 44 | } 45 | } else { 46 | var c = {}; 47 | for ( var property in target ) c[property] = target[property]; 48 | } 49 | 50 | return c; 51 | } 52 | } 53 | } 54 | 55 | // Deep Copy 56 | var deepCopiers = []; 57 | 58 | function DeepCopier(config) { 59 | for ( var key in config ) this[key] = config[key]; 60 | } 61 | DeepCopier.prototype = { 62 | constructor: DeepCopier, 63 | 64 | // determines if this DeepCopier can handle the given object. 65 | canCopy: function(source) { return false; }, 66 | 67 | // starts the deep copying process by creating the copy object. You 68 | // can initialize any properties you want, but you can't call recursively 69 | // into the DeeopCopyAlgorithm. 70 | create: function(source) { }, 71 | 72 | // Completes the deep copy of the source object by populating any properties 73 | // that need to be recursively deep copied. You can do this by using the 74 | // provided deepCopyAlgorithm instance's deepCopy() method. This will handle 75 | // cyclic references for objects already deepCopied, including the source object 76 | // itself. The "result" passed in is the object returned from create(). 77 | populate: function(deepCopyAlgorithm, source, result) {} 78 | }; 79 | 80 | function DeepCopyAlgorithm() { 81 | // copiedObjects keeps track of objects already copied by this 82 | // deepCopy operation, so we can correctly handle cyclic references. 83 | this.copiedObjects = []; 84 | thisPass = this; 85 | this.recursiveDeepCopy = function(source) { 86 | return thisPass.deepCopy(source); 87 | } 88 | this.depth = 0; 89 | } 90 | DeepCopyAlgorithm.prototype = { 91 | constructor: DeepCopyAlgorithm, 92 | 93 | maxDepth: 256, 94 | 95 | // add an object to the cache. No attempt is made to filter duplicates; 96 | // we always check getCachedResult() before calling it. 97 | cacheResult: function(source, result) { 98 | this.copiedObjects.push([source, result]); 99 | }, 100 | 101 | // Returns the cached copy of a given object, or undefined if it's an 102 | // object we haven't seen before. 103 | getCachedResult: function(source) { 104 | var copiedObjects = this.copiedObjects; 105 | var length = copiedObjects.length; 106 | for ( var i=0; i this.maxDepth ) { 163 | throw new Error("Exceeded max recursion depth in deep copy."); 164 | } 165 | 166 | // It's now safe to let the deepCopier recursively deep copy its properties. 167 | deepCopier.populate(this.recursiveDeepCopy, source, result); 168 | 169 | this.depth--; 170 | 171 | return result; 172 | } 173 | }; 174 | 175 | // entry point for deep copy. 176 | // source is the object to be deep copied. 177 | // maxDepth is an optional recursion limit. Defaults to 256. 178 | function deepCopy(source, maxDepth) { 179 | var deepCopyAlgorithm = new DeepCopyAlgorithm(); 180 | if ( maxDepth ) deepCopyAlgorithm.maxDepth = maxDepth; 181 | return deepCopyAlgorithm.deepCopy(source); 182 | } 183 | 184 | // publicly expose the DeepCopier class. 185 | deepCopy.DeepCopier = DeepCopier; 186 | 187 | // publicly expose the list of deepCopiers. 188 | deepCopy.deepCopiers = deepCopiers; 189 | 190 | // make deepCopy() extensible by allowing others to 191 | // register their own custom DeepCopiers. 192 | deepCopy.register = function(deepCopier) { 193 | if ( !(deepCopier instanceof DeepCopier) ) { 194 | deepCopier = new DeepCopier(deepCopier); 195 | } 196 | deepCopiers.unshift(deepCopier); 197 | } 198 | 199 | // Generic Object copier 200 | // the ultimate fallback DeepCopier, which tries to handle the generic case. This 201 | // should work for base Objects and many user-defined classes. 202 | deepCopy.register({ 203 | canCopy: function(source) { return true; }, 204 | 205 | create: function(source) { 206 | if ( source instanceof source.constructor ) { 207 | return clone(source.constructor.prototype); 208 | } else { 209 | return {}; 210 | } 211 | }, 212 | 213 | populate: function(deepCopy, source, result) { 214 | for ( var key in source ) { 215 | if ( source.hasOwnProperty(key) ) { 216 | result[key] = deepCopy(source[key]); 217 | } 218 | } 219 | return result; 220 | } 221 | }); 222 | 223 | // Array copier 224 | deepCopy.register({ 225 | canCopy: function(source) { 226 | return ( source instanceof Array ); 227 | }, 228 | 229 | create: function(source) { 230 | return new source.constructor(); 231 | }, 232 | 233 | populate: function(deepCopy, source, result) { 234 | for ( var i=0; i>> 0) + 2); 5 | }; 6 | 7 | CanvasRenderingContext2D.prototype.clear = function() { 8 | this.save(); 9 | this.clearRect(0, 0, this.canvas.width, this.canvas.height); 10 | this.restore(); 11 | }; 12 | 13 | Number.prototype.clamp = function(min, max) { 14 | return Math.min(Math.max(this, min), max); 15 | }; 16 | 17 | function Circles(opts) { 18 | var options = opts || {}, 19 | self = this; 20 | 21 | this.canvas = { output: document.createElement('canvas'), 22 | buffer: document.createElement('canvas') }; 23 | 24 | this.buffer = this.canvas.buffer.getContext('2d'); 25 | this.output = this.canvas.output.getContext('2d'); 26 | 27 | this.width = this.canvas.buffer.width = this.canvas.output.width = options.width || 640; 28 | this.height = this.canvas.buffer.height = this.canvas.output.height = options.height || 480; 29 | 30 | if (options.output) document.getElementById(options.output).appendChild(this.canvas.output); 31 | 32 | this.controllers = {}; 33 | 34 | this.elements = []; 35 | 36 | this.add = function() { 37 | this.elements.push.apply(this.elements, arguments); 38 | }; 39 | 40 | this.remove = function(element) { 41 | var index = this.elements.indexOf(element); 42 | if (index >= 0) this.elements.splice(index, 1); 43 | }; 44 | 45 | this.get = function(cls) { 46 | return this.elements.filter(function(el) { return el instanceof cls; }); 47 | }; 48 | 49 | this.resources = {}; 50 | 51 | this.load = function(resources, cb) { 52 | var resLoaded = 0, 53 | resCount = 0, 54 | loaded = false; 55 | 56 | for (var src in resources) { 57 | resCount++; 58 | } 59 | 60 | for (var src in resources) { 61 | var ext = resources[src].ext(); 62 | var evt = 'onload'; 63 | if (ext === 'png') this.resources[src] = new Image(); 64 | else if (ext === 'wav' || ext === 'mp3') { 65 | this.resources[src] = new Audio(); 66 | evt = 'oncanplaythrough'; 67 | } 68 | 69 | 70 | this.resources[src][evt] = function(){ 71 | if (++resLoaded >= resCount) { 72 | if (loaded === false) { 73 | cb(self.resources); 74 | loaded = true; 75 | } 76 | } 77 | }; 78 | this.resources[src].src = resources[src]; 79 | } 80 | }; 81 | 82 | this.provide = function(resources) { 83 | this.elements.forEach(function(element) { 84 | element.provide(resources); 85 | }); 86 | }; 87 | 88 | this.time = { initialized: false, 89 | start: 0, 90 | current: 0, 91 | elapsed: 0, 92 | ticks: 0, 93 | lastTick: 0, 94 | lastTickDuration: 0 }; 95 | 96 | this.update = function() { 97 | this.elements.forEach(function(element) { 98 | element.update(self.time, self); 99 | }); 100 | }; 101 | 102 | this.draw = function() { 103 | this.elements.forEach(function(element) { 104 | element.draw(self.buffer, self); 105 | }); 106 | }; 107 | 108 | this.start = this.loop = function() { 109 | requestAnimFrame(self.loop); 110 | 111 | if (!self.time.initialized) { 112 | self.time.initialized = true; 113 | self.time.start = performance.now(); 114 | self.time.lastTick = self.time.current = performance.now(); 115 | self.time.elapsed = self.time.current - self.time.start; 116 | self.time.ticks++; 117 | self.time.lastTickDuration = self.time.current - self.time.lastTick; 118 | } else { 119 | self.time.lastTick = self.time.current; 120 | self.time.current = performance.now(); 121 | self.time.elapsed = self.time.current - self.time.start; 122 | self.time.ticks++; 123 | self.time.lastTickDuration = self.time.current - self.time.lastTick; 124 | } 125 | 126 | 127 | 128 | self.update(); 129 | self.draw(); 130 | 131 | self.output.drawImage(self.canvas.buffer, 0, 0); 132 | }; 133 | } 134 | 135 | 136 | function MouseController(game) { 137 | var self = this; 138 | 139 | this.x = -1000; 140 | this.y = -1000; 141 | this.wasPressed = false; 142 | this.pressed = false; 143 | this.present = false; 144 | 145 | 146 | game.canvas.output.addEventListener('mousemove', function(evt) { 147 | var rect = game.canvas.output.getBoundingClientRect(); 148 | self.x = Math.round(evt.clientX - rect.left); 149 | self.y = Math.round(evt.clientY - rect.top); 150 | 151 | self.present = true; 152 | }); 153 | 154 | game.canvas.output.addEventListener('mousedown', function(evt) { 155 | self.pressed = true; 156 | self.present = true; 157 | }); 158 | 159 | game.canvas.output.addEventListener('mouseup', function(evt) { 160 | self.pressed = false; 161 | self.present = true; 162 | }); 163 | 164 | game.canvas.output.addEventListener('mouseleave', function(evt) { 165 | self.present = false; 166 | }); 167 | 168 | game.canvas.output.addEventListener('mouseenter', function(evt) { 169 | self.present = true; 170 | }); 171 | 172 | game.controllers.mouse = this; 173 | } 174 | 175 | function KeyboardController(game) { 176 | var self = this; 177 | 178 | window.addEventListener('keyup', function(evt) { 179 | delete self[evt.keyCode]; 180 | }); 181 | 182 | window.addEventListener('keydown', function(evt) { 183 | self[evt.keyCode] = true; 184 | }); 185 | 186 | game.controllers.keyboard = this; 187 | } 188 | 189 | var ZERO = 0.0001, 190 | STOP = Math.random() * 999999999; 191 | 192 | function EmptyElement(opts) { 193 | var options = opts || {}, 194 | self = this; 195 | 196 | this.x = options.x || 0; 197 | this.y = options.y || 0; 198 | this.width = options.width || 0; 199 | this.height = options.height || 0; 200 | 201 | this.collisions = {}; 202 | this.collisions.point = function(x, y) { 203 | return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height; 204 | }; 205 | 206 | this._update = []; 207 | this._draw = []; 208 | 209 | this.on = function(action, cb) { 210 | if (action in this && ('_'+action) in this) this['_'+action].push(cb); 211 | }; 212 | 213 | 214 | this.register = function(evt) { 215 | this['_'+evt] = []; 216 | this[evt] = function() { 217 | for (var index = 0; index < this['_'+evt].length; index++) { 218 | var callback = this['_'+evt][index]; 219 | var result = callback.apply(self, Array.prototype.slice.call(arguments)); 220 | if (result === STOP) break; 221 | } 222 | }; 223 | }; 224 | 225 | this.update = function(time, game) { 226 | for (var index = 0; index < this._update.length; index++) { 227 | var callback = this._update[index]; 228 | var result = callback.call(self, time, game); 229 | if (result === STOP) break; 230 | } 231 | }; 232 | 233 | this.draw = function(ctx, game) { 234 | for (var index = 0; index < this._draw.length; index++) { 235 | var callback = this._draw[index]; 236 | var result = callback.call(self, ctx, game); 237 | if (result === STOP) break; 238 | } 239 | }; 240 | 241 | if (options.update) this.on('update', options.update); 242 | if (options.draw) this.on('draw', options.draw); 243 | 244 | if (options.descriptor) options.descriptor.call(this); 245 | 246 | 247 | this.provide = function(resources) { 248 | if (this._resources) { 249 | this.resources = {}; 250 | this._resources.forEach(function(resource){ 251 | self.resources[resource] = resources[resource]; 252 | }); 253 | } 254 | } 255 | } 256 | 257 | function ColorExtension(opts) { 258 | var options = opts || {}, 259 | self = this; 260 | 261 | this.hue = options.hue || 0; 262 | this.saturation = options.saturation || 0; 263 | this.brightness = options.brightness || 0; 264 | this.alpha = options.alpha || 1; 265 | 266 | this.getColor = function(h, s, b, a) { 267 | return 'hsla(' + (h || this.hue) + ', ' + (s || this.saturation) + '%, ' + (b || this.brightness) + '%, ' + (a || this.alpha) + ')'; 268 | }; 269 | } 270 | 271 | function ResourceExtension(opts) { 272 | var options = opts || {}, 273 | self = this; 274 | 275 | this._resources = opts.resources || []; 276 | } 277 | 278 | function BackgroundElement(opts) { 279 | var options = opts || {}, 280 | self = this; 281 | EmptyElement.call(this, opts); 282 | ColorExtension.call(this, opts); 283 | 284 | this.on('draw', function(ctx, game) { 285 | ctx.fillStyle = 'hsl(' + this.hue + ', ' + this.saturation + '%, ' + this.brightness + '%)'; 286 | ctx.fillRect(0, 0, game.width, game.height); 287 | }); 288 | } 289 | 290 | function TextElement(opts) { 291 | var options = opts || {}, 292 | self = this; 293 | EmptyElement.call(this, opts); 294 | ColorExtension.call(this, opts); 295 | 296 | this.text = options.text || ""; 297 | this.font = options.font || 'Courier New'; 298 | this.fontSize = options.fontSize || 12; 299 | 300 | this.centerX = function(game) { 301 | this.x = game.width / 2 - this.measure(game.buffer, this.text) / 2; 302 | }; 303 | 304 | this.centerY = function(game) { 305 | this.y = game.height / 2 - this.fontSize / 2; 306 | }; 307 | 308 | this.measure = function(ctx, text) { 309 | ctx.font = this.getFont(); 310 | return ctx.measureText(text).width; 311 | }; 312 | 313 | this.getFont = function() { 314 | return this.fontSize + 'px ' + this.font; 315 | }; 316 | 317 | this.on('draw', function(ctx, game) { 318 | ctx.font = this.getFont(); 319 | ctx.fillStyle = this.getColor(); 320 | ctx.fillText(this.text, this.x, this.y); 321 | }); 322 | } 323 | 324 | function FadingOutTextElement(opts) { 325 | var options = opts || {}, 326 | self = this; 327 | TextElement.call(this, opts); 328 | 329 | this.step = options.step || 0.01; 330 | 331 | this.refresh = function(lvl) { 332 | this.alpha = lvl || 1; 333 | }; 334 | 335 | this.on('update', function(time, game) { 336 | if (this.alpha > 0) this.alpha -= this.step; 337 | else this.alpha = 0; 338 | }); 339 | } 340 | 341 | FadingOutTextElement.prototype = TextElement.prototype; 342 | 343 | function NoteElement(opts) { 344 | var options = opts || {}, 345 | self = this; 346 | EmptyElement.call(this, opts); 347 | ColorExtension.call(this, opts); 348 | ResourceExtension.call(this, opts); 349 | 350 | this.width = this.height = options.size || 5; 351 | this.resourceName = this._resources[0] || ''; 352 | 353 | this.score = options.score || 0; 354 | this._hits = options.hits || 0; 355 | this.hits = options.hits || 0; 356 | 357 | this.id = options.id || ''; 358 | 359 | this.force = false; 360 | this.disabled = options.disabled || false; 361 | 362 | this.register('hit'); 363 | this.register('hitSuccess'); 364 | 365 | this.on('hit', function(force, wave, game, cb) { 366 | if ((this.hits > 0 || this.hits === -1) && this.force === false && this.disabled === false) { 367 | this.hitSuccess(force, wave, game, cb); 368 | return this.score; 369 | } else { 370 | return false; 371 | } 372 | }); 373 | 374 | this.on('hitSuccess', function(force, wave, game, cb) { 375 | if (this.hits > 0) this.hits--; 376 | this.resources[this.resourceName].volume = force; 377 | this.resources[this.resourceName].currentTime = 0; 378 | this.resources[this.resourceName].play(); 379 | this.force = force; 380 | cb(this.score); 381 | }); 382 | 383 | this.on('update', function(time, game) { 384 | if (this.force) this.force -= this.force/3; 385 | if (this.force < 0.01) this.force = false; 386 | }); 387 | 388 | this.on('draw', function(ctx, game) { 389 | if (this.force || this.hits === 0 || this.disabled) { 390 | ctx.fillStyle = this.getColor(undefined, ZERO); 391 | ctx.fillRect(this.x - this.force*8, this.y - this.force*8, this.width + this.force*16, this.height + this.force*16); 392 | } else { 393 | ctx.fillStyle = this.getColor(); 394 | ctx.fillRect(this.x, this.y, this.width, this.height); 395 | } 396 | }); 397 | } 398 | 399 | function NoteEventElement(opts) { 400 | var options = opts || {}, 401 | self = this; 402 | NoteElement.call(this, opts); 403 | 404 | this.on('hitSuccess', opts.event); 405 | 406 | } 407 | 408 | NoteEventElement.prototype = NoteElement.prototype; 409 | 410 | function NoteEmitElement(opts) { 411 | var options = opts || {}, 412 | self = this; 413 | NoteElement.call(this, opts); 414 | 415 | this.on('hitSuccess', function(force, wave, game, cb) { 416 | game.controllers.game.emitWave({ brightness: 100, x: self.x+self.width/2, y: self.y+self.height/2, force: wave.force, speed: wave.speed }, game); 417 | }); 418 | 419 | } 420 | 421 | NoteEmitElement.prototype = NoteElement.prototype; 422 | 423 | function NoteTransElement(opts) { 424 | var options = opts || {}, 425 | self = this; 426 | NoteElement.call(this, opts); 427 | 428 | this.id = options.id || ''; 429 | this.target = options.target || ''; 430 | 431 | this.on('hitSuccess', function(force, wave, game, cb) { 432 | game.get(NoteElement).filter(function(note) { 433 | return note.id === self.target; 434 | }).forEach(function(targets) { 435 | console.log(targets); 436 | game.controllers.game.emitWave({ brightness: 100, x: targets.x+targets.width/2, y: targets.y+targets.height/2, force: wave.force, speed: wave.speed }, game); 437 | }); 438 | }); 439 | } 440 | 441 | NoteTransElement.prototype = NoteElement.prototype; 442 | 443 | function WaveElement(opts) { 444 | var options = opts || {}, 445 | self = this; 446 | EmptyElement.call(this, opts); 447 | ColorExtension.call(this, opts); 448 | 449 | this.force = opts.force || 50; 450 | this.speed = opts.speed || 10; 451 | this.elapsed = 0; 452 | this.radius = 0; 453 | 454 | this.disabled = false; 455 | 456 | this.collisions.point = function(x, y, margin) { 457 | var dx = self.x - x, 458 | dy = self.y - y; 459 | 460 | var dist = Math.sqrt(dx*dx + dy*dy); 461 | var abs = Math.abs(dist - self.radius); 462 | 463 | return abs < margin; 464 | }; 465 | 466 | this.collidingWith = function(game, cls, cb) { 467 | return game.elements.filter(function(element) { 468 | if (element instanceof cls) { 469 | var center = { x: element.x + element.width/2, y: element.y + element.height/2 }; 470 | var margin = self.radius - ((self.elapsed - game.time.lastTickDuration) / 1000 * self.speed); 471 | if (self.collisions.point(center.x, center.y, margin)) { 472 | cb.call(self, element, margin, Math.sqrt(Math.pow(self.x - element.x, 2) + Math.pow(self.y - element.y, 2))); 473 | } 474 | } 475 | }); 476 | }; 477 | 478 | this.on('update', function(time, game) { 479 | if (!this.disabled) { 480 | this.elapsed += time.lastTickDuration; 481 | this.radius = this.elapsed / 1000 * this.speed; 482 | 483 | var alpha = this.radius / this.force; 484 | 485 | if (alpha > 1) { this.disabled = true; this.alpha = 0; } 486 | else { 487 | this.alpha = 1 - alpha; 488 | } 489 | } else { 490 | game.remove(self); 491 | } 492 | }); 493 | 494 | this.on('draw', function(ctx, game) { 495 | if (!this.disabled) { 496 | ctx.strokeStyle = this.getColor(); 497 | ctx.beginPath(); 498 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); 499 | ctx.stroke(); 500 | } 501 | }); 502 | } 503 | 504 | function BlockerElement(opts) { 505 | var options = opts || {}, 506 | self = this; 507 | EmptyElement.call(this, opts); 508 | ColorExtension.call(this, opts); 509 | 510 | this.radius = options.radius || 0; 511 | this.fill = options.fill || 'evenodd'; 512 | 513 | this.path = new Path2D(); 514 | 515 | this.paths = { 516 | circularIn: function(path) { 517 | path.moveTo(0, 0); 518 | path.lineTo(game.width, 0); 519 | path.lineTo(game.width, this.y); 520 | path.lineTo(this.x + this.radius, this.y); 521 | path.arc(this.x, this.y, this.radius, 0, Math.PI*2); 522 | path.lineTo(game.width, this.y); 523 | path.lineTo(game.width, game.height); 524 | path.lineTo(0, game.height); 525 | path.closePath(); 526 | }, 527 | circleOut: function(path) { 528 | path.moveTo(self.x, self.y); 529 | path.lineTo(self.x+self.radius, self.y); 530 | path.arc(self.x, self.y, self.radius, 0, Math.PI*2-0.001); 531 | path.closePath(); 532 | }, 533 | rectOut: function(path) { 534 | path.moveTo(self.x, self.y); 535 | path.lineTo(self.x+self.width, self.y); 536 | path.lineTo(self.x+self.width, self.y+self.height); 537 | path.lineTo(self.x, self.y+self.height); 538 | path.closePath(); 539 | } 540 | } 541 | 542 | if (options.path) options.path(this.path, this.paths); 543 | 544 | 545 | 546 | this.on('draw', function(ctx, game) { 547 | ctx.save(); 548 | 549 | ctx.fillStyle = this.getColor(); 550 | ctx.fill(this.path, this.fill); 551 | 552 | ctx.restore(); 553 | }); 554 | } 555 | 556 | function GameLevel(opts) { 557 | var options = opts || {}, 558 | self = this; 559 | 560 | this.elements = options.elements || []; 561 | this.score = options.score || 1; 562 | this.waves = options.waves || -1; 563 | this.wavesPower = options.power || 50; 564 | this.wavesSpeed = options.speed || 20; 565 | this.nextLvl = options.next || ''; 566 | 567 | 568 | if (options.descriptor) { 569 | this.elements = []; 570 | options.descriptor.call(this, this.elements); 571 | this._descriptor = true; 572 | this._descriptorF = options.descriptor; 573 | 574 | this._reload = function() { 575 | this._descriptorF.call(self); 576 | } 577 | } 578 | } 579 | 580 | -------------------------------------------------------------------------------- /circles.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | function( callback ){ 6 | window.setTimeout(callback, 1000 / 60); 7 | }; 8 | })(); 9 | 10 | 11 | function CirclesGame (width, height) { 12 | this.width = width; 13 | this.height = height; 14 | this.canvas = document.createElement("canvas"); 15 | this.canvas.width = this.width; 16 | this.canvas.height = this.height; 17 | this.buffer = this.canvas.getContext("2d"); 18 | this.ocanvas = null; 19 | this.output = null; 20 | 21 | this.fps = 60; 22 | this.looptime = 0; 23 | this.settings = {}; 24 | 25 | this.SetScene = function (canvasId) { 26 | this.ocanvas = document.getElementById(canvasId); 27 | this.ocanvas.width = this.width; 28 | this.ocanvas.height = this.height; 29 | this.output = this.ocanvas.getContext("2d"); 30 | return this.ocanvas; 31 | } 32 | 33 | this.DrawScene = function (shift_x, shift_y) { 34 | this.output.globalAlpha = 0.75; 35 | this.output.drawImage(this.canvas,shift_x,shift_y); 36 | 37 | /*var imgd = this.buffer.getImageData(0, 0, 640, 480); 38 | var data = imgd.data; 39 | 40 | for (var i = 0, n = data.length; i < n; i += 4) { 41 | 42 | // generating random color coefficients 43 | var randColor1 = 0.6 + Math.random() * 0.4; 44 | var randColor2 = 0.6 + Math.random() * 0.4; 45 | var randColor3 = 0.6 + Math.random() * 0.4; 46 | 47 | // assigning random colors to our data 48 | data[i] = data[i]*randColor1; // green 49 | data[i+1] = data[i+1]*randColor2; // green 50 | data[i+2] = data[i+2]*randColor3; // blue 51 | } 52 | var newcont = document.createElement("canvas"); 53 | newcont.width = 640; 54 | newcont.height = 480; 55 | var abba = newcont.getContext("2d"); 56 | abba.putImageData(imgd, 0, 0); 57 | 58 | this.output.drawImage(newcont,0,0);*/ 59 | 60 | 61 | } 62 | 63 | this.ClearBuffer = function () { 64 | this.buffer.save(); 65 | var old = this.buffer.fillStyle= "#000000"; 66 | this.buffer.fillRect(0,0,this.canvas.width,this.canvas.height); 67 | this.buffer.restore(); 68 | } 69 | 70 | this.Logic = function () { 71 | 72 | } 73 | 74 | } 75 | 76 | //USER INPUT ************* 77 | function mouse(obj, evt) { 78 | var rect = obj.getBoundingClientRect(); 79 | return { 80 | x: evt.clientX - rect.left, 81 | y: evt.clientY - rect.top 82 | }; 83 | } 84 | 85 | var keyboard = {}; 86 | addEventListener("keydown", function (e) { 87 | keyboard[e.keyCode] = true; 88 | }, false); 89 | addEventListener("keyup", function (e) { 90 | delete keyboard[e.keyCode]; 91 | }, false); 92 | //************************ 93 | 94 | var game = new CirclesGame(640,480); 95 | game.SetScene("circles"); 96 | 97 | 98 | //*****************GLOBAL SETTINGS************** 99 | game.buffer.shadowBlur = 20; 100 | game.buffer.shadowColor = "rgba(255,255,255,0.4)"; 101 | game.buffer.webkitImageSmoothingEnabled = true; 102 | game.fps = 60; 103 | 104 | const TYPE_NOTE = 1; 105 | const TYPE_EMIT = 2; 106 | const TYPE_ABSR = 3; 107 | const TYPE_TIME = 4; 108 | const TYPE_RESE = 5; 109 | const TYPE_LABE = 6; 110 | const TYPE_ANCH = 7 111 | 112 | const CURR_WAVE = 0; 113 | const CURR_EMIT = 1; 114 | //-----------------GLOBAL SETTINGS------------- 115 | 116 | //****************GAME OBJECTS******************* 117 | var cursor = { 118 | x: 0, 119 | y: 0, 120 | pressed: false, 121 | current: 0 122 | } 123 | 124 | const colors = randomColors(64); 125 | 126 | function randomColors(total) { 127 | var i = 360 / (total - 1); // distribute the colors evenly on the hue range 128 | var r = []; // hold the generated colors 129 | for (var x=0; x0) { 303 | Settings.wavesLeft--; 304 | Settings.gameElements.circle.push(new Circle(mousevn.x, mousevn.y, Settings.cursorSize, 1)); 305 | } 306 | break; 307 | case CURR_EMIT: 308 | if(Settings.emittersLeft>0) { 309 | Settings.emittersLeft--; 310 | Settings.gameElements.block.push(new Block(mousevn.x, mousevn.y, 0, TYPE_EMIT, {blocked: false, cycle: 1, wave: 1, once: true})); 311 | } 312 | break; 313 | } 314 | } 315 | } 316 | game.ocanvas.ontouchstart = function (e) { 317 | var mousevn = mouse(this,e); 318 | if(Settings.wavesLeft>0) { 319 | Settings.wavesLeft--; 320 | Settings.gameElements.circle.push(new Circle(mousevn.x, mousevn.y, Settings.cursorSize, 1)); 321 | } 322 | cursor.pressed = true; 323 | } 324 | game.ocanvas.onmouseup = function (e) { 325 | cursor.pressed = false; 326 | } 327 | game.ocanvas.ontouchend = function (e) { 328 | cursor.pressed = false; 329 | } 330 | game.ocanvas.oncontextmenu = function (e) { 331 | return false; 332 | } 333 | //KEYBOARD##################################### 334 | if(32 in keyboard) { 335 | Settings.LoadLevel(Levels[Settings.levelNumber]); 336 | Settings.multiplerScore = 1; 337 | Settings.bufferScore = 0; 338 | } 339 | 340 | if(49 in keyboard) cursor.current = CURR_WAVE; 341 | else if (50 in keyboard) cursor.current = CURR_EMIT; 342 | //REST%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 343 | 344 | 345 | 346 | 347 | for (i in Settings.gameElements.circle) { 348 | if (Settings.gameElements.circle[i]!=null) { 349 | game.buffer.save(); 350 | game.buffer.beginPath(); 351 | game.buffer.strokeStyle="#fff"; 352 | game.buffer.globalAlpha= Settings.gameElements.circle[i].opacity; 353 | game.buffer.arc(Settings.gameElements.circle[i].x,Settings.gameElements.circle[i].y,Settings.gameElements.circle[i].r,0,2*Math.PI); 354 | game.buffer.stroke(); 355 | game.buffer.restore(); 356 | 357 | for (n in Settings.gameElements.block) { 358 | var distance = Math.sqrt(Math.pow(Settings.gameElements.block[n].x - Settings.gameElements.circle[i].x,2) + Math.pow(Settings.gameElements.block[n].y- Settings.gameElements.circle[i].y,2))-Settings.gameElements.circle[i].r-Settings.gameElements.block[n].r; 359 | switch (Settings.gameElements.block[n].type) { 360 | case TYPE_NOTE: 361 | if (distance<0.5&&distance>-0.5&&!Settings.gameElements.block[n].settings.blocked&&Settings.goal>0) { 362 | Settings.gameElements.block[n].settings.audio.volume = ((Settings.gameElements.circle[i].opacity>=0.2) ? Settings.gameElements.circle[i].opacity : 0.2); 363 | Settings.gameElements.block[n].settings.audio.currentTime = 0; 364 | Settings.gameElements.block[n].settings.audio.play(); 365 | Settings.gameElements.block[n].settings.isplay = true; 366 | $('body').stop().animate({backgroundColor: Settings.gameElements.block[n].settings.color},500); 367 | if(Settings.gameElements.block[n].settings.once) Settings.gameElements.block[n].settings.blocked = true; 368 | if(Settings.gameElements.block[n].settings.goalable) Settings.goal--; 369 | } 370 | break; 371 | case TYPE_EMIT: 372 | if (distance<0.5&&distance>-0.5&&!Settings.gameElements.block[n].settings.blocked&&Settings.gameElements.block[n].settings.cycle>=1){ 373 | console.log("H"); 374 | Settings.gameElements.circle.push(new Circle(Settings.gameElements.block[n].x,Settings.gameElements.block[n].y,Settings.gameElements.block[n].settings.wave,1)); 375 | if(Settings.gameElements.block[n].settings.once) Settings.gameElements.block[n].settings.blocked = true; 376 | else Settings.gameElements.block[n].settings.cycle = 0; 377 | } 378 | break; 379 | case TYPE_ANCH: 380 | if (distance<0.5&&distance>-0.5){ 381 | Settings.LoadLevel(Levels[Settings.gameElements.block[n].settings.level]); 382 | } 383 | break; 384 | } 385 | } 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | if (Settings.gameElements.circle[i].cycle > 0) { 395 | Settings.gameElements.circle[i].cycle-=Settings.gameElements.circle[i].step; 396 | Settings.gameElements.circle[i].r++; 397 | Settings.gameElements.circle[i].opacity -=Settings.gameElements.circle[i].step/100; 398 | if (Settings.gameElements.circle[i].opacity <= 0) Settings.gameElements.circle[i].opacity = 0; 399 | } else if (Settings.gameElements.circle[i].cycle <= 0) { 400 | Settings.gameElements.circle[i] = null; 401 | } 402 | } 403 | }; 404 | 405 | for (n in Settings.gameElements.block) { 406 | game.buffer.save(); 407 | switch (Settings.gameElements.block[n].type) { 408 | case TYPE_NOTE: 409 | if(Settings.gameElements.block[n].settings.blocked) game.buffer.fillStyle = "#999999"; 410 | else { 411 | game.buffer.fillStyle = colors[Settings.gameElements.block[n].x%colors.length]; 412 | Settings.gameElements.block[n].settings.color = game.buffer.fillStyle; 413 | } 414 | if (Settings.gameElements.block[n].settings.isplay) { 415 | game.buffer.fillRect(Settings.gameElements.block[n].x-2,Settings.gameElements.block[n].y-2,4,4); 416 | Settings.gameElements.block[n].settings.isplay = false; 417 | } else { 418 | game.buffer.fillRect(Settings.gameElements.block[n].x-4,Settings.gameElements.block[n].y-4,8,8); 419 | } 420 | break; 421 | case TYPE_LABE: 422 | var distance = Math.abs(Settings.gameElements.block[n].x-cursor.x)+Math.abs(Settings.gameElements.block[n].y-cursor.y); 423 | Settings.gameElements.block[n].settings.opacity = 0.7; 424 | game.buffer.globalAlpha = Settings.gameElements.block[n].settings.opacity; 425 | game.buffer.fillStyle = "white"; 426 | game.buffer.font = Settings.gameElements.block[n].settings.size+"px Minimal"; 427 | game.buffer.textBaseline = "top"; 428 | game.buffer.textAlign = "center"; 429 | game.buffer.fillText(Settings.gameElements.block[n].settings.text,Settings.gameElements.block[n].x,Settings.gameElements.block[n].y); 430 | break; 431 | case TYPE_ANCH: 432 | game.buffer.fillStyle = "yellow"; 433 | game.buffer.strokeStyle = "cyan"; 434 | game.buffer.beginPath(); 435 | game.buffer.rect(Settings.gameElements.block[n].x-2,Settings.gameElements.block[n].y-2,4,4); 436 | game.buffer.fill(); 437 | game.buffer.lineWidth = 1; 438 | game.buffer.stroke(); 439 | break; 440 | case TYPE_EMIT: 441 | if(!Settings.gameElements.block[n].settings.once) { 442 | game.buffer.beginPath(); 443 | if(Settings.gameElements.block[n].settings.cycle<1) Settings.gameElements.block[n].settings.cycle += Settings.gameElements.block[n].settings.speed/100; 444 | else Settings.gameElements.block[n].settings.cycle = 1; 445 | game.buffer.fillStyle="white"; 446 | game.buffer.strokeStyle = 'white'; 447 | game.buffer.lineWidth = 2; 448 | game.buffer.arc(Settings.gameElements.block[n].x, Settings.gameElements.block[n].y, 5, 0, 2 * Math.PI*Settings.gameElements.block[n].settings.cycle, false); 449 | game.buffer.stroke(); 450 | } else { 451 | if(Settings.gameElements.block[n].settings.blocked) { 452 | game.buffer.beginPath(); 453 | game.buffer.fillStyle="#888888"; 454 | game.buffer.strokeStyle = '#888888'; 455 | game.buffer.lineWidth = 1; 456 | game.buffer.rect(Settings.gameElements.block[n].x-2, Settings.gameElements.block[n].y-2, 4, 4); 457 | game.buffer.stroke(); 458 | game.buffer.fill(); 459 | } else { 460 | game.buffer.beginPath(); 461 | game.buffer.fillStyle="white"; 462 | game.buffer.strokeStyle = 'white'; 463 | game.buffer.lineWidth = 1; 464 | game.buffer.rect(Settings.gameElements.block[n].x-3, Settings.gameElements.block[n].y-3, 6, 6); 465 | game.buffer.stroke(); 466 | game.buffer.fill(); 467 | } 468 | } 469 | break; 470 | } 471 | 472 | 473 | game.buffer.restore(); 474 | } 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | Settings.Check(); 484 | 485 | 486 | game.buffer.save(); 487 | if (90 in keyboard) { 488 | game.buffer.save(); 489 | game.buffer.beginPath(); 490 | game.buffer.strokeStyle="red"; 491 | game.buffer.globalAlpha= 0.2; 492 | game.buffer.arc(cursor.x,cursor.y,100/Settings.cursorSize,0,2*Math.PI); 493 | game.buffer.stroke(); 494 | game.buffer.restore(); 495 | } 496 | 497 | var quan = 1; 498 | game.buffer.fillStyle = "white"; 499 | 500 | 501 | if(cursor.current==CURR_WAVE) { 502 | game.buffer.lineWidth = 2; 503 | quan = Settings.wavesLeft/Levels[Settings.levelNumber].wavesLeft; 504 | game.buffer.beginPath(); 505 | game.buffer.strokeStyle=game.buffer.fillStyle; 506 | game.buffer.arc(cursor.x,cursor.y,5,0,2*Math.PI*quan); 507 | game.buffer.stroke(); 508 | game.buffer.beginPath(); 509 | game.buffer.strokeStyle=game.buffer.fillStyle; 510 | if(cursor.pressed) { 511 | game.buffer.lineWidth = 2; 512 | } else { 513 | game.buffer.lineWidth = 1; 514 | } 515 | game.buffer.arc(cursor.x,cursor.y,1,0,2*Math.PI); 516 | game.buffer.fill(); 517 | game.buffer.stroke(); 518 | } 519 | else if (cursor.current==CURR_EMIT) { 520 | quan = Settings.emittersLeft/Levels[Settings.levelNumber].emittersLeft; 521 | game.buffer.beginPath(); 522 | game.buffer.strokeStyle=game.buffer.fillStyle; 523 | game.buffer.rect(cursor.x-4,cursor.y-4,8,8); 524 | game.buffer.stroke(); 525 | game.buffer.fillRect(cursor.x-4,cursor.y-4,8*quan,8); 526 | } 527 | 528 | game.buffer.textBaseline = "top"; 529 | game.buffer.textAlign = "center"; 530 | game.buffer.shadowBlur = 0; 531 | game.buffer.fillStyle="white"; 532 | game.buffer.font="60px Minimal"; 533 | 534 | if(Fader.fade>0) { 535 | Fader.fade-=0.5; 536 | Fader.opac = Fader.fade/100; 537 | game.buffer.globalAlpha = Fader.opac; 538 | game.buffer.fillText(Settings.levelName,320,80); 539 | game.buffer.font="12px Minimal"; 540 | game.buffer.fillText(Settings.levelDesc,320,50); 541 | } 542 | 543 | game.buffer.textBaseline = "top"; 544 | game.buffer.textAlign = "left"; 545 | game.buffer.shadowBlur = 0; 546 | game.buffer.fillStyle="white"; 547 | game.buffer.globalAlpha = 0.9; 548 | game.buffer.font="60px Minimal"; 549 | game.buffer.fillText(Settings.levelNumber,20,0); 550 | 551 | game.buffer.textBaseline = "bottom"; 552 | game.buffer.textAlign = "right"; 553 | 554 | game.buffer.save(); 555 | game.buffer.font="14px Minimal"; 556 | if(Settings.goal<33) { 557 | for (var i = 0; i < Settings.goal; i++) { 558 | game.buffer.fillStyle = "white"; 559 | game.buffer.fillRect(i*20,game.canvas.height-5,20,3); 560 | } 561 | game.buffer.fillText(Settings.goal,Settings.goal*20,game.canvas.height); 562 | } else { 563 | for (var i = 0; i < Settings.goal; i++) { 564 | game.buffer.fillStyle = "white"; 565 | game.buffer.fillRect(game.canvas.width/Settings.goal*i,game.canvas.height-5,game.canvas.width/Settings.goal,3); 566 | } 567 | game.buffer.fillText(Settings.goal,game.canvas.width,game.canvas.height); 568 | } 569 | 570 | game.buffer.restore(); 571 | 572 | game.buffer.restore(); 573 | game.DrawScene(0, 0, true); 574 | game.ClearBuffer(); 575 | //game LOGIC HERE 576 | } 577 | 578 | //Settings.LoadLevel(0); 579 | game.Logic(); --------------------------------------------------------------------------------