From 3fc08b6cb747ebbc80c51cb50c6785eb61298daa Mon Sep 17 00:00:00 2001 From: Sky Date: Tue, 23 May 2023 16:14:21 -0400 Subject: [PATCH] katamari fix v3 (turns out it isnt happy if it doesnt have jquery so just force jquery whenever ran) --- js/katamari.js | 786 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 783 insertions(+), 3 deletions(-) diff --git a/js/katamari.js b/js/katamari.js index 26d5e740..26489615 100644 --- a/js/katamari.js +++ b/js/katamari.js @@ -1,4 +1,741 @@ -var BORDER_STYLE="1px solid #bbb",CSS_TRANSFORM=null,CSS_TRANSFORM_ORIGIN=null,POSSIBLE_TRANSFORM_PREFIXES=['-webkit-','-moz-','-o-','-ms-',''],khFirst=false;if(!window.khNodes){khFirst=true;window.khNodes=new StickyNodes()}function getCssTransform(){var i,d=document.createElement('div'),pre;for(i=0;i0){textEls.push(el);return}if(!el.childNodes||el.khIgnore){return}shouldAdd=shouldAddChildren(el);for(i=0,len=el.childNodes.length;i0&&ws[0].length===0){ws.shift()}for(i=0;i0){n=document.createElement('span');n.innerHTML=words[i];p.insertBefore(n,textEl);addDomNode(n)}if(i0){n=document.createTextNode(ws[i]);p.insertBefore(n,textEl)}}p.removeChild(textEl)}buildTextEls(el,shouldAddChildren(el));textEls.map(wordsToSpans)};this.addTagNames=function(el,tagNames){var tname=el.tagName&&el.tagName.toLowerCase(),i,j,els,len;if(el.khIgnore){return}if(tagNames.indexOf(tname)!==-1){addDomNode(el)}if(!el.getElementsByTagName){return}for(i=0;igetVol()){return false}attachGridObj(go);return true}this.updatePhysics=function(){var oldX=x,oldY=y,dx,dy,bounce=false,accelTh;if(accel){accelTh=Math.atan2(accelTargetY-y,accelTargetX-x);vx+=Math.cos(accelTh)*0.5;vy+=Math.sin(accelTh)*0.5}else{vx*=0.95;vy*=0.95}x+=vx;y+=vy;if(x-radius<0){bounce=true;x=radius+1;vx= -vx}else if(x+radius>docW){bounce=true;x=docW-radius-1;vx= -vx}if(y-radius<0){bounce=true;y=radius+1;vy= -vy}else if(y+radius>docH){bounce=true;y=docH-radius-1;vy= -vy}if(vx!==0||vy!==0){th=Math.atan2(vy,vx);dx=x-oldX;dy=y-oldY;phi-=Math.sqrt(dx*dx+dy*dy)/radius}stickyNodes.removeIntersecting(x,y,radius,removeIntCb);this.draw();if(bounce&&sounds){sounds.play_bounce()}};function drawBall(){var sx1,sy1,sx2,sy2,dx,dy,i,pct1,pct2,z1,z2;canvas_el.style.left=(x-radius)+'px';canvas_el.style.top=(y-radius)+'px';if(radius!=lastR){canvas_el.width=2*radius+1;canvas_el.height=2*radius+1;lastR=radius}canvas_ctx.clearRect(0,0,2*radius,2*radius);canvas_ctx.fillStyle="#fff";canvas_ctx.beginPath();canvas_ctx.arc(radius,radius,radius-1,0,Math.PI*2,true);canvas_ctx.fill();canvas_ctx.strokeStyle=color;canvas_ctx.beginPath();canvas_ctx.arc(radius,radius,radius-1,0,Math.PI*2,true);canvas_ctx.stroke();canvas_ctx.fillStyle=color;sx1=radius+radius*Math.cos(th+Math.PI/16);sy1=radius+radius*Math.sin(th+Math.PI/16);sx2=radius+radius*Math.cos(th-Math.PI/16);sy2=radius+radius*Math.sin(th-Math.PI/16);dx=(radius+radius*Math.cos(th+Math.PI*15/16))-sx1;dy=(radius+radius*Math.sin(th+Math.PI*15/16))-sy1;for(i=0;i0&&z2>0){canvas_ctx.beginPath();canvas_ctx.moveTo(sx1+pct1*dx,sy1+pct1*dy);canvas_ctx.lineTo(sx1+pct2*dx,sy1+pct2*dy);canvas_ctx.lineTo(sx2+pct2*dx,sy2+pct2*dy);canvas_ctx.lineTo(sx2+pct1*dx,sy2+pct1*dy);canvas_ctx.fill()}}}function drawAttached(att){var oth=th+att.offTh,ophi=phi+att.offPhi,ox=att.r*Math.cos(oth),oy=att.r*Math.sin(oth),dx=(att.r*Math.cos((th-att.offTh)+Math.PI))-ox,dy=(att.r*Math.sin((th-att.offTh)+Math.PI))-oy,pct=(-Math.cos(ophi)+1)/2,cx=ox+pct*dx,cy=oy+pct*dy,oz=att.r*Math.sin(ophi);if(oz<0&&Math.sqrt(cx*cx+cy*cy)+att.diag0)?501:499;att.el.style.setProperty(CSS_TRANSFORM,'translate('+x+'px,'+y+'px) rotate('+th+'rad) scaleX('+Math.cos(ophi)+') '+att.attT,null);return true}function onAttachedRemoved(att){attachedDiv.removeChild(att.el);delete att.el}this.draw=function(){var i,att,numAttachedVisible=0;drawBall();for(i=attached.length;--i>=0;){att=attached[i];if(att.removeRMAX_ATTACHED_VISIBLE){attached.splice(0,i).map(onAttachedRemoved);break}}}}}function preventDefault(event){event.preventDefault();event.returnValue=false;return false}function Game(gameDiv,stickyNodes,ballOpts){var stickyNodes,player1,physicsInterval,resizeInterval,listeners=[];player1=new PlayerBall(gameDiv,stickyNodes,ballOpts,false);player1.init();player1.setXY(300,300);window.scrollTo(0,200);function on_resize(){player1.setDocSize(jQuery(document).width()-5,jQuery(document).height()-5)}on_resize();document.addEventListener('touchstart',function(event){if(event.touches.length===1){player1.setAccel(true);return preventDefault(event)}},true);document.addEventListener('touchmove',function(event){player1.setAccelTarget(event.touches[0].pageX,event.touches[0].pageY)},true);document.addEventListener('touchend',function(event){if(event.touches.length===0){player1.setAccel(false);return preventDefault(event)}},true);if(ballOpts.MOUSEB!==-5){document.addEventListener('mousemove',function(event){player1.setAccelTarget(event.pageX,event.pageY)},true);document.addEventListener('mousedown',function(event){if(event.button===ballOpts.MOUSEB){player1.setAccel(true);return preventDefault(event)}},true);document.addEventListener('mouseup',function(event){if(event.button===ballOpts.MOUSEB){player1.setAccel(false);return preventDefault(event)}},true);if(ballOpts.MOUSEB===0){document.addEventListener('click',function(event){if(event.button===0){return preventDefault(event)}},true)}else if(ballOpts.MOUSEB===2){document.addEventListener('contextmenu',preventDefault,true)}}physicsInterval=setInterval(function(){player1.updatePhysics()},25);resizeInterval=setInterval(on_resize,1000)}function whenAllLoaded(gameDiv,popup,stickyNodes){stickyNodes.finalize(jQuery(document).width(),jQuery(document).height());jQuery('#loadingp').empty();jQuery('').click(function(){var game,ballOpts;ballOpts={color:jQuery('#khcolor').val(),VOL_MULT:parseFloat(jQuery('#vol_mult').val()),MAX_ATTACHED_VISIBLE:parseInt(jQuery('#maxAtt').val(),10),CHECK_VOLS:(jQuery('#checkv').attr('checked'))?true:false,MOUSEB:parseInt(jQuery('#mouseb').val(),10)};gameDiv.removeChild(popup);game=new Game(gameDiv,stickyNodes,ballOpts)}).appendTo('#loadingp')}function buildPopup(gameDiv){var d=document.createElement('div'),b;d.style.cssText='\ +/* +Copyright Alex Leone, David Nufer, David Truong, 2011-03-11. kathack.com + +javascript:var i,s,ss=['http://kathack.com/js/kh.js','http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js'];for(i=0;i!=ss.length;i++){s=document.createElement('script');s.src=ss[i];document.body.appendChild(s);}void(0); + +*/ +var BORDER_STYLE = "1px solid #bbb", + CSS_TRANSFORM = null, + CSS_TRANSFORM_ORIGIN = null, + POSSIBLE_TRANSFORM_PREFIXES = ['-webkit-', '-moz-', '-o-', '-ms-', ''], + khFirst = false; + +/* When running twice on one page, update pick-uppable nodes instead of + * creating more. + */ +if (!window.khNodes) { + khFirst = true; + window.khNodes = new StickyNodes(); +} + +function getCssTransform() { + var i, d = document.createElement('div'), pre; + for (i = 0; i < POSSIBLE_TRANSFORM_PREFIXES.length; i++) { + pre = POSSIBLE_TRANSFORM_PREFIXES[i]; + d.style.setProperty(pre + 'transform', 'rotate(1rad) scaleX(2)', null); + if (d.style.getPropertyValue(pre + 'transform')) { + CSS_TRANSFORM = pre + 'transform'; + CSS_TRANSFORM_ORIGIN = pre + 'transform-origin'; + return; + } + } + alert("Your browser doesn't support CSS tranforms!"); + throw "Your browser doesn't support CSS tranforms!"; +} +getCssTransform(); + +/** + * Returns true if the circle intersects the element rectangle. + * 0 | 1 | 2 + * ------------------ + * 3 | 4 | 5 + * ------------------ + * 6 | 7 | 9 + */ +function circleGridObjInt(cx, cy, cr, cr2, go) { + var dx, dy; + if (cx < go.left) { + dx = go.left - cx; + if (cy < go.top) { /* zone 0. */ + dy = go.top - cy; + return ((dx * dx + dy * dy) <= cr2); + } else if (cy <= go.bottom) { /* zone 3. */ + return (dx <= cr); + } else { /* zone 6. */ + dy = cy - go.bottom; + return ((dx * dx + dy * dy) <= cr2); + } + } else if (cx <= go.right) { + if (cy < go.top) { /* zone 1. */ + return ((go.top - cy) <= cr); + } else if (cy <= go.bottom) { /* zone 4. */ + return true; + } else { /* zone 7. */ + return ((cy - go.bottom) <= cr); + } + } else { + dx = cx - go.right; + if (cy < go.top) { /* zone 2. */ + dy = go.top - cy; + return ((dx * dx + dy * dy) <= cr2); + } else if (cy <= go.bottom) { /* zone 5. */ + return (dx <= cr); + } else { /* zone 9. */ + dy = cy - go.bottom; + return ((dx * dx + dy * dy) <= cr2); + } + } +} + +/** + * Returns [x,y] where the rectangle is closest to (cx, cy). + * 0 | 1 | 2 + * ------------------ + * 3 | 4 | 5 + * ------------------ + * 6 | 7 | 9 + */ +function getClosestPoint(cx, cy, go) { + var dx, dy; + if (cx < go.left) { + dx = go.left - cx; + if (cy < go.top) { /* zone 0. */ + return [go.left, go.top]; + } else if (cy <= go.bottom) { /* zone 3. */ + return [go.left, cy]; + } else { /* zone 6. */ + return [go.left, go.bottom]; + } + } else if (cx <= go.right) { + if (cy < go.top) { /* zone 1. */ + return [cx, go.top]; + } else if (cy <= go.bottom) { /* zone 4. */ + return [cx, cy]; + } else { /* zone 7. */ + return [cx, go.bottom]; + } + } else { + dx = cx - go.right; + if (cy < go.top) { /* zone 2. */ + return [go.right, go.top]; + } else if (cy <= go.bottom) { /* zone 5. */ + return [go.right, cy]; + } else { /* zone 9. */ + return [go.right, go.bottom]; + } + } +} + +/** + * Returns the "volume" of the grid object. + */ +function gridObjVol(go) { + return go.w * go.h * Math.min(go.w, go.h); +} + +function StickyNodes() { + var domNodes = [], + grid = [], + GRIDX = 100, + GRIDY = 100, + REPLACE_WORDS_IN = { + a: 1, b: 1, big: 1, body: 1, cite:1, code: 1, dd: 1, div: 1, + dt: 1, em: 1, font: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, + i: 1, label: 1, legend: 1, li: 1, p: 1, pre: 1, small: 1, + span: 1, strong: 1, sub: 1, sup: 1, td: 1, th: 1, tt: 1 + }; + + function addDomNode(el) { + if (el !== undefined && el !== null) { + el.khIgnore = true; + el.style.border = BORDER_STYLE; + domNodes.push(el); + } + } + this.addDomNode = addDomNode; + + this.addWords = function (el) { + var textEls = []; + + function shouldAddChildren(el) { + return el.tagName && REPLACE_WORDS_IN[el.tagName.toLowerCase()]; + } + + function buildTextEls(el, shouldAdd) { + var i, len; + if (shouldAdd && el.nodeType === Node.TEXT_NODE && + el.nodeValue.trim().length > 0) { + textEls.push(el); + return; + } + if (!el.childNodes || el.khIgnore) { + return; + } + shouldAdd = shouldAddChildren(el); + for (i = 0, len = el.childNodes.length; i < len; i++) { + buildTextEls(el.childNodes[i], shouldAdd); + } + } + + function wordsToSpans(textEl) { + var p = textEl.parentNode, + words = textEl.nodeValue.split(/\s+/), + ws = textEl.nodeValue.split(/\S+/), + i, n, len = Math.max(words.length, ws.length); + /* preserve whitespace for pre tags. */ + if (ws.length > 0 && ws[0].length === 0) { + ws.shift(); + } + for (i = 0; i < len; i++) { + if (i < words.length && words[i].length > 0) { + n = document.createElement('span'); + n.innerHTML = words[i]; + p.insertBefore(n, textEl); + addDomNode(n); + } + if (i < ws.length && ws[i].length > 0) { + n = document.createTextNode(ws[i]); + p.insertBefore(n, textEl); + } + } + p.removeChild(textEl); + } + + buildTextEls(el, shouldAddChildren(el)); + textEls.map(wordsToSpans); + }; + + /* includes el. */ + this.addTagNames = function (el, tagNames) { + var tname = el.tagName && el.tagName.toLowerCase(), + i, j, els, len; + if (el.khIgnore) { + return; + } + if (tagNames.indexOf(tname) !== -1) { + addDomNode(el); + } + if (!el.getElementsByTagName) { + return; + } + for (i = 0; i < tagNames.length; i++) { + els = el.getElementsByTagName(tagNames[i]); + for (j = 0, len = els.length; j < len; j++) { + if (!els[j].khIgnore) { + addDomNode(els[j]); + } + } + } + }; + + this.finalize = function (docW, docH) { + var xi, yi, i, len, startXI, startYI, el, go, off, w, h, + endXI = Math.floor(docW / GRIDX) + 1, + endYI = Math.floor(docH / GRIDY) + 1; + /* initialize grid. */ + grid = new Array(endXI); + for (xi = 0; xi < endXI; xi++) { + grid[xi] = new Array(endYI); + } + /* add nodes into grid. */ + for (i = 0, len = domNodes.length; i < len; i++) { + el = domNodes[i]; + if (el.khPicked) { + continue; + } + off = jQuery(el).offset(); + w = jQuery(el).width(); + h = jQuery(el).height(); + go = { + el: domNodes[i], /* dom element. */ + left: off.left, + right: off.left + w, + top: off.top, + bottom: off.top + h, + w: w, + h: h, + x: off.left + (w / 2), /* center x. */ + y: off.top + (h / 2), /* center y. */ + diag: Math.sqrt(((w * w) + (h * h)) / 4), /* center to corner */ + + /* these are for removing ourselves from the grid. */ + arrs: [], /* which arrays we're in (grid[x][y]). */ + idxs: [] /* what indexes. */ + }; + startXI = Math.floor(go.left / GRIDX); + startYI = Math.floor(go.top / GRIDY); + endXI = Math.floor((go.left + go.w) / GRIDX) + 1; + endYI = Math.floor((go.top + go.h) / GRIDY) + 1; + for (xi = startXI; xi < endXI; xi++) { + for (yi = startYI; yi < endYI; yi++) { + if (grid[xi] === undefined) { + grid[xi] = []; + } + if (grid[xi][yi] === undefined) { + grid[xi][yi] = [go]; + } else { + grid[xi][yi].push(go); + } + go.arrs.push(grid[xi][yi]); + go.idxs.push(grid[xi][yi].length - 1); + } + } + } + }; + + function removeGridObj(go) { + var i; + for (i = 0; i < go.arrs.length; i++) { + go.arrs[i][go.idxs[i]] = undefined; + } + go.el.style.visibility = "hidden"; + go.el.khPicked = true; + delete go.arrs; + delete go.idxs; + } + + /** + * cb(gridObj) -> boolean true if the object should be removed. + */ + this.removeIntersecting = function (x, y, r, cb) { + var xi, yi, arr, i, r2 = r * r, go, + startXI = Math.floor((x - r) / GRIDX), + startYI = Math.floor((y - r) / GRIDY), + endXI = Math.floor((x + r) / GRIDX) + 1, + endYI = Math.floor((y + r) / GRIDY) + 1; + for (xi = startXI; xi < endXI; xi++) { + if (grid[xi] === undefined) { + continue; + } + for (yi = startYI; yi < endYI; yi++) { + arr = grid[xi][yi]; + if (arr === undefined) { + continue; + } + for (i = 0; i < arr.length; i++) { + go = arr[i]; + if (go !== undefined && + circleGridObjInt(x, y, r, r2, go) && + cb(go)) { + removeGridObj(go); + } + } + } + } + }; +} + +function PlayerBall(parentNode, stickyNodes, ballOpts, sounds) { + var x = 300, y = 300, + vx = 0, vy = 0, + radius = 20, + lastR = 0, /**< optimization: only resize when necessary. */ + docW = 10000, docH = 10000, + + attached = [], + attachedDiv, /* div to put attached nodes into. */ + canvas_el, + canvas_ctx, + color = ballOpts.color, + + accelTargetX = 0, accelTargetY = 0, + accel = false, + + VOL_MULT = ballOpts.VOL_MULT, + MAX_ATTACHED_VISIBLE = ballOpts.MAX_ATTACHED_VISIBLE, + CHECK_VOLS = ballOpts.CHECK_VOLS, + + /** + * which direction the ball is facing in the xy axis, in radians. + * th: 0 is facing dead East + * th: 1/2 PI is facing dead South + * note that this is like regular th on a graph with y inverted. + * Same rotation as css transform. + */ + th = 0, + + /** + * Ball angle in the rotation axis / z plane, in radians. + * phi: 0 is pointing in the direction the ball is rolling. + * phi: 1/2 PI is pointing straight up (out of the page). + * note that forward rotation means phi -= 0.1. + */ + phi = 0; + + this.init = function () { + canvas_el = document.createElement('canvas'); + canvas_el.width = radius * 2; + canvas_el.height = radius * 2; + canvas_el.style.cssText = 'position: absolute; z-index: 500;'; + parentNode.appendChild(canvas_el); + canvas_ctx = canvas_el.getContext('2d'); + + attachedDiv = document.createElement('div'); + parentNode.appendChild(attachedDiv); + }; + + this.setRadius = function (r) { + radius = r; + }; + + this.getState = function () { + return { + x: x, + y: y, + vx: vx, + vy: vy, + radius: radius, + th: th, + phi: phi, + }; + }; + + this.setState = function (s) { + x = s.x; + y = s.y; + vx = s.vx; + vy = s.vy; + radius = s.radius; + th = s.th; + phi = s.phi; + }; + + this.setXY = function (sx, sy) { + x = sx; + y = sy; + }; + + this.setTh = function (sth) { + th = sth; + }; + + this.setPhi = function (sphi) { + phi = sphi; + }; + + this.setColor = function (c) { + color = c; + }; + + this.setDocSize = function (w, h) { + docW = w; + docH = h; + }; + + this.setAccel = function (bool) { + accel = bool; + }; + + this.setAccelTarget = function (tx, ty) { + accelTargetX = tx; + accelTargetY = ty; + }; + + function getVol() { + return (4 * Math.PI * radius * radius * radius / 3); + } + + function grow(go) { + var newVol = getVol() + gridObjVol(go) * VOL_MULT; + radius = Math.pow(newVol * 3 / (4 * Math.PI), 1 / 3); + } + + function attachGridObj(go) { + var attXY = getClosestPoint(x, y, go), + dx = attXY[0] - x, + dy = attXY[1] - y, + r = Math.sqrt(dx * dx + dy * dy), + attTh = 0 - th, + offLeft = attXY[0] - go.left, + offTop = attXY[1] - go.top, + offTh = Math.atan2(dy, dx) - th, + attX = r * Math.cos(offTh), + attY = r * Math.sin(offTh), + el = go.el.cloneNode(true), + go_jel = jQuery(go.el), + newAtt = { + el: el, + attX: attX, + attY: attY, + attT: 'translate(' + Math.round(attX) + 'px,' + + Math.round(attY) + 'px) ' + + 'rotate(' + attTh + 'rad)', + r: r, + offTh: offTh, + offPhi: 0 - phi, + diag: go.diag, + removeR: r + go.diag, + visible: false, + display: go_jel.css('display') + }; + attached.push(newAtt); + grow(go); + el.style.position = 'absolute'; + el.style.left = (-offLeft) + 'px'; + el.style.top = (-offTop) + 'px'; + el.style.setProperty(CSS_TRANSFORM_ORIGIN, + offLeft + 'px ' + offTop + 'px', null); + el.style.display = 'none'; + /* copy computed styles from old object. */ + el.style.color = go_jel.css('color'); + el.style.textDecoration = go_jel.css('text-decoration'); + el.style.fontSize = go_jel.css('font-size'); + el.style.fontWeight = go_jel.css('font-weight'); + el.khIgnore = true; + attachedDiv.appendChild(el); + if (sounds) { + sounds.play_pop(); + } + } + + /** + * @return true if the object should be removed from stickyNodes. + */ + function removeIntCb(go) { + if (CHECK_VOLS && gridObjVol(go) > getVol()) { + return false; + } + attachGridObj(go); + return true; + } + + this.updatePhysics = function () { + var oldX = x, oldY = y, dx, dy, + bounce = false, + accelTh; + if (accel) { + accelTh = Math.atan2(accelTargetY - y, accelTargetX - x); + vx += Math.cos(accelTh) * 0.5; + vy += Math.sin(accelTh) * 0.5; + } else { + vx *= 0.95; + vy *= 0.95; + } + x += vx; + y += vy; + /* bounce ball on edges of document. */ + if (x - radius < 0) { + bounce = true; + x = radius + 1; + vx = -vx; + } else if (x + radius > docW) { + bounce = true; + x = docW - radius - 1; + vx = -vx; + } + if (y - radius < 0) { + bounce = true; + y = radius + 1; + vy = -vy; + } else if (y + radius > docH) { + bounce = true; + y = docH - radius - 1; + vy = -vy; + } + if (vx !== 0 || vy !== 0) { + th = Math.atan2(vy, vx); + dx = x - oldX; + dy = y - oldY; + /* arclen = th * r, so th = arclen / r. */ + phi -= Math.sqrt(dx * dx + dy * dy) / radius; + } + stickyNodes.removeIntersecting(x, y, radius, removeIntCb); + this.draw(); + if (bounce && sounds) { + sounds.play_bounce(); + } + }; + + function drawBall() { + var sx1, sy1, sx2, sy2, dx, dy, i, pct1, pct2, z1, z2; + /* move/resize canvas element. */ + canvas_el.style.left = (x - radius) + 'px'; + canvas_el.style.top = (y - radius) + 'px'; + if (radius != lastR) { + canvas_el.width = 2 * radius + 1; + canvas_el.height = 2 * radius + 1; + lastR = radius; + } + /* draw white circle. */ + canvas_ctx.clearRect(0, 0, 2 * radius, 2 * radius); + canvas_ctx.fillStyle = "#fff"; + canvas_ctx.beginPath(); + canvas_ctx.arc(radius, radius, radius - 1, 0, Math.PI * 2, true); + canvas_ctx.fill(); + /* draw outer border. */ + canvas_ctx.strokeStyle = color; + canvas_ctx.beginPath(); + canvas_ctx.arc(radius, radius, radius - 1, 0, Math.PI * 2, true); + canvas_ctx.stroke(); + /* draw stripes. */ + canvas_ctx.fillStyle = color; + sx1 = radius + radius * Math.cos(th + Math.PI / 16); + sy1 = radius + radius * Math.sin(th + Math.PI / 16); + sx2 = radius + radius * Math.cos(th - Math.PI / 16); + sy2 = radius + radius * Math.sin(th - Math.PI / 16); + dx = (radius + radius * Math.cos(th + Math.PI * 15 / 16)) - sx1; + dy = (radius + radius * Math.sin(th + Math.PI * 15 / 16)) - sy1; + for (i = 0; i < Math.PI * 2; i += Math.PI / 7) { + pct1 = (-Math.cos(phi + i) + 1) / 2; + pct2 = (-Math.cos(phi + i + Math.PI / 32) + 1) / 2; + z1 = Math.sin(phi + i); + z2 = Math.sin(phi + i + Math.PI / 32); + if (z1 > 0 && z2 > 0) { + canvas_ctx.beginPath(); + canvas_ctx.moveTo(sx1 + pct1 * dx, sy1 + pct1 * dy); + canvas_ctx.lineTo(sx1 + pct2 * dx, sy1 + pct2 * dy); + canvas_ctx.lineTo(sx2 + pct2 * dx, sy2 + pct2 * dy); + canvas_ctx.lineTo(sx2 + pct1 * dx, sy2 + pct1 * dy); + canvas_ctx.fill(); + } + } + } + + /** + * @return true if the attached object is roughly visible. + */ + function drawAttached(att) { + var oth = th + att.offTh, + ophi = phi + att.offPhi, + ox = att.r * Math.cos(oth), + oy = att.r * Math.sin(oth), + dx = (att.r * Math.cos((th - att.offTh) + Math.PI)) - ox, + dy = (att.r * Math.sin((th - att.offTh) + Math.PI)) - oy, + pct = (-Math.cos(ophi) + 1) / 2, + cx = ox + pct * dx, + cy = oy + pct * dy, + oz = att.r * Math.sin(ophi); + if (oz < 0 && Math.sqrt(cx * cx + cy * cy) + att.diag < radius) { + /* hidden behind circle. */ + if (att.visible) { + att.visible = false; + att.el.style.display = "none"; + } + return false; + } + /* attached node is visible. */ + if (!att.visible) { + att.visible = true; + att.el.style.display = att.display; + } + //att.el.style.zIndex = 500 + Math.round(oz); + att.el.style.zIndex = (oz > 0)? 501 : 499; + att.el.style.setProperty( + CSS_TRANSFORM, + 'translate(' + x + 'px,' + y + 'px) ' + + 'rotate(' + th + 'rad) ' + + 'scaleX(' + Math.cos(ophi) + ') ' + + att.attT, null); + return true; + } + + function onAttachedRemoved(att) { + attachedDiv.removeChild(att.el); + delete att.el; + } + + this.draw = function () { + var i, att, numAttachedVisible = 0; + drawBall(); + for (i = attached.length; --i >= 0;) { + att = attached[i]; + if (att.removeR < radius) { + attached.splice(i, 1).map(onAttachedRemoved); + } else if (drawAttached(att)) { + if (++numAttachedVisible > MAX_ATTACHED_VISIBLE) { + /* remove older items and stop. */ + attached.splice(0, i).map(onAttachedRemoved); + break; + } + } + } + }; +} + +function preventDefault(event) { + event.preventDefault(); + event.returnValue = false; + return false; +} + +function Game(gameDiv, stickyNodes, ballOpts) { + var stickyNodes, player1, physicsInterval, resizeInterval, listeners = []; + player1 = new PlayerBall(gameDiv, stickyNodes, ballOpts, false); + player1.init(); + player1.setXY(300, 300); + window.scrollTo(0, 200); + + function on_resize() { + player1.setDocSize(jQuery(document).width() - 5, + jQuery(document).height() - 5); + } + on_resize(); + + /* touch events - always on? */ + document.addEventListener('touchstart', function (event) { + if (event.touches.length === 1) { + player1.setAccel(true); + return preventDefault(event); + } + }, true); + document.addEventListener('touchmove', function (event) { + player1.setAccelTarget(event.touches[0].pageX, + event.touches[0].pageY); + }, true); + document.addEventListener('touchend', function (event) { + if (event.touches.length === 0) { + player1.setAccel(false); + return preventDefault(event); + } + }, true); + + if (ballOpts.MOUSEB !== -5) { + /* mouse buttons */ + document.addEventListener('mousemove', function (event) { + player1.setAccelTarget(event.pageX, event.pageY); + }, true); + document.addEventListener('mousedown', function (event) { + if (event.button === ballOpts.MOUSEB) { + player1.setAccel(true); + return preventDefault(event); + } + }, true); + document.addEventListener('mouseup', function (event) { + if (event.button === ballOpts.MOUSEB) { + player1.setAccel(false); + return preventDefault(event); + } + }, true); + + if (ballOpts.MOUSEB === 0) { + /* block click events. */ + document.addEventListener('click', function (event) { + if (event.button === 0) { + return preventDefault(event); + } + }, true); + } else if (ballOpts.MOUSEB === 2) { + /* block right-click context menu. */ + document.addEventListener('contextmenu', preventDefault, true); + } + } + + physicsInterval = setInterval(function () { + player1.updatePhysics(); + }, 25); + resizeInterval = setInterval(on_resize, 1000); +} + +function whenAllLoaded(gameDiv, popup, stickyNodes) { + stickyNodes.finalize(jQuery(document).width(), jQuery(document).height()); + jQuery('#loadingp').empty(); + jQuery('').click(function () { + var game, ballOpts; + ballOpts = { + color: jQuery('#khcolor').val(), + VOL_MULT: parseFloat(jQuery('#vol_mult').val()), + MAX_ATTACHED_VISIBLE: parseInt(jQuery('#maxAtt').val(), 10), + CHECK_VOLS: (jQuery('#checkv').attr('checked'))? true : false, + MOUSEB: parseInt(jQuery('#mouseb').val(), 10) + }; + gameDiv.removeChild(popup); + game = new Game(gameDiv, stickyNodes, ballOpts); + }).appendTo('#loadingp'); +} + +function buildPopup(gameDiv) { + var d = document.createElement('div'), b; + d.style.cssText = '\ position: fixed;\ left: 50%;\ top: 50%;\ @@ -11,7 +748,8 @@ color:black;\ padding:20px;\ font-size:13px;\ text-align:left;\ -z-index:501;';d.innerHTML='

\ +z-index:501;'; + d.innerHTML = '

\ \ Katamari!

\ \ @@ -20,6 +758,8 @@ Katamari!\ \ \ to control the ball!

\ +\
\
\ -

Loading!

';gameDiv.appendChild(d);d.getElementsByTagName('button')[0].addEventListener('click',function(){gameDiv.removeChild(d)},true);return d}function main(){var gameDiv,checkInterval,stickyNodes,popup;gameDiv=document.createElement('div');gameDiv.khIgnore=true;document.body.appendChild(gameDiv);popup=buildPopup(gameDiv);setTimeout(function(){var i,len,el;window.khNodes.addWords(document.body);for(i=0,len=document.body.childNodes.length;iLoading!

'; + gameDiv.appendChild(d); + d.getElementsByTagName('button')[0].addEventListener('click', function () { + gameDiv.removeChild(d); + }, true); + return d; +} + +function main() { + var gameDiv, checkInterval, stickyNodes, popup; + + gameDiv = document.createElement('div'); + gameDiv.khIgnore = true; + document.body.appendChild(gameDiv); + popup = buildPopup(gameDiv); + + /* setTimeout so that the popup displays before we freeze. */ + setTimeout(function () { + var i, len, el; + window.khNodes.addWords(document.body); + for (i = 0, len = document.body.childNodes.length; i < len; i++) { + el = document.body.childNodes[i]; + window.khNodes.addTagNames(el, [ + 'button', 'canvas', 'iframe', 'img', 'input', 'select', + 'textarea' + ]); + } + + checkInterval = setInterval(function () { + if (window.jQuery) { + clearInterval(checkInterval); + whenAllLoaded(gameDiv, popup, window.khNodes); + } + }, 100); + }, 0); +} +whenAllLoaded(gameDiv, popup, window.khNodes); + +if (!window.noMain) { + main(); +}