Browse Source

Site updated: 2020-04-11 10:37:41

schtonn 5 years ago
parent
commit
f25f84d022

File diff suppressed because it is too large
+ 0 - 0
archives/2020/03/index.html


File diff suppressed because it is too large
+ 0 - 0
archives/2020/index.html


File diff suppressed because it is too large
+ 0 - 0
archives/index.html


+ 80 - 0
core/css/css

@@ -0,0 +1,80 @@
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Montserrat Bold'), local('Montserrat-Bold'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_dJE3gTD_u50.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Montserrat Bold'), local('Montserrat-Bold'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_dJE3g3D_u50.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* vietnamese */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Montserrat Bold'), local('Montserrat-Bold'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_dJE3gbD_u50.woff2) format('woff2');
+  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Montserrat Bold'), local('Montserrat-Bold'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_dJE3gfD_u50.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Montserrat Bold'), local('Montserrat-Bold'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_dJE3gnD_g.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 900;
+  src: local('Montserrat Black'), local('Montserrat-Black'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_epG3gTD_u50.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 900;
+  src: local('Montserrat Black'), local('Montserrat-Black'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_epG3g3D_u50.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* vietnamese */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 900;
+  src: local('Montserrat Black'), local('Montserrat-Black'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_epG3gbD_u50.woff2) format('woff2');
+  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 900;
+  src: local('Montserrat Black'), local('Montserrat-Black'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_epG3gfD_u50.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 900;
+  src: local('Montserrat Black'), local('Montserrat-Black'), url(https://fonts.gstatic.com/s/montserrat/v14/JTURjIg1_i6t8kCHKm45_epG3gnD_g.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}

+ 1 - 0
core/css/main.css

@@ -0,0 +1 @@
+#status,.ui{padding:8px}#panels,#status{position:absolute}#message p,.ui h2{margin-bottom:20px}body,html{overflow:hidden;font-family:Helvetica,Arial,sans-serif;color:#fff;font-size:11px;background:#010c12}.no-canvas{color:#999;font-size:24px;text-align:center;margin-top:150px}canvas{border:6px solid #333;cursor:crosshair;z-index:1}#background{border:none;z-index:0}.ui{font-family:Arial,Helvetica,sans-serif;font-size:10px;color:#999;text-align:left;background-color:rgba(0,0,0,.4);float:left}#status{width:884px;height:15px;display:none;z-index:2}#status span{color:#bbb;font-weight:700;margin-right:5px}#status .fps{float:right}#panels{width:100%;z-index:3}#message{padding:150px 300px 0 60px;width:100%;height:100%;box-sizing:border-box;font-family:Montserrat,Helvetica,Arial,sans-serif;font-size:20px;text-transform:uppercase;line-height:1.4}#message .start-button{display:inline-block;padding:10px 20px;font-family:inherit;border-radius:2px;margin-top:20px;background-color:#eee;color:#222;font-size:1.6em;text-transform:inherit;transition:all .18s linear;outline:0;border:0;cursor:pointer;-webkit-appearance:none;-webkit-tap-highlight-color:transparent}#message .start-button:hover{background-color:#fff}#message a{color:#fff;text-decoration:none}#message a:hover{text-decoration:underline}.ui h2{color:#eee}.ui p em{color:#f5f5f5}.ui ol{margin:10px 0;padding-left:1em}.ui ol li{margin:0 0 2px;list-style:outside}.ui a.external{outline:0;font-family:sans-serif;font-size:1em;font-weight:700;text-decoration:none;color:#bbb;display:inline;padding:0}.ui a.external:hover{color:#fff}@media screen and (max-width:600px){#message{padding:60px 40px}}

+ 1 - 0
core/css/reset.css

@@ -0,0 +1 @@
+html,legend{color:#000}html{background:#222}a{cursor:pointer}blockquote,body,code,dd,div,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,html,input,legend,li,ol,p,pre,td,textarea,th,ul{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:400}li{list-style:none}caption,th{text-align:left}q:after,q:before{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,select,textarea{font-family:inherit;font-size:inherit;font-weight:inherit;outline-style:none;outline-width:0}a:focus,h1,h2,h3,h4,h5,h6,object{-moz-outline-style:none;border:0}strong{font-weight:700}

+ 1 - 0
core/index.html

@@ -0,0 +1 @@
+<!DOCTYPE html><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="description" content="Use your shield to defend the Core from the evil red organisms!"><meta name="author" content="Hakim El Hattab"><meta http-equiv="X-UA-Compatible" content="chrome=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="viewport" content="width=450"><title>core</title><link href="css/reset.css" rel="stylesheet" media="screen"><link href="css/main.css" rel="stylesheet" media="screen"><link href="css/css" rel="stylesheet"></head><body data-gr-c-s-loaded="true" class="vsc-initialized" style=""><div id="status" class="ui" style="left:-49px;top:-12px;display:block">Score: <span>5457</span> Time: <span>59.98s</span></div><div id="panels" style="left:-49px;top:-12px;width:1000px;height:650px;display:block"><div id="message" class="ui"> <button id="startButton" class="start-button">Start game</button></div></div><canvas id="world" width="1000" height="650" style="position:absolute;left:-55px;top:-18px"><p class="no-canvas"> You need a <a href="https://www.google.com/chrome">modern browser</a> to view this.</p></canvas><canvas id="background" width="1000" height="650" style="position:absolute;left:-49px;top:-12px"></canvas><script src="js/timbre.js"></script><script src="js/core-audio.js"></script><script src="js/core.js"></script></body></html>

+ 104 - 0
core/js/core-audio.js

@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2018 Luis Bergmann, https://luisbergmann.com/
+ */
+var CoreAudio = new function() {
+
+  // notes and octaves
+  var notes = [0, 2, 3, 5, 7, 10];
+  var octaves = [0, 12, 24, 36];
+
+  // sound chain
+  // it can get cpu intensive with the reverb
+  var reverb = T("reverb", {room:0.7, damp:0.2, mix:0.3}).play();
+
+  //organism sound chain
+  var orgOsc = T("pulse");
+  var orgEnv = T("perc", {a:1, r:30});
+  var orgOscEnv1 = T("OscGen", {osc:orgOsc, env:orgEnv, mul:0.1});
+  reverb.append(orgOscEnv1);
+
+  var orgOsc2 = T("sin");
+  var orgEnv2 = T("perc", {a:10, r:500});
+  var orgOscEnv2 = T("OscGen", {osc:orgOsc2, env:orgEnv2, mul:0.2});
+  reverb.append(orgOscEnv2);
+
+  //shield sound chain
+  var shieldNoiseOsc = T("fnoise", {freq:50, mul:0.1});
+  var shieldOsc = T("pulse", {freq:50, mul:0.1});
+  var shieldEnv = T("perc", {a:10, r:100}, shieldNoiseOsc, shieldOsc).play();
+
+  // play synth note every time a organism is created
+  this.playSynth = function(objPos) {
+    //choose and play synth note
+    var midicps = T("midicps");
+    var note = notes[Math.random() * 5 | 0];
+    var octave = octaves[Math.random() * 3 | 0];
+
+    if (Math.random() < 0.7) {
+      // for saving cpu not every time there's a organism a oscillator plays
+      var playOsc = T("sin", {freq:midicps.at(60 + note + octave), mul:0.2});
+      var playEnv = T("perc", {r:3000}, playOsc).bang();
+      var eq = T("eq", {params:{hpf:[200, 1], lpf:[2000, 1]}}, playEnv);
+      var tremParam = T("param", {value:1}).to(Math.random()*5 + 5, 3000);
+      var tremOsc = T("sin", {freq:tremParam, mul:.6});
+      var trem = T("*", eq, tremOsc);
+      var panParam = T("param", {value:objPos}).expTo(0.5, 3000);
+      var pan = T("pan", {pos:panParam}, trem).play();
+
+      var interval = T("timeout", {timeout:3000}).on('ended', function () {
+        tremOsc.pause();
+        trem.pause();
+        pan.pause();
+      }).start();
+    }
+  }
+
+
+  this.organismDead = function () {
+    var midicps = T("midicps");
+    var note = notes[Math.random() * 5 | 0];
+    var octave = octaves[Math.random() * 3 | 0];
+
+    orgOscEnv1.noteOn((180 + note + octave), 80);
+    orgOscEnv2.noteOn((60 + note + octave), 80);
+
+    //low sound
+    var lowOsc = T("sin", {freq:Math.random()*50+50, mul:0.6});
+    var losOscEq = T("eq", {params:{hpf:[150, 1]}}, lowOsc);
+    var lowPscEnv= T("perc", {a:70, r:100}, losOscEq).bang().play();
+
+  }
+
+  this.playShield = function () {
+    shieldEnv.bang();
+  }
+
+  this.playGameOver = function () {
+    // var downer = T("param", {value:10000}).to(50, 2000);
+    var osc = T('fnoise', {freq:400, mul:0.1});
+    var env = T('perc', {a:10, r:2000}, osc).bang().play();
+  }
+
+
+  this.energyUp = function () {
+    var energyFreq = T("param", {value:80}).to(Math.random()*500+500, "1sec");
+    var osc1 = T('sin', {freq:energyFreq, mul:0.6});
+    var osc1Eq = T("eq", {params:{hpf:[150, 1]}}, osc1);
+    var env = T("perc", {a:50, r:500}, osc1Eq).bang().play();
+  }
+
+  this.energyDown = function () {
+
+    //sine osc with tremolo
+    var downer2 = T("param", {value:(Math.random()*100 + 100)}).to(10, 1000);
+    var osc2 = T('sin', {freq:downer2, mul:0.1});
+    var osc2Eq = T("eq", {params:{hpf:[150, 1]}}, osc2);
+    var downer3 = T("param", {value:(Math.random()*50 + 50)}).to(30, 1000);
+    var osc3 = T('pulse', {freq:downer3, mul:0.1});
+    var osc3Eq = T("eq", {params:{hpf:[150, 1]}}, osc3);
+    var env2 = T("perc", {r:800}, osc2Eq, osc3Eq).bang();
+    var trem2param = T("param", {value:Math.random()*5+1}).to(1, 1000);
+    var trem2 = T("sin", {freq:trem2param, mul:.7})
+    var oscTrem2 = T("*", env2, trem2).play();
+  }
+}

+ 714 - 0
core/js/core.js

@@ -0,0 +1,714 @@
+/**
+ * Core
+ * MIT licensed
+ *
+ * Copyright (C) 2018 Hakim El Hattab, http://hakim.se
+ */
+var Core = new function(){
+
+	// Flags if we are running mobile mode
+	var isMobile = !!navigator.userAgent.toLowerCase().match( /ipod|ipad|iphone|android/gi );
+
+	var DEFAULT_WIDTH = 1000,
+		DEFAULT_HEIGHT = 650,
+		BORDER_WIDTH = 6,
+		FRAMERATE = 60;
+
+	// Types of organisms
+	var ORGANISM_ENEMY = 'enemy',
+		ORGANISM_ENERGY = 'energy';
+
+	// The world dimensions
+	var world = {
+		width: isMobile ? window.innerWidth : DEFAULT_WIDTH,
+		height: isMobile ? window.innerHeight : DEFAULT_HEIGHT
+	};
+
+	var canvas,
+		context;
+
+	var canvasBackground,
+		contextBackground;
+
+	// UI DOM elements
+	var status;
+	var panels;
+	var message;
+	var title;
+	var startButton;
+
+	// Game elements
+	var organisms = [];
+	var particles = [];
+	var player;
+
+	// Mouse properties
+	var mouseX = (window.innerWidth + world.width) * 0.5;
+	var mouseY = (window.innerHeight + world.height) * 0.5;
+	var mouseIsDown = false;
+	var spaceIsDown = false;
+
+	// Game properties and scoring
+	var playing = false;
+	var score = 0;
+	var time = 0;
+	var duration = 0;
+	var difficulty = 1;
+	var lastspawn = 0;
+
+	// Game statistics
+	var fc = 0; // Frame count
+	var fs = 0; // Frame score
+	var cs = 0; // Collision score
+
+	// The world's velocity
+	var velocity = { x: -1.3, y: 1 };
+
+	// Performance (FPS) tracking
+	var fps = 0;
+	var fpsMin = 1000;
+	var fpsMax = 0;
+	var timeLastSecond = new Date().getTime();
+	var frames = 0;
+
+	this.init = function(){
+
+		canvas = document.getElementById('world');
+		canvasBackground = document.getElementById('background');
+		panels = document.getElementById('panels');
+		status = document.getElementById('status');
+		message = document.getElementById('message');
+		title = document.getElementById('title');
+		startButton = document.getElementById('startButton');
+
+		if (canvas && canvas.getContext) {
+			context = canvas.getContext('2d');
+
+			contextBackground = canvasBackground.getContext('2d');
+
+			// Register event listeners
+			document.addEventListener('mousemove', documentMouseMoveHandler, false);
+			document.addEventListener('mousedown', documentMouseDownHandler, false);
+			document.addEventListener('mouseup', documentMouseUpHandler, false);
+			canvas.addEventListener('touchstart', documentTouchStartHandler, false);
+			document.addEventListener('touchmove', documentTouchMoveHandler, false);
+			document.addEventListener('touchend', documentTouchEndHandler, false);
+			window.addEventListener('resize', windowResizeHandler, false);
+			startButton.addEventListener('click', startButtonClickHandler, false);
+			document.addEventListener('keydown', documentKeyDownHandler, false);
+			document.addEventListener('keyup', documentKeyUpHandler, false);
+
+			// Define our player
+			player = new Player();
+
+			// Force an initial resize to make sure the UI is sized correctly
+			windowResizeHandler();
+
+			// If we are running on mobile, certain elements need to be configured differently
+			if( isMobile ) {
+				status.style.width = world.width + 'px';
+				canvas.style.border = 'none';
+			}
+
+			animate();
+		}
+	};
+
+	function renderBackground() {
+		var gradient = contextBackground.createRadialGradient( world.width * 0.5, world.height * 0.5, 0, world.width * 0.5, world.height * 0.5, 500 );
+		gradient.addColorStop(0,'rgba(0, 70, 70, 1)');
+		gradient.addColorStop(1,'rgba(0, 8, 14, 1)');
+
+		contextBackground.fillStyle = gradient;
+		contextBackground.fillRect( 0, 0, world.width, world.height );
+	}
+
+	/**
+	 * Handles click on the start button in the UI.
+	 */
+	function startButtonClickHandler(event){
+		if( playing == false ) {
+			playing = true;
+
+			// Reset game properties
+			organisms = [];
+			score = 0;
+			difficulty = 1;
+
+			// Reset game tracking properties
+			fc = 0;
+			fs = 0;
+			ms = 0;
+			cs = 0;
+
+			// Reset the player data
+			player.energy = 30;
+
+			// Hide the game UI
+			panels.style.display = 'none';
+			status.style.display = 'block';
+
+			time = new Date().getTime();
+		}
+	}
+
+	/**
+	 * Stops the currently ongoing game and shows the
+	 * resulting data in the UI.
+	 */
+	function gameOver() {
+		playing = false;
+
+		// Determine the duration of the game
+		duration = new Date().getTime() - time;
+
+		// Show the UI
+		panels.style.display = 'block';
+
+		// Ensure that the score is an integer
+		score = Math.round(score);
+
+		// Write the users score to the UI
+		title.innerHTML = 'Game Over! (' + score + ' points)';
+
+		// Update the status bar with the final score and time
+		scoreText = 'Score: <span>' + Math.round( score ) + '</span>';
+		scoreText += ' Time: <span>' + Math.round( ( ( new Date().getTime() - time ) / 1000 ) * 100 ) / 100 + 's</span>';
+		status.innerHTML = scoreText;
+	}
+
+	function documentKeyDownHandler(event) {
+		switch( event.keyCode ) {
+			case 32:
+				if( !spaceIsDown && player.energy > 15 ) {
+					player.energy -= 4;
+				}
+				spaceIsDown = true;
+				event.preventDefault();
+				break;
+		}
+	}
+	function documentKeyUpHandler(event) {
+		switch( event.keyCode ) {
+			case 32:
+				spaceIsDown = false;
+				event.preventDefault();
+				break;
+		}
+	}
+
+	/**
+	 * Event handler for document.onmousemove.
+	 */
+	function documentMouseMoveHandler(event){
+		mouseX = event.clientX - (window.innerWidth - world.width) * 0.5 - BORDER_WIDTH;
+		mouseY = event.clientY - (window.innerHeight - world.height) * 0.5 - BORDER_WIDTH;
+	}
+
+	/**
+	 * Event handler for document.onmousedown.
+	 */
+	function documentMouseDownHandler(event){
+		mouseIsDown = true;
+	}
+
+	/**
+	 * Event handler for document.onmouseup.
+	 */
+	function documentMouseUpHandler(event) {
+		mouseIsDown = false;
+	}
+
+	/**
+	 * Event handler for document.ontouchstart.
+	 */
+	function documentTouchStartHandler(event) {
+		if(event.touches.length == 1) {
+			event.preventDefault();
+
+			mouseX = event.touches[0].pageX - (window.innerWidth - world.width) * 0.5;
+			mouseY = event.touches[0].pageY - (window.innerHeight - world.height) * 0.5;
+
+			mouseIsDown = true;
+		}
+	}
+
+	/**
+	 * Event handler for document.ontouchmove.
+	 */
+	function documentTouchMoveHandler(event) {
+		if(event.touches.length == 1) {
+			event.preventDefault();
+
+			mouseX = event.touches[0].pageX - (window.innerWidth - world.width) * 0.5 - 60;
+			mouseY = event.touches[0].pageY - (window.innerHeight - world.height) * 0.5 - 30;
+		}
+	}
+
+	/**
+	 * Event handler for document.ontouchend.
+	 */
+	function documentTouchEndHandler(event) {
+		mouseIsDown = false;
+	}
+
+	/**
+	 * Event handler for window.onresize.
+	 */
+	function windowResizeHandler() {
+		// Update the game size
+		world.width = isMobile ? window.innerWidth : DEFAULT_WIDTH;
+		world.height = isMobile ? window.innerHeight : DEFAULT_HEIGHT;
+
+		// Center the player
+		player.position.x = world.width * 0.5;
+		player.position.y = world.height * 0.5;
+
+		// Resize the canvas
+		canvas.width = world.width;
+		canvas.height = world.height;
+		canvasBackground.width = world.width;
+		canvasBackground.height = world.height;
+
+		// Determine the x/y position of the canvas
+		var cvx = (window.innerWidth - world.width) * 0.5;
+		var cvy = (window.innerHeight - world.height) * 0.5;
+
+		// Position the canvas
+		canvas.style.position = 'absolute';
+		canvas.style.left = cvx + 'px';
+		canvas.style.top = cvy + 'px';
+		canvasBackground.style.position = 'absolute';
+		canvasBackground.style.left = cvx + BORDER_WIDTH + 'px';
+		canvasBackground.style.top = cvy + BORDER_WIDTH + 'px';
+
+		if( isMobile ) {
+			panels.style.left = '0px';
+			panels.style.top = '0px';
+			panels.style.width = '100%';
+			panels.style.height = '100%';
+			status.style.left = '0px';
+			status.style.top = '0px';
+		}
+		else {
+			panels.style.left = cvx + BORDER_WIDTH + 'px';
+			panels.style.top = cvy + BORDER_WIDTH + 'px';
+			panels.style.width = world.width + 'px';
+			panels.style.height = world.height + 'px';
+			status.style.left = cvx + BORDER_WIDTH + 'px';
+			status.style.top = cvy + BORDER_WIDTH + 'px';
+		}
+
+		renderBackground();
+	}
+
+	/**
+	 * Emits a random number of particles from a set point.
+	 *
+	 * @param position The point where particles will be
+	 * emitted from
+	 * @param spread The pixel spread of the emittal
+	 */
+	function emitParticles( position, direction, spread, seed ) {
+		var q = seed + ( Math.random() * seed );
+
+		while( --q >= 0 ) {
+			var p = new Point();
+			p.position.x = position.x + ( Math.sin(q) * spread );
+			p.position.y = position.y + ( Math.cos(q) * spread );
+			p.velocity = { x: direction.x + ( -1 + Math.random() * 2 ), y: direction.y + ( - 1 + Math.random() * 2 ) };
+			p.alpha = 1;
+
+			particles.push( p );
+		}
+	}
+
+	/**
+	 * Called on every frame to update the game properties
+	 * and render the current state to the canvas.
+	 */
+	function animate() {
+
+		// Fetch the current time for this frame
+		var frameTime = new Date().getTime();
+
+		// Increase the frame count
+		frames ++;
+
+		// Check if a second has passed since the last time we updated the FPS
+		if( frameTime > timeLastSecond + 1000 ) {
+			// Establish the current, minimum and maximum FPS
+			fps = Math.min( Math.round( ( frames * 1000 ) / ( frameTime - timeLastSecond ) ), FRAMERATE );
+			fpsMin = Math.min( fpsMin, fps );
+			fpsMax = Math.max( fpsMax, fps );
+
+			timeLastSecond = frameTime;
+			frames = 0;
+		}
+
+		// A factor by which the score will be scaled, depending on current FPS
+		var scoreFactor = 0.01 + ( Math.max( Math.min( fps, FRAMERATE ), 0 ) / FRAMERATE * 0.99 );
+
+		// Scales down the factor by itself
+		scoreFactor = scoreFactor * scoreFactor;
+
+		// Clear the canvas of all old pixel data
+		context.clearRect(0,0,canvas.width,canvas.height);
+
+		var i,
+			ilen,
+			j,
+			jlen;
+
+		// Only update game properties and draw the player if a game is active
+		if( playing ) {
+
+			// Increment the difficulty slightly
+			difficulty += 0.0015;
+
+			// Increment the score depending on difficulty
+			score += (0.4 * difficulty) * scoreFactor;
+
+			// Increase the game frame count stat
+			fc ++;
+
+			// Increase the score count stats
+			fs += (0.4 * difficulty) * scoreFactor;
+
+			//player.angle = Math.atan2( mouseY - player.position.y, mouseX - player.position.x );
+
+			var targetAngle = Math.atan2( mouseY - player.position.y, mouseX - player.position.x );
+
+			if( Math.abs( targetAngle - player.angle ) > Math.PI ) {
+				player.angle = targetAngle;
+			}
+
+			player.angle += ( targetAngle - player.angle ) * 0.2;
+
+			player.energyRadiusTarget = ( player.energy / 100 ) * ( player.radius * 0.8 );
+			player.energyRadius += ( player.energyRadiusTarget - player.energyRadius ) * 0.2;
+
+			player.shield = { x: player.position.x + Math.cos( player.angle ) * player.radius, y: player.position.y + Math.sin( player.angle ) * player.radius };
+
+			// Shield
+			context.beginPath();
+			context.strokeStyle = '#648d93';
+			context.lineWidth = 3;
+			context.arc( player.position.x, player.position.y, player.radius, player.angle + 1.6, player.angle - 1.6, true );
+			context.stroke();
+
+			// Core
+			context.beginPath();
+			context.fillStyle = "#249d93";
+			context.strokeStyle = "#3be2d4";
+			context.lineWidth = 1.5;
+
+			player.updateCore();
+
+			var loopedNodes = player.coreNodes.concat();
+			loopedNodes.push( player.coreNodes[0] );
+
+			for( var i = 0; i < loopedNodes.length; i++ ) {
+				p = loopedNodes[i];
+				p2 = loopedNodes[i+1];
+
+				p.position.x += ( (player.position.x + p.normal.x + p.offset.x) - p.position.x ) * 0.2;
+				p.position.y += ( (player.position.y + p.normal.y + p.offset.y) - p.position.y ) * 0.2;
+
+				if( i == 0 ) {
+					// This is the first loop, so we need to start by moving into position
+					context.moveTo( p.position.x, p.position.y );
+				}
+				else if( p2 ) {
+					// Draw a curve between the current and next trail point
+					context.quadraticCurveTo( p.position.x, p.position.y, p.position.x + ( p2.position.x - p.position.x ) / 2, p.position.y + ( p2.position.y - p.position.y ) / 2 );
+				}
+			}
+
+			context.closePath();
+			context.fill();
+			context.stroke();
+
+		}
+
+		if( spaceIsDown && player.energy > 10 ) {
+			player.energy -= 0.1;
+
+			context.beginPath();
+			context.strokeStyle = '#648d93';
+			context.lineWidth = 1;
+			context.fillStyle = 'rgba( 0, 100, 100, ' + ( player.energy / 100 ) * 0.9 + ' )';
+			context.arc( player.position.x, player.position.y, player.radius, 0, Math.PI*2, true );
+			context.fill();
+			context.stroke();
+
+			// play sound for the shield
+			CoreAudio.playShield();
+		}
+
+		var enemyCount = 0;
+		var energyCount = 0;
+
+		// Go through each enemy and draw it + update its properties
+		for( i = 0; i < organisms.length; i++ ) {
+			p = organisms[i];
+
+			p.position.x += p.velocity.x;
+			p.position.y += p.velocity.y;
+
+			p.alpha += ( 1 - p.alpha ) * 0.1;
+
+			if( p.type == ORGANISM_ENEMY ) context.fillStyle = 'rgba( 255, 0, 0, ' + p.alpha + ' )';
+			if( p.type == ORGANISM_ENERGY ) context.fillStyle = 'rgba( 0, 235, 190, ' + p.alpha + ' )';
+
+			context.beginPath();
+			context.arc(p.position.x, p.position.y, p.size/2, 0, Math.PI*2, true);
+			context.fill();
+
+			var angle = Math.atan2( p.position.y - player.position.y, p.position.x - player.position.x );
+
+			if (playing) {
+
+				var dist = Math.abs( angle - player.angle );
+
+				if( dist > Math.PI ) {
+					dist = ( Math.PI * 2 ) - dist;
+				}
+
+				if ( dist < 1.6 ) {
+					if (p.distanceTo(player.position) > player.radius - 5 && p.distanceTo(player.position) < player.radius + 5) {
+						p.dead = true;
+
+						// play sound
+						CoreAudio.organismDead();
+					}
+				}
+
+				if (spaceIsDown && p.distanceTo(player.position) < player.radius && player.energy > 11) {
+					p.dead = true;
+					score += 4;
+				}
+
+				if (p.distanceTo(player.position) < player.energyRadius + (p.size * 0.5)) {
+					if (p.type == ORGANISM_ENEMY) {
+						player.energy -= 6;
+
+						// play sound
+						CoreAudio.energyDown();
+					}
+
+					if (p.type == ORGANISM_ENERGY) {
+						player.energy += 8;
+						score += 30;
+
+						// play sound
+						CoreAudio.energyUp();
+					}
+
+					player.energy = Math.max(Math.min(player.energy, 100), 0);
+
+					p.dead = true;
+				}
+			}
+
+			// If the enemy is outside of the game bounds, destroy it
+			if( p.position.x < -p.size || p.position.x > world.width + p.size || p.position.y < -p.size || p.position.y > world.height + p.size ) {
+				p.dead = true;
+			}
+
+			// If the enemy is dead, remove it
+			if( p.dead ) {
+				emitParticles( p.position, { x: (p.position.x - player.position.x) * 0.02, y: (p.position.y - player.position.y) * 0.02 }, 5, 5 );
+
+				organisms.splice( i, 1 );
+				i --;
+			}
+			else {
+				if( p.type == ORGANISM_ENEMY ) enemyCount ++;
+				if( p.type == ORGANISM_ENERGY ) energyCount ++;
+			}
+		}
+
+		// If there are less enemies than intended for this difficulty, add another one
+		if( enemyCount < 1 * difficulty && new Date().getTime() - lastspawn > 100 ) {
+			organisms.push( giveLife( new Enemy() ) );
+			lastspawn = new Date().getTime();
+		}
+
+		//
+		if( energyCount < 1 && Math.random() > 0.996 ) {
+			organisms.push( giveLife( new Energy() ) );
+		}
+
+		// Go through and draw all particle effects
+		for( i = 0; i < particles.length; i++ ) {
+			p = particles[i];
+
+			// Apply velocity to the particle
+			p.position.x += p.velocity.x;
+			p.position.y += p.velocity.y;
+
+			// Fade out
+			p.alpha -= 0.02;
+
+			// Draw the particle
+			context.fillStyle = 'rgba(255,255,255,'+Math.max(p.alpha,0)+')';
+			context.fillRect( p.position.x, p.position.y, 1, 1 );
+
+			// If the particle is faded out to less than zero, remove it
+			if( p.alpha <= 0 ) {
+				particles.splice( i, 1 );
+			}
+		}
+
+		// If the game is active, update the game status bar with score, duration and FPS
+		if( playing ) {
+			scoreText = 'Score: <span>' + Math.round( score ) + '</span>';
+			scoreText += ' Time: <span>' + Math.round( ( ( new Date().getTime() - time ) / 1000 ) * 100 ) / 100 + 's</span>';
+			scoreText += ' <p class="fps">FPS: <span>' + Math.round( fps ) + ' ('+Math.round(Math.max(Math.min(fps/FRAMERATE, FRAMERATE), 0)*100)+'%)</span></p>';
+			status.innerHTML = scoreText;
+
+			if( player.energy === 0 ) {
+				emitParticles( player.position, { x: 0, y: 0 }, 10, 40 );
+
+				gameOver();
+
+				// play sound
+				CoreAudio.playGameOver();
+			}
+		}
+
+		requestAnimFrame( animate );
+	}
+
+	/**
+	 *
+	 */
+	function giveLife( organism ) {
+		var side = Math.round( Math.random() * 3 );
+
+		switch( side ) {
+			case 0:
+				organism.position.x = 10;
+				organism.position.y = world.height * Math.random();
+				break;
+			case 1:
+				organism.position.x = world.width * Math.random();
+				organism.position.y = 10;
+				break;
+			case 2:
+				organism.position.x = world.width - 10;
+				organism.position.y = world.height * Math.random();
+				break;
+			case 3:
+				organism.position.x = world.width * Math.random();
+				organism.position.y = world.height - 10;
+				break;
+		}
+
+		if( playing ) CoreAudio.playSynth( organism.position.x / world.width );
+
+		organism.speed = Math.min( Math.max( Math.random(), 0.6 ), 0.75 );
+
+		organism.velocity.x = ( player.position.x - organism.position.x ) * 0.006 * organism.speed;
+		organism.velocity.y = ( player.position.y - organism.position.y ) * 0.006 * organism.speed;
+
+		if( organism.type == 'enemy' ) {
+			organism.velocity.x *= (1+(Math.random()*0.1));
+			organism.velocity.y *= (1+(Math.random()*0.1));
+		}
+
+		organism.alpha = 0;
+
+		return organism;
+	}
+
+};
+
+function Point( x, y ) {
+	this.position = { x: x, y: y };
+}
+Point.prototype.distanceTo = function(p) {
+	var dx = p.x-this.position.x;
+	var dy = p.y-this.position.y;
+	return Math.sqrt(dx*dx + dy*dy);
+};
+Point.prototype.clonePosition = function() {
+	return { x: this.position.x, y: this.position.y };
+};
+
+function Player() {
+	this.position = { x: 0, y: 0 };
+	this.length = 15;
+	this.energy = 30;
+	this.energyRadius = 0;
+	this.energyRadiusTarget = 0;
+	this.radius = 60;
+	this.angle = 0;
+	this.coreQuality = 16;
+	this.coreNodes = [];
+}
+Player.prototype = new Point();
+Player.prototype.updateCore = function() {
+	var i, j, n;
+
+	if( this.coreNodes.length == 0 ) {
+		var i, n;
+
+		for (i = 0; i < this.coreQuality; i++) {
+			n = {
+				position: { x: this.position.x, y: this.position.y },
+				normal: { x: 0, y: 0 },
+				normalTarget: { x: 0, y: 0 },
+				offset: { x: 0, y: 0 }
+			};
+
+			this.coreNodes.push( n );
+		}
+	}
+
+	for (i = 0; i < this.coreQuality; i++) {
+
+		var n = this.coreNodes[i];
+
+		var angle = ( i / this.coreQuality ) * Math.PI * 2;
+
+		n.normal.x = Math.cos( angle ) * this.energyRadius;
+		n.normal.y = Math.sin( angle ) * this.energyRadius;
+
+		n.offset.x = Math.random() * 5;
+		n.offset.y = Math.random() * 5;
+	}
+};
+
+function Enemy() {
+	this.position = { x: 0, y: 0 };
+	this.velocity = { x: 0, y: 0 };
+	this.size = 6 + ( Math.random() * 4 );
+	this.speed = 1;
+	this.type = 'enemy';
+}
+Enemy.prototype = new Point();
+
+function Energy() {
+	this.position = { x: 0, y: 0 };
+	this.velocity = { x: 0, y: 0 };
+	this.size = 10 + ( Math.random() * 6 );
+	this.speed = 1;
+	this.type = 'energy';
+}
+Energy.prototype = new Point();
+
+// shim with setTimeout fallback from http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+window.requestAnimFrame = (function(){
+  return  window.requestAnimationFrame       ||
+          window.webkitRequestAnimationFrame ||
+          window.mozRequestAnimationFrame    ||
+          window.oRequestAnimationFrame      ||
+          window.msRequestAnimationFrame     ||
+          function(/* function */ callback, /* DOMElement */ element){
+            window.setTimeout(callback, 1000 / 60);
+          };
+})();
+
+Core.init();

File diff suppressed because it is too large
+ 1 - 0
core/js/timbre.js


File diff suppressed because it is too large
+ 0 - 0
css/main.css


File diff suppressed because it is too large
+ 0 - 0
games/index.html


BIN
images/computer.mp4


File diff suppressed because it is too large
+ 0 - 0
index.html


File diff suppressed because it is too large
+ 0 - 0
posts/computer/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/dinic/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/fibonacci/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/matrix-pow/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/min-span-tree/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/plan/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/segment-tree/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/test/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/tree-link/index.html


File diff suppressed because it is too large
+ 0 - 0
posts/union-find/index.html


File diff suppressed because it is too large
+ 0 - 0
sans/index.html


File diff suppressed because it is too large
+ 0 - 0
tags/graph/index.html


File diff suppressed because it is too large
+ 0 - 0
tags/index.html


File diff suppressed because it is too large
+ 0 - 0
tags/math/index.html


File diff suppressed because it is too large
+ 0 - 0
tags/other/index.html


File diff suppressed because it is too large
+ 0 - 0
tags/struct/index.html


Some files were not shown because too many files changed in this diff