├── .gitignore ├── src ├── pacman.png ├── audio │ ├── die.mp3 │ ├── die.ogg │ ├── eating.mp3 │ ├── eating.ogg │ ├── siren.mp3 │ ├── siren.ogg │ ├── vcs_90.mp3 │ ├── vcs_90.ogg │ ├── eatghost.mp3 │ ├── eatghost.ogg │ ├── eatpill.mp3 │ ├── eatpill.ogg │ ├── extra lives.mp3 │ ├── extra lives.ogg │ ├── eating.short.mp3 │ ├── eating.short.ogg │ ├── intermission.mp3 │ ├── intermission.ogg │ ├── opening_song.mp3 │ └── opening_song.ogg ├── BD_Cartoon_Shout-webfont.ttf ├── index.html ├── modernizr-1.5.min.js └── pacman.js ├── .vscode └── extensions.json ├── .gitpod.yml ├── .sasjslint ├── package.json ├── .releaserc ├── sasjs └── sasjsconfig.json ├── CHANGELOG.md ├── .github └── workflows │ └── release.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | sasjsbuild/ 3 | .env* 4 | 5 | sasjsresults/ 6 | -------------------------------------------------------------------------------- /src/pacman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/pacman.png -------------------------------------------------------------------------------- /src/audio/die.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/die.mp3 -------------------------------------------------------------------------------- /src/audio/die.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/die.ogg -------------------------------------------------------------------------------- /src/audio/eating.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.mp3 -------------------------------------------------------------------------------- /src/audio/eating.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.ogg -------------------------------------------------------------------------------- /src/audio/siren.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/siren.mp3 -------------------------------------------------------------------------------- /src/audio/siren.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/siren.ogg -------------------------------------------------------------------------------- /src/audio/vcs_90.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/vcs_90.mp3 -------------------------------------------------------------------------------- /src/audio/vcs_90.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/vcs_90.ogg -------------------------------------------------------------------------------- /src/audio/eatghost.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatghost.mp3 -------------------------------------------------------------------------------- /src/audio/eatghost.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatghost.ogg -------------------------------------------------------------------------------- /src/audio/eatpill.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatpill.mp3 -------------------------------------------------------------------------------- /src/audio/eatpill.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eatpill.ogg -------------------------------------------------------------------------------- /src/audio/extra lives.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/extra lives.mp3 -------------------------------------------------------------------------------- /src/audio/extra lives.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/extra lives.ogg -------------------------------------------------------------------------------- /src/audio/eating.short.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.short.mp3 -------------------------------------------------------------------------------- /src/audio/eating.short.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/eating.short.ogg -------------------------------------------------------------------------------- /src/audio/intermission.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/intermission.mp3 -------------------------------------------------------------------------------- /src/audio/intermission.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/intermission.ogg -------------------------------------------------------------------------------- /src/audio/opening_song.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/opening_song.mp3 -------------------------------------------------------------------------------- /src/audio/opening_song.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/audio/opening_song.ogg -------------------------------------------------------------------------------- /src/BD_Cartoon_Shout-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasjs/pacman/main/src/BD_Cartoon_Shout-webfont.ttf -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "sasjs.sasjs-for-vscode", 4 | "hediet.vscode-drawio" 5 | ] 6 | } -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm install -g npm 3 | - command: npm i -g @sasjs/cli && npm i 4 | 5 | image: 6 | file: .gitpod.dockerfile 7 | vscode: 8 | extensions: 9 | - sasjs.sasjs-for-vscode 10 | - ritwickdey.liveserver -------------------------------------------------------------------------------- /.sasjslint: -------------------------------------------------------------------------------- 1 | { 2 | "noEncodedPasswords": true, 3 | "hasDoxygenHeader": true, 4 | "hasMacroNameInMend": false, 5 | "hasMacroParentheses": true, 6 | "indentationMultiple": 2, 7 | "lowerCaseFileNames": true, 8 | "maxLineLength": 80, 9 | "noNestedMacros": true, 10 | "noSpacesInFileNames": true, 11 | "noTabIndentation": true, 12 | "noTrailingSpaces": true 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sasjs/pacman", 3 | "repository": "https://github.com/sasjs/pacman", 4 | "version": "1.0.0", 5 | "description": "Pacman for SAS!", 6 | "keywords": [ 7 | "SAS", 8 | "SASViya", 9 | "SASjs" 10 | ], 11 | "author": "", 12 | "devDependencies": { 13 | "@sasjs/cli": "^3.15.0", 14 | "@semantic-release/changelog": "^6.0.1", 15 | "@semantic-release/exec": "^6.0.3", 16 | "@semantic-release/git": "^10.0.1", 17 | "@semantic-release/github": "^8.0.4" 18 | } 19 | } -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main" 4 | ], 5 | "plugins": [ 6 | "@semantic-release/commit-analyzer", 7 | "@semantic-release/release-notes-generator", 8 | "@semantic-release/changelog", 9 | [ 10 | "@semantic-release/git", 11 | { 12 | "assets": [ 13 | "CHANGELOG.md" 14 | ] 15 | } 16 | ], 17 | [ 18 | "@semantic-release/github", 19 | { 20 | "assets": [ 21 | { 22 | "path": "./artefacts/server.json.zip", 23 | "label": "SASjs Streamed App (Zip File)" 24 | }, 25 | { 26 | "path": "./artefacts/sas9.sas", 27 | "label": "SAS 9 EBI Deployment (SAS Program)" 28 | }, 29 | { 30 | "path": "./artefacts/viya.sas", 31 | "label": "Viya Deployment (SAS Program)" 32 | } 33 | ] 34 | } 35 | ], 36 | [ 37 | "@semantic-release/exec", 38 | { 39 | "publishCmd": "echo 'publish command'" 40 | } 41 | ] 42 | ] 43 | } -------------------------------------------------------------------------------- /sasjs/sasjsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json", 3 | "streamConfig": { 4 | "streamWeb": true, 5 | "streamWebFolder": "web", 6 | "webSourcePath": "src", 7 | "streamServiceName": "Pacman", 8 | "streamLogo": "pacman.png" 9 | }, 10 | "defaultTarget": "server", 11 | "targets": [ 12 | { 13 | "name": "viya", 14 | "serverUrl": "", 15 | "serverType": "SASVIYA", 16 | "httpsAgentOptions": { 17 | "allowInsecureRequests": false 18 | }, 19 | "appLoc": "/Public/app/pacman", 20 | "contextName": "SAS Job Execution compute context" 21 | }, 22 | { 23 | "name": "sas9", 24 | "serverUrl": "", 25 | "serverType": "SAS9", 26 | "appLoc": "/Public/app/pacman", 27 | "serverName": "SASApp", 28 | "repositoryName": "Foundation" 29 | }, 30 | { 31 | "name": "server", 32 | "serverUrl": "", 33 | "serverType": "SASJS", 34 | "appLoc": "/Public/app/pacman", 35 | "deployConfig": { 36 | "deployServicePack": true 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.4](https://github.com/sasjs/pacman/compare/v1.0.3...v1.0.4) (2022-08-03) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * tweaks to get URLs to work on Viya ([57551a3](https://github.com/sasjs/pacman/commit/57551a316a386678ef5149e3543994392bb5c36e)) 7 | 8 | ## [1.0.3](https://github.com/sasjs/pacman/compare/v1.0.2...v1.0.3) (2022-08-03) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * JS in pacman ([6fb662a](https://github.com/sasjs/pacman/commit/6fb662a3e20093a20087e7760e92c0dedf8ed71d)) 14 | 15 | ## [1.0.2](https://github.com/sasjs/pacman/compare/v1.0.1...v1.0.2) (2022-08-02) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * minor adjustments ([2b0fec4](https://github.com/sasjs/pacman/commit/2b0fec46f5bc40f2d97a794995c76efe396ba73d)) 21 | 22 | ## [1.0.1](https://github.com/sasjs/pacman/compare/v1.0.0...v1.0.1) (2022-08-02) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * link in readme, logo in sasjs/server ([471b193](https://github.com/sasjs/pacman/commit/471b1936fd900e07cae7d2ebeaccbdb82b230ed5)) 28 | 29 | # 1.0.0 (2022-08-02) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * move original files to src folder ([aa91ded](https://github.com/sasjs/pacman/commit/aa91dedda9cc02c926c57d682524ccd2e4d1d3a5)) 35 | * semantic release ([984505a](https://github.com/sasjs/pacman/commit/984505a542bc29d46957745391c68e81a81ae392)) 36 | 37 | 38 | ### Features 39 | 40 | * remaining assets ([6657f22](https://github.com/sasjs/pacman/commit/6657f22d43a4c09209ba416ae578a7022d78d405)) 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Push SASjs Deployment Assets 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [lts/*] 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | env: 28 | CI: true 29 | 30 | - name: create SAS 9 EBI build and Add to Release 31 | run: | 32 | npx sasjs cb -t sas9 33 | mkdir ./artefacts 34 | cp ./sasjsbuild/sas9.sas ./artefacts/sas9.sas 35 | 36 | - name: create Viya build and Add to Release 37 | run: | 38 | npx sasjs cb -t viya 39 | cp ./sasjsbuild/viya.sas ./artefacts/viya.sas 40 | 41 | - name: create SASjs build and Add to Release 42 | run: | 43 | npx sasjs cb -t server 44 | cp ./sasjsbuild/server.json.zip ./artefacts/server.json.zip 45 | 46 | - name: Install Semantic Release and plugins 47 | run: | 48 | npm i 49 | npm i -g semantic-release 50 | - name: Release 51 | run: | 52 | GITHUB_TOKEN=${{ secrets.GH_TOKEN }} semantic-release 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pacman for SAS 2 | 3 | Thanks to [Dale Harvey](https://github.com/daleharvey) for this very [permissively licensed](https://github.com/daleharvey/pacman/blob/master/LICENSE) pacman game! 4 | 5 | ![](src/pacman.png) 6 | 7 | The aim of this repo is to demonstrate the ease with which apps can be streamed in SAS Viya, EBI, and SASjs Server. 8 | 9 | ## Deployment 10 | 11 | If you just want to deploy the game in your SAS environment, in all cases you need to download the relevant asset from the [releases](https://github.com/sasjs/pacman/releases) page. 12 | 13 | * SAS 9 - Open `sas9.sas` in Enterprise Guide or Studio. Modify line 2 with your preferred appLoc. Execute and open the link at the end of the log. 14 | * Viya - Open `viya.sas` in SAS Studio. Execute and open the link at the end of the log. 15 | * SASjs Server - just import the `server.json.zip` file to AppStream 16 | 17 | ## Build 18 | 19 | If you would like to build the app from source, you first need to [install](https://cli.sasjs.io/installation) the SASjs CLI. 20 | 21 | Then, update the `defaultTarget` attribute of the `sasjs/sasjsconfig.json` according to whether you are running SASjs `server`, `sas9` EBI, or `viya`. You should also update the `serverUrl` and `appLoc` attributes of the relevant target, to determine to which server and logical folder you will deploy. 22 | 23 | Instructions thereafter: 24 | 25 | ```bash 26 | npm i 27 | sasjs auth 28 | sasjs cbd 29 | ``` 30 | 31 | You can then click the link at the bottom of the console to launch! 32 | 33 | ## Demo 34 | 35 | This video demonstrates a deployment onto SAS Viya. 36 | 37 | [![Pacman on SAS](https://img.youtube.com/vi/uSZw2d_NOHg/0.jpg)](http://www.youtube.com/watch?v=uSZw2d_NOHg) 38 | 39 | 40 | If you'd like to publish an app with SASjs, we're happy to help - contact us [here](https://sasapps.io/contact). 41 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HTML5 Pacman 7 | 8 | 44 | 45 | 46 | 47 | 48 | 49 |
shim for font face
50 | 51 |

HTML5 Pacman

52 | 53 | 54 | Writeup | 55 | Code on Github 56 | 57 |
58 | 59 | 60 | 61 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/modernizr-1.5.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Modernizr JavaScript library 1.5 3 | * http://www.modernizr.com/ 4 | * 5 | * Copyright (c) 2009-2010 Faruk Ates - http://farukat.es/ 6 | * Dual-licensed under the BSD and MIT licenses. 7 | * http://www.modernizr.com/license/ 8 | * 9 | * Featuring major contributions by 10 | * Paul Irish - http://paulirish.com 11 | */ 12 | window.Modernizr=function(i,e,I){function C(a,b){for(var c in a)if(m[a[c]]!==I&&(!b||b(a[c],D)))return true}function r(a,b){var c=a.charAt(0).toUpperCase()+a.substr(1);return!!C([a,"Webkit"+c,"Moz"+c,"O"+c,"ms"+c,"Khtml"+c],b)}function P(){j[E]=function(a){for(var b=0,c=a.length;b7)};d.historymanagement=function(){return!!(i.history&&history.pushState)};d.draganddrop=function(){return u("drag")&&u("dragstart")&&u("dragenter")&&u("dragover")&&u("dragleave")&&u("dragend")&&u("drop")};d.websockets=function(){return"WebSocket"in i};d.rgba=function(){m.cssText="background-color:rgba(150,255,150,.5)";return(""+m.backgroundColor).indexOf("rgba")!==-1};d.hsla=function(){m.cssText="background-color:hsla(120,40%,100%,.5)";return(""+ 17 | m.backgroundColor).indexOf("rgba")!==-1};d.multiplebgs=function(){m.cssText="background:url(//:),url(//:),red url(//:)";return/(url\s*\(.*?){3}/.test(m.background)};d.backgroundsize=function(){return r("backgroundSize")};d.borderimage=function(){return r("borderImage")};d.borderradius=function(){return r("borderRadius","",function(a){return(""+a).indexOf("orderRadius")!==-1})};d.boxshadow=function(){return r("boxShadow")};d.opacity=function(){var a=y.join("opacity:.5;")+"";m.cssText=a;return(""+m.opacity).indexOf("0.5")!== 18 | -1};d.cssanimations=function(){return r("animationName")};d.csscolumns=function(){return r("columnCount")};d.cssgradients=function(){var a=("background-image:"+y.join("gradient(linear,left top,right bottom,from(#9f9),to(white));background-image:")+y.join("linear-gradient(left top,#9f9, white);background-image:")).slice(0,-17);m.cssText=a;return(""+m.backgroundImage).indexOf("gradient")!==-1};d.cssreflections=function(){return r("boxReflect")};d.csstransforms=function(){return!!C(["transformProperty", 19 | "WebkitTransform","MozTransform","OTransform","msTransform"])};d.csstransforms3d=function(){var a=!!C(["perspectiveProperty","WebkitPerspective","MozPerspective","OPerspective","msPerspective"]);if(a){var b=document.createElement("style"),c=e.createElement("div");b.textContent="@media ("+y.join("transform-3d),(")+"modernizr){#modernizr{height:3px}}";e.getElementsByTagName("head")[0].appendChild(b);c.id="modernizr";s.appendChild(c);a=c.offsetHeight===3;b.parentNode.removeChild(b);c.parentNode.removeChild(c)}return a}; 20 | d.csstransitions=function(){return r("transitionProperty")};d.fontface=function(){var a;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)a=true;else{var b=e.createElement("style"),c=e.createElement("span"),h,t=false,g=e.body,o,w;b.textContent="@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}"; 21 | e.getElementsByTagName("head")[0].appendChild(b);c.setAttribute("style","font:99px _,arial,helvetica;position:absolute;visibility:hidden");if(!g){g=s.appendChild(e.createElement("fontface"));t=true}c.innerHTML="........";c.id="fonttest";g.appendChild(c);h=c.offsetWidth*c.offsetHeight;c.style.font="99px testfont,_,arial,helvetica";a=h!==c.offsetWidth*c.offsetHeight;var v=function(){if(g.parentNode){a=j.fontface=h!==c.offsetWidth*c.offsetHeight;s.className=s.className.replace(/(no-)?fontface\b/,"")+ 22 | (a?" ":" no-")+"fontface"}};setTimeout(v,75);setTimeout(v,150);addEventListener("load",function(){v();(w=true)&&o&&o(a);setTimeout(function(){t||(g=c);g.parentNode.removeChild(g);b.parentNode.removeChild(b)},50)},false)}j._fontfaceready=function(p){w||a?p(a):(o=p)};return a||h!==c.offsetWidth};d.video=function(){var a=e.createElement("video"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('video/ogg; codecs="theora"');b.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"');b.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"')}return b}; 23 | d.audio=function(){var a=e.createElement("audio"),b=!!a.canPlayType;if(b){b=new Boolean(b);b.ogg=a.canPlayType('audio/ogg; codecs="vorbis"');b.mp3=a.canPlayType("audio/mpeg;");b.wav=a.canPlayType('audio/wav; codecs="1"');b.m4a=a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")}return b};d.localStorage=function(){return"localStorage"in i&&i.localStorage!==null};d.sessionStorage=function(){try{return"sessionStorage"in i&&i.sessionStorage!==null}catch(a){return false}};d.webworkers=function(){return!!i.Worker}; 24 | d.applicationCache=function(){var a=i.applicationCache;return!!(a&&typeof a.status!="undefined"&&typeof a.update=="function"&&typeof a.swapCache=="function")};d.svg=function(){return!!e.createElementNS&&!!e.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect};d.smil=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg","animate")))};d.svgclippaths=function(){return!!e.createElementNS&&/SVG/.test(M.call(e.createElementNS("http://www.w3.org/2000/svg", 25 | "clipPath")))};for(var z in d)if(O(d,z))N.push(((j[z.toLowerCase()]=d[z]())?"":"no-")+z.toLowerCase());j[E]||P();j.addTest=function(a,b){a=a.toLowerCase();if(!j[a]){b=!!b();s.className+=" "+(b?"":"no-")+a;j[a]=b;return j}};m.cssText="";D=n=null;(function(){var a=e.createElement("div");a.innerHTML="";return a.childNodes.length!==1})()&&function(a,b){function c(f,k){if(o[f])o[f].styleSheet.cssText+=k;else{var l=t[G],q=b[A]("style");q.media=f;l.insertBefore(q,l[G]);o[f]=q;c(f,k)}}function h(f, 26 | k){for(var l=new RegExp("\\b("+w+")\\b(?!.*[;}])","gi"),q=function(B){return".iepp_"+B},x=-1;++x\\s*$","i");g.innerHTML=f.outerHTML.replace(/\r|\n/g," ").replace(l,f.currentStyle.display=="block"?"":"");l=g.childNodes[0];l.className+=" iepp_"+q;l=p[p.length]=[f,l];f.parentNode.replaceChild(l[1],l[0])}h(b.styleSheets,"all")});a.attachEvent("onafterprint", 28 | function(){for(var f=-1,k;++f 10) { 53 | return x1 + (10 - rem); 54 | } else if (rem > 0 && result < 0) { 55 | return x1 - rem; 56 | } 57 | return x1 + x2; 58 | }; 59 | 60 | function isVunerable() { 61 | return eatable !== null; 62 | }; 63 | 64 | function isDangerous() { 65 | return eaten === null; 66 | }; 67 | 68 | function isHidden() { 69 | return eatable === null && eaten !== null; 70 | }; 71 | 72 | function getRandomDirection() { 73 | var moves = (direction === LEFT || direction === RIGHT) 74 | ? [UP, DOWN] : [LEFT, RIGHT]; 75 | return moves[Math.floor(Math.random() * 2)]; 76 | }; 77 | 78 | function reset() { 79 | eaten = null; 80 | eatable = null; 81 | position = { "x": 90, "y": 80 }; 82 | direction = getRandomDirection(); 83 | due = getRandomDirection(); 84 | }; 85 | 86 | function onWholeSquare(x) { 87 | return x % 10 === 0; 88 | }; 89 | 90 | function oppositeDirection(dir) { 91 | return dir === LEFT && RIGHT || 92 | dir === RIGHT && LEFT || 93 | dir === UP && DOWN || UP; 94 | }; 95 | 96 | function makeEatable() { 97 | direction = oppositeDirection(direction); 98 | eatable = game.getTick(); 99 | }; 100 | 101 | function eat() { 102 | eatable = null; 103 | eaten = game.getTick(); 104 | }; 105 | 106 | function pointToCoord(x) { 107 | return Math.round(x / 10); 108 | }; 109 | 110 | function nextSquare(x, dir) { 111 | var rem = x % 10; 112 | if (rem === 0) { 113 | return x; 114 | } else if (dir === RIGHT || dir === DOWN) { 115 | return x + (10 - rem); 116 | } else { 117 | return x - rem; 118 | } 119 | }; 120 | 121 | function onGridSquare(pos) { 122 | return onWholeSquare(pos.y) && onWholeSquare(pos.x); 123 | }; 124 | 125 | function secondsAgo(tick) { 126 | return (game.getTick() - tick) / Pacman.FPS; 127 | }; 128 | 129 | function getColour() { 130 | if (eatable) { 131 | if (secondsAgo(eatable) > 5) { 132 | return game.getTick() % 20 > 10 ? "#FFFFFF" : "#0000BB"; 133 | } else { 134 | return "#0000BB"; 135 | } 136 | } else if (eaten) { 137 | return "#222"; 138 | } 139 | return colour; 140 | }; 141 | 142 | function draw(ctx) { 143 | 144 | var s = map.blockSize, 145 | top = (position.y / 10) * s, 146 | left = (position.x / 10) * s; 147 | 148 | if (eatable && secondsAgo(eatable) > 8) { 149 | eatable = null; 150 | } 151 | 152 | if (eaten && secondsAgo(eaten) > 3) { 153 | eaten = null; 154 | } 155 | 156 | var tl = left + s; 157 | var base = top + s - 3; 158 | var inc = s / 10; 159 | 160 | var high = game.getTick() % 10 > 5 ? 3 : -3; 161 | var low = game.getTick() % 10 > 5 ? -3 : 3; 162 | 163 | ctx.fillStyle = getColour(); 164 | ctx.beginPath(); 165 | 166 | ctx.moveTo(left, base); 167 | 168 | ctx.quadraticCurveTo(left, top, left + (s / 2), top); 169 | ctx.quadraticCurveTo(left + s, top, left + s, base); 170 | 171 | // Wavy things at the bottom 172 | ctx.quadraticCurveTo(tl - (inc * 1), base + high, tl - (inc * 2), base); 173 | ctx.quadraticCurveTo(tl - (inc * 3), base + low, tl - (inc * 4), base); 174 | ctx.quadraticCurveTo(tl - (inc * 5), base + high, tl - (inc * 6), base); 175 | ctx.quadraticCurveTo(tl - (inc * 7), base + low, tl - (inc * 8), base); 176 | ctx.quadraticCurveTo(tl - (inc * 9), base + high, tl - (inc * 10), base); 177 | 178 | ctx.closePath(); 179 | ctx.fill(); 180 | 181 | ctx.beginPath(); 182 | ctx.fillStyle = "#FFF"; 183 | ctx.arc(left + 6, top + 6, s / 6, 0, 300, false); 184 | ctx.arc((left + s) - 6, top + 6, s / 6, 0, 300, false); 185 | ctx.closePath(); 186 | ctx.fill(); 187 | 188 | var f = s / 12; 189 | var off = {}; 190 | off[RIGHT] = [f, 0]; 191 | off[LEFT] = [-f, 0]; 192 | off[UP] = [0, -f]; 193 | off[DOWN] = [0, f]; 194 | 195 | ctx.beginPath(); 196 | ctx.fillStyle = "#000"; 197 | ctx.arc(left + 6 + off[direction][0], top + 6 + off[direction][1], 198 | s / 15, 0, 300, false); 199 | ctx.arc((left + s) - 6 + off[direction][0], top + 6 + off[direction][1], 200 | s / 15, 0, 300, false); 201 | ctx.closePath(); 202 | ctx.fill(); 203 | 204 | }; 205 | 206 | function pane(pos) { 207 | 208 | if (pos.y === 100 && pos.x >= 190 && direction === RIGHT) { 209 | return { "y": 100, "x": -10 }; 210 | } 211 | 212 | if (pos.y === 100 && pos.x <= -10 && direction === LEFT) { 213 | return position = { "y": 100, "x": 190 }; 214 | } 215 | 216 | return false; 217 | }; 218 | 219 | function move(ctx) { 220 | 221 | var oldPos = position, 222 | onGrid = onGridSquare(position), 223 | npos = null; 224 | 225 | if (due !== direction) { 226 | 227 | npos = getNewCoord(due, position); 228 | 229 | if (onGrid && 230 | map.isFloorSpace({ 231 | "y": pointToCoord(nextSquare(npos.y, due)), 232 | "x": pointToCoord(nextSquare(npos.x, due)) 233 | })) { 234 | direction = due; 235 | } else { 236 | npos = null; 237 | } 238 | } 239 | 240 | if (npos === null) { 241 | npos = getNewCoord(direction, position); 242 | } 243 | 244 | if (onGrid && 245 | map.isWallSpace({ 246 | "y": pointToCoord(nextSquare(npos.y, direction)), 247 | "x": pointToCoord(nextSquare(npos.x, direction)) 248 | })) { 249 | 250 | due = getRandomDirection(); 251 | return move(ctx); 252 | } 253 | 254 | position = npos; 255 | 256 | var tmp = pane(position); 257 | if (tmp) { 258 | position = tmp; 259 | } 260 | 261 | due = getRandomDirection(); 262 | 263 | return { 264 | "new": position, 265 | "old": oldPos 266 | }; 267 | }; 268 | 269 | return { 270 | "eat": eat, 271 | "isVunerable": isVunerable, 272 | "isDangerous": isDangerous, 273 | "makeEatable": makeEatable, 274 | "reset": reset, 275 | "move": move, 276 | "draw": draw 277 | }; 278 | }; 279 | 280 | Pacman.User = function (game, map) { 281 | 282 | var position = null, 283 | direction = null, 284 | eaten = null, 285 | due = null, 286 | lives = null, 287 | score = 5, 288 | keyMap = {}; 289 | 290 | keyMap[KEY.ARROW_LEFT] = LEFT; 291 | keyMap[KEY.ARROW_UP] = UP; 292 | keyMap[KEY.ARROW_RIGHT] = RIGHT; 293 | keyMap[KEY.ARROW_DOWN] = DOWN; 294 | 295 | function addScore(nScore) { 296 | score += nScore; 297 | if (score >= 10000 && score - nScore < 10000) { 298 | lives += 1; 299 | } 300 | }; 301 | 302 | function theScore() { 303 | return score; 304 | }; 305 | 306 | function loseLife() { 307 | lives -= 1; 308 | }; 309 | 310 | function getLives() { 311 | return lives; 312 | }; 313 | 314 | function initUser() { 315 | score = 0; 316 | lives = 3; 317 | newLevel(); 318 | } 319 | 320 | function newLevel() { 321 | resetPosition(); 322 | eaten = 0; 323 | }; 324 | 325 | function resetPosition() { 326 | position = { "x": 90, "y": 120 }; 327 | direction = LEFT; 328 | due = LEFT; 329 | }; 330 | 331 | function reset() { 332 | initUser(); 333 | resetPosition(); 334 | }; 335 | 336 | function keyDown(e) { 337 | if (typeof keyMap[e.keyCode] !== "undefined") { 338 | due = keyMap[e.keyCode]; 339 | e.preventDefault(); 340 | e.stopPropagation(); 341 | return false; 342 | } 343 | return true; 344 | }; 345 | 346 | function getNewCoord(dir, current) { 347 | return { 348 | "x": current.x + (dir === LEFT && -2 || dir === RIGHT && 2 || 0), 349 | "y": current.y + (dir === DOWN && 2 || dir === UP && -2 || 0) 350 | }; 351 | }; 352 | 353 | function onWholeSquare(x) { 354 | return x % 10 === 0; 355 | }; 356 | 357 | function pointToCoord(x) { 358 | return Math.round(x / 10); 359 | }; 360 | 361 | function nextSquare(x, dir) { 362 | var rem = x % 10; 363 | if (rem === 0) { 364 | return x; 365 | } else if (dir === RIGHT || dir === DOWN) { 366 | return x + (10 - rem); 367 | } else { 368 | return x - rem; 369 | } 370 | }; 371 | 372 | function next(pos, dir) { 373 | return { 374 | "y": pointToCoord(nextSquare(pos.y, dir)), 375 | "x": pointToCoord(nextSquare(pos.x, dir)), 376 | }; 377 | }; 378 | 379 | function onGridSquare(pos) { 380 | return onWholeSquare(pos.y) && onWholeSquare(pos.x); 381 | }; 382 | 383 | function isOnSamePlane(due, dir) { 384 | return ((due === LEFT || due === RIGHT) && 385 | (dir === LEFT || dir === RIGHT)) || 386 | ((due === UP || due === DOWN) && 387 | (dir === UP || dir === DOWN)); 388 | }; 389 | 390 | function move(ctx) { 391 | 392 | var npos = null, 393 | nextWhole = null, 394 | oldPosition = position, 395 | block = null; 396 | 397 | if (due !== direction) { 398 | npos = getNewCoord(due, position); 399 | 400 | if (isOnSamePlane(due, direction) || 401 | (onGridSquare(position) && 402 | map.isFloorSpace(next(npos, due)))) { 403 | direction = due; 404 | } else { 405 | npos = null; 406 | } 407 | } 408 | 409 | if (npos === null) { 410 | npos = getNewCoord(direction, position); 411 | } 412 | 413 | if (onGridSquare(position) && map.isWallSpace(next(npos, direction))) { 414 | direction = NONE; 415 | } 416 | 417 | if (direction === NONE) { 418 | return { "new": position, "old": position }; 419 | } 420 | 421 | if (npos.y === 100 && npos.x >= 190 && direction === RIGHT) { 422 | npos = { "y": 100, "x": -10 }; 423 | } 424 | 425 | if (npos.y === 100 && npos.x <= -12 && direction === LEFT) { 426 | npos = { "y": 100, "x": 190 }; 427 | } 428 | 429 | position = npos; 430 | nextWhole = next(position, direction); 431 | 432 | block = map.block(nextWhole); 433 | 434 | if ((isMidSquare(position.y) || isMidSquare(position.x)) && 435 | block === Pacman.BISCUIT || block === Pacman.PILL) { 436 | 437 | map.setBlock(nextWhole, Pacman.EMPTY); 438 | addScore((block === Pacman.BISCUIT) ? 10 : 50); 439 | eaten += 1; 440 | 441 | if (eaten === 182) { 442 | game.completedLevel(); 443 | } 444 | 445 | if (block === Pacman.PILL) { 446 | game.eatenPill(); 447 | } 448 | } 449 | 450 | return { 451 | "new": position, 452 | "old": oldPosition 453 | }; 454 | }; 455 | 456 | function isMidSquare(x) { 457 | var rem = x % 10; 458 | return rem > 3 || rem < 7; 459 | }; 460 | 461 | function calcAngle(dir, pos) { 462 | if (dir == RIGHT && (pos.x % 10 < 5)) { 463 | return { "start": 0.25, "end": 1.75, "direction": false }; 464 | } else if (dir === DOWN && (pos.y % 10 < 5)) { 465 | return { "start": 0.75, "end": 2.25, "direction": false }; 466 | } else if (dir === UP && (pos.y % 10 < 5)) { 467 | return { "start": 1.25, "end": 1.75, "direction": true }; 468 | } else if (dir === LEFT && (pos.x % 10 < 5)) { 469 | return { "start": 0.75, "end": 1.25, "direction": true }; 470 | } 471 | return { "start": 0, "end": 2, "direction": false }; 472 | }; 473 | 474 | function drawDead(ctx, amount) { 475 | 476 | var size = map.blockSize, 477 | half = size / 2; 478 | 479 | if (amount >= 1) { 480 | return; 481 | } 482 | 483 | ctx.fillStyle = "#FFFF00"; 484 | ctx.beginPath(); 485 | ctx.moveTo(((position.x / 10) * size) + half, 486 | ((position.y / 10) * size) + half); 487 | 488 | ctx.arc(((position.x / 10) * size) + half, 489 | ((position.y / 10) * size) + half, 490 | half, 0, Math.PI * 2 * amount, true); 491 | 492 | ctx.fill(); 493 | }; 494 | 495 | function draw(ctx) { 496 | 497 | var s = map.blockSize, 498 | angle = calcAngle(direction, position); 499 | 500 | ctx.fillStyle = "#FFFF00"; 501 | 502 | ctx.beginPath(); 503 | 504 | ctx.moveTo(((position.x / 10) * s) + s / 2, 505 | ((position.y / 10) * s) + s / 2); 506 | 507 | ctx.arc(((position.x / 10) * s) + s / 2, 508 | ((position.y / 10) * s) + s / 2, 509 | s / 2, Math.PI * angle.start, 510 | Math.PI * angle.end, angle.direction); 511 | 512 | ctx.fill(); 513 | }; 514 | 515 | initUser(); 516 | 517 | return { 518 | "draw": draw, 519 | "drawDead": drawDead, 520 | "loseLife": loseLife, 521 | "getLives": getLives, 522 | "score": score, 523 | "addScore": addScore, 524 | "theScore": theScore, 525 | "keyDown": keyDown, 526 | "move": move, 527 | "newLevel": newLevel, 528 | "reset": reset, 529 | "resetPosition": resetPosition 530 | }; 531 | }; 532 | 533 | Pacman.Map = function (size) { 534 | 535 | var height = null, 536 | width = null, 537 | blockSize = size, 538 | pillSize = 0, 539 | map = null; 540 | 541 | function withinBounds(y, x) { 542 | return y >= 0 && y < height && x >= 0 && x < width; 543 | } 544 | 545 | function isWall(pos) { 546 | return withinBounds(pos.y, pos.x) && map[pos.y][pos.x] === Pacman.WALL; 547 | } 548 | 549 | function isFloorSpace(pos) { 550 | if (!withinBounds(pos.y, pos.x)) { 551 | return false; 552 | } 553 | var peice = map[pos.y][pos.x]; 554 | return peice === Pacman.EMPTY || 555 | peice === Pacman.BISCUIT || 556 | peice === Pacman.PILL; 557 | } 558 | 559 | function drawWall(ctx) { 560 | 561 | var i, j, p, line; 562 | 563 | ctx.strokeStyle = "#0000FF"; 564 | ctx.lineWidth = 5; 565 | ctx.lineCap = "round"; 566 | 567 | for (i = 0; i < Pacman.WALLS.length; i += 1) { 568 | line = Pacman.WALLS[i]; 569 | ctx.beginPath(); 570 | 571 | for (j = 0; j < line.length; j += 1) { 572 | 573 | p = line[j]; 574 | 575 | if (p.move) { 576 | ctx.moveTo(p.move[0] * blockSize, p.move[1] * blockSize); 577 | } else if (p.line) { 578 | ctx.lineTo(p.line[0] * blockSize, p.line[1] * blockSize); 579 | } else if (p.curve) { 580 | ctx.quadraticCurveTo(p.curve[0] * blockSize, 581 | p.curve[1] * blockSize, 582 | p.curve[2] * blockSize, 583 | p.curve[3] * blockSize); 584 | } 585 | } 586 | ctx.stroke(); 587 | } 588 | } 589 | 590 | function reset() { 591 | map = Pacman.MAP.clone(); 592 | height = map.length; 593 | width = map[0].length; 594 | }; 595 | 596 | function block(pos) { 597 | return map[pos.y][pos.x]; 598 | }; 599 | 600 | function setBlock(pos, type) { 601 | map[pos.y][pos.x] = type; 602 | }; 603 | 604 | function drawPills(ctx) { 605 | 606 | if (++pillSize > 30) { 607 | pillSize = 0; 608 | } 609 | 610 | for (i = 0; i < height; i += 1) { 611 | for (j = 0; j < width; j += 1) { 612 | if (map[i][j] === Pacman.PILL) { 613 | ctx.beginPath(); 614 | 615 | ctx.fillStyle = "#000"; 616 | ctx.fillRect((j * blockSize), (i * blockSize), 617 | blockSize, blockSize); 618 | 619 | ctx.fillStyle = "#FFF"; 620 | ctx.arc((j * blockSize) + blockSize / 2, 621 | (i * blockSize) + blockSize / 2, 622 | Math.abs(5 - (pillSize / 3)), 623 | 0, 624 | Math.PI * 2, false); 625 | ctx.fill(); 626 | ctx.closePath(); 627 | } 628 | } 629 | } 630 | }; 631 | 632 | function draw(ctx) { 633 | 634 | var i, j, size = blockSize; 635 | 636 | ctx.fillStyle = "#000"; 637 | ctx.fillRect(0, 0, width * size, height * size); 638 | 639 | drawWall(ctx); 640 | 641 | for (i = 0; i < height; i += 1) { 642 | for (j = 0; j < width; j += 1) { 643 | drawBlock(i, j, ctx); 644 | } 645 | } 646 | }; 647 | 648 | function drawBlock(y, x, ctx) { 649 | 650 | var layout = map[y][x]; 651 | 652 | if (layout === Pacman.PILL) { 653 | return; 654 | } 655 | 656 | ctx.beginPath(); 657 | 658 | if (layout === Pacman.EMPTY || layout === Pacman.BLOCK || 659 | layout === Pacman.BISCUIT) { 660 | 661 | ctx.fillStyle = "#000"; 662 | ctx.fillRect((x * blockSize), (y * blockSize), 663 | blockSize, blockSize); 664 | 665 | if (layout === Pacman.BISCUIT) { 666 | ctx.fillStyle = "#FFF"; 667 | ctx.fillRect((x * blockSize) + (blockSize / 2.5), 668 | (y * blockSize) + (blockSize / 2.5), 669 | blockSize / 6, blockSize / 6); 670 | } 671 | } 672 | ctx.closePath(); 673 | }; 674 | 675 | reset(); 676 | 677 | return { 678 | "draw": draw, 679 | "drawBlock": drawBlock, 680 | "drawPills": drawPills, 681 | "block": block, 682 | "setBlock": setBlock, 683 | "reset": reset, 684 | "isWallSpace": isWall, 685 | "isFloorSpace": isFloorSpace, 686 | "height": height, 687 | "width": width, 688 | "blockSize": blockSize 689 | }; 690 | }; 691 | 692 | Pacman.Audio = function (game) { 693 | 694 | var files = [], 695 | endEvents = [], 696 | progressEvents = [], 697 | playing = []; 698 | 699 | function load(name, path, cb) { 700 | 701 | var f = files[name] = document.createElement("audio"); 702 | 703 | progressEvents[name] = function (event) { progress(event, name, cb); }; 704 | 705 | f.addEventListener("canplaythrough", progressEvents[name], true); 706 | f.setAttribute("preload", "true"); 707 | f.setAttribute("autobuffer", "true"); 708 | f.setAttribute("src", path); 709 | f.pause(); 710 | }; 711 | 712 | function progress(event, name, callback) { 713 | if (event.loaded === event.total && typeof callback === "function") { 714 | callback(); 715 | files[name].removeEventListener("canplaythrough", 716 | progressEvents[name], true); 717 | } 718 | }; 719 | 720 | function disableSound() { 721 | for (var i = 0; i < playing.length; i++) { 722 | files[playing[i]].pause(); 723 | files[playing[i]].currentTime = 0; 724 | } 725 | playing = []; 726 | }; 727 | 728 | function ended(name) { 729 | 730 | var i, tmp = [], found = false; 731 | 732 | files[name].removeEventListener("ended", endEvents[name], true); 733 | 734 | for (i = 0; i < playing.length; i++) { 735 | if (!found && playing[i]) { 736 | found = true; 737 | } else { 738 | tmp.push(playing[i]); 739 | } 740 | } 741 | playing = tmp; 742 | }; 743 | 744 | function play(name) { 745 | if (!game.soundDisabled()) { 746 | endEvents[name] = function () { ended(name); }; 747 | playing.push(name); 748 | files[name].addEventListener("ended", endEvents[name], true); 749 | files[name].play(); 750 | } 751 | }; 752 | 753 | function pause() { 754 | for (var i = 0; i < playing.length; i++) { 755 | files[playing[i]].pause(); 756 | } 757 | }; 758 | 759 | function resume() { 760 | for (var i = 0; i < playing.length; i++) { 761 | files[playing[i]].play(); 762 | } 763 | }; 764 | 765 | return { 766 | "disableSound": disableSound, 767 | "load": load, 768 | "play": play, 769 | "pause": pause, 770 | "resume": resume 771 | }; 772 | }; 773 | 774 | var PACMAN = (function () { 775 | 776 | var state = WAITING, 777 | audio = null, 778 | ghosts = [], 779 | ghostSpecs = ["#00FFDE", "#FF0000", "#FFB8DE", "#FFB847"], 780 | eatenCount = 0, 781 | level = 0, 782 | tick = 0, 783 | ghostPos, userPos, 784 | stateChanged = true, 785 | timerStart = null, 786 | lastTime = 0, 787 | ctx = null, 788 | timer = null, 789 | map = null, 790 | user = null, 791 | stored = null; 792 | 793 | function getTick() { 794 | return tick; 795 | }; 796 | 797 | function drawScore(text, position) { 798 | ctx.fillStyle = "#FFFFFF"; 799 | ctx.font = "12px BDCartoonShoutRegular"; 800 | ctx.fillText(text, 801 | (position["new"]["x"] / 10) * map.blockSize, 802 | ((position["new"]["y"] + 5) / 10) * map.blockSize); 803 | } 804 | 805 | function dialog(text) { 806 | ctx.fillStyle = "#FFFF00"; 807 | ctx.font = "14px BDCartoonShoutRegular"; 808 | var width = ctx.measureText(text).width, 809 | x = ((map.width * map.blockSize) - width) / 2; 810 | ctx.fillText(text, x, (map.height * 10) + 8); 811 | } 812 | 813 | function soundDisabled() { 814 | return localStorage["soundDisabled"] === "true"; 815 | }; 816 | 817 | function startLevel() { 818 | user.resetPosition(); 819 | for (var i = 0; i < ghosts.length; i += 1) { 820 | ghosts[i].reset(); 821 | } 822 | audio.play("start"); 823 | timerStart = tick; 824 | setState(COUNTDOWN); 825 | } 826 | 827 | function startNewGame() { 828 | setState(WAITING); 829 | level = 1; 830 | user.reset(); 831 | map.reset(); 832 | map.draw(ctx); 833 | startLevel(); 834 | } 835 | 836 | function keyDown(e) { 837 | if (e.keyCode === KEY.N) { 838 | startNewGame(); 839 | } else if (e.keyCode === KEY.S) { 840 | audio.disableSound(); 841 | localStorage["soundDisabled"] = !soundDisabled(); 842 | } else if (e.keyCode === KEY.P && state === PAUSE) { 843 | audio.resume(); 844 | map.draw(ctx); 845 | setState(stored); 846 | } else if (e.keyCode === KEY.P) { 847 | stored = state; 848 | setState(PAUSE); 849 | audio.pause(); 850 | map.draw(ctx); 851 | dialog("Paused"); 852 | } else if (state !== PAUSE) { 853 | return user.keyDown(e); 854 | } 855 | return true; 856 | } 857 | 858 | function loseLife() { 859 | setState(WAITING); 860 | user.loseLife(); 861 | if (user.getLives() > 0) { 862 | startLevel(); 863 | } 864 | } 865 | 866 | function setState(nState) { 867 | state = nState; 868 | stateChanged = true; 869 | }; 870 | 871 | function collided(user, ghost) { 872 | return (Math.sqrt(Math.pow(ghost.x - user.x, 2) + 873 | Math.pow(ghost.y - user.y, 2))) < 10; 874 | }; 875 | 876 | function drawFooter() { 877 | 878 | var topLeft = (map.height * map.blockSize), 879 | textBase = topLeft + 17; 880 | 881 | ctx.fillStyle = "#000000"; 882 | ctx.fillRect(0, topLeft, (map.width * map.blockSize), 30); 883 | 884 | ctx.fillStyle = "#FFFF00"; 885 | 886 | for (var i = 0, len = user.getLives(); i < len; i++) { 887 | ctx.fillStyle = "#FFFF00"; 888 | ctx.beginPath(); 889 | ctx.moveTo(150 + (25 * i) + map.blockSize / 2, 890 | (topLeft + 1) + map.blockSize / 2); 891 | 892 | ctx.arc(150 + (25 * i) + map.blockSize / 2, 893 | (topLeft + 1) + map.blockSize / 2, 894 | map.blockSize / 2, Math.PI * 0.25, Math.PI * 1.75, false); 895 | ctx.fill(); 896 | } 897 | 898 | ctx.fillStyle = !soundDisabled() ? "#00FF00" : "#FF0000"; 899 | ctx.font = "bold 16px sans-serif"; 900 | //ctx.fillText("♪", 10, textBase); 901 | ctx.fillText("s", 10, textBase); 902 | 903 | ctx.fillStyle = "#FFFF00"; 904 | ctx.font = "14px BDCartoonShoutRegular"; 905 | ctx.fillText("Score: " + user.theScore(), 30, textBase); 906 | ctx.fillText("Level: " + level, 260, textBase); 907 | } 908 | 909 | function redrawBlock(pos) { 910 | map.drawBlock(Math.floor(pos.y / 10), Math.floor(pos.x / 10), ctx); 911 | map.drawBlock(Math.ceil(pos.y / 10), Math.ceil(pos.x / 10), ctx); 912 | } 913 | 914 | function mainDraw() { 915 | 916 | var diff, u, i, len, nScore; 917 | 918 | ghostPos = []; 919 | 920 | for (i = 0, len = ghosts.length; i < len; i += 1) { 921 | ghostPos.push(ghosts[i].move(ctx)); 922 | } 923 | u = user.move(ctx); 924 | 925 | for (i = 0, len = ghosts.length; i < len; i += 1) { 926 | redrawBlock(ghostPos[i].old); 927 | } 928 | redrawBlock(u.old); 929 | 930 | for (i = 0, len = ghosts.length; i < len; i += 1) { 931 | ghosts[i].draw(ctx); 932 | } 933 | user.draw(ctx); 934 | 935 | userPos = u["new"]; 936 | 937 | for (i = 0, len = ghosts.length; i < len; i += 1) { 938 | if (collided(userPos, ghostPos[i]["new"])) { 939 | if (ghosts[i].isVunerable()) { 940 | audio.play("eatghost"); 941 | ghosts[i].eat(); 942 | eatenCount += 1; 943 | nScore = eatenCount * 50; 944 | drawScore(nScore, ghostPos[i]); 945 | user.addScore(nScore); 946 | setState(EATEN_PAUSE); 947 | timerStart = tick; 948 | } else if (ghosts[i].isDangerous()) { 949 | audio.play("die"); 950 | setState(DYING); 951 | timerStart = tick; 952 | } 953 | } 954 | } 955 | }; 956 | 957 | function mainLoop() { 958 | 959 | var diff; 960 | 961 | if (state !== PAUSE) { 962 | ++tick; 963 | } 964 | 965 | map.drawPills(ctx); 966 | 967 | if (state === PLAYING) { 968 | mainDraw(); 969 | } else if (state === WAITING && stateChanged) { 970 | stateChanged = false; 971 | map.draw(ctx); 972 | dialog("Press N to start a New game"); 973 | } else if (state === EATEN_PAUSE && 974 | (tick - timerStart) > (Pacman.FPS / 3)) { 975 | map.draw(ctx); 976 | setState(PLAYING); 977 | } else if (state === DYING) { 978 | if (tick - timerStart > (Pacman.FPS * 2)) { 979 | loseLife(); 980 | } else { 981 | redrawBlock(userPos); 982 | for (i = 0, len = ghosts.length; i < len; i += 1) { 983 | redrawBlock(ghostPos[i].old); 984 | ghostPos.push(ghosts[i].draw(ctx)); 985 | } 986 | user.drawDead(ctx, (tick - timerStart) / (Pacman.FPS * 2)); 987 | } 988 | } else if (state === COUNTDOWN) { 989 | 990 | diff = 5 + Math.floor((timerStart - tick) / Pacman.FPS); 991 | 992 | if (diff === 0) { 993 | map.draw(ctx); 994 | setState(PLAYING); 995 | } else { 996 | if (diff !== lastTime) { 997 | lastTime = diff; 998 | map.draw(ctx); 999 | dialog("Starting in: " + diff); 1000 | } 1001 | } 1002 | } 1003 | 1004 | drawFooter(); 1005 | } 1006 | 1007 | function eatenPill() { 1008 | audio.play("eatpill"); 1009 | timerStart = tick; 1010 | eatenCount = 0; 1011 | for (i = 0; i < ghosts.length; i += 1) { 1012 | ghosts[i].makeEatable(ctx); 1013 | } 1014 | }; 1015 | 1016 | function completedLevel() { 1017 | setState(WAITING); 1018 | level += 1; 1019 | map.reset(); 1020 | user.newLevel(); 1021 | startLevel(); 1022 | }; 1023 | 1024 | function keyPress(e) { 1025 | if (state !== WAITING && state !== PAUSE) { 1026 | e.preventDefault(); 1027 | e.stopPropagation(); 1028 | } 1029 | }; 1030 | 1031 | function init(wrapper, root) { 1032 | 1033 | var i, len, ghost, 1034 | blockSize = wrapper.offsetWidth / 19, 1035 | canvas = document.createElement("canvas"); 1036 | 1037 | canvas.setAttribute("width", (blockSize * 19) + "px"); 1038 | canvas.setAttribute("height", (blockSize * 22) + 30 + "px"); 1039 | 1040 | wrapper.appendChild(canvas); 1041 | 1042 | ctx = canvas.getContext('2d'); 1043 | 1044 | audio = new Pacman.Audio({ "soundDisabled": soundDisabled }); 1045 | map = new Pacman.Map(blockSize); 1046 | user = new Pacman.User({ 1047 | "completedLevel": completedLevel, 1048 | "eatenPill": eatenPill 1049 | }, map); 1050 | 1051 | for (i = 0, len = ghostSpecs.length; i < len; i += 1) { 1052 | ghost = new Pacman.Ghost({ "getTick": getTick }, map, ghostSpecs[i]); 1053 | ghosts.push(ghost); 1054 | } 1055 | 1056 | map.draw(ctx); 1057 | dialog("Loading ..."); 1058 | 1059 | if (Modernizr.audio.ogg) { 1060 | var audio_files = [ 1061 | ["start", "audio/opening_song.ogg"], 1062 | ["die", "audio/die.ogg"], 1063 | ["eatghost", "audio/eatghost.ogg"], 1064 | ["eatpill", "audio/eatpill.ogg"], 1065 | ["eating", "audio/eating.short.ogg"], 1066 | ["eating2", "audio/eating.short.ogg"] 1067 | ]; 1068 | } else { 1069 | var audio_files = [ 1070 | ["start", "audio/opening_song.mp3"], 1071 | ["die", "audio/die.mp3"], 1072 | ["eatghost", "audio/eatghost.mp3"], 1073 | ["eatpill", "audio/eatpill.mp3"], 1074 | ["eating", "audio/eating.short.mp3"], 1075 | ["eating2", "audio/eating.short.mp3"] 1076 | ]; 1077 | } 1078 | 1079 | load(audio_files, function () { loaded(); }); 1080 | }; 1081 | 1082 | function load(arr, callback) { 1083 | 1084 | if (arr.length === 0) { 1085 | callback(); 1086 | } else { 1087 | var x = arr.pop(); 1088 | audio.load(x[0], x[1], function () { load(arr, callback); }); 1089 | } 1090 | }; 1091 | 1092 | function loaded() { 1093 | 1094 | dialog("Press N to Start"); 1095 | 1096 | document.addEventListener("keydown", keyDown, true); 1097 | document.addEventListener("keypress", keyPress, true); 1098 | 1099 | timer = window.setInterval(mainLoop, 1000 / Pacman.FPS); 1100 | }; 1101 | 1102 | return { 1103 | "init": init 1104 | }; 1105 | 1106 | }()); 1107 | 1108 | /* Human readable keyCode index */ 1109 | var KEY = { 'BACKSPACE': 8, 'TAB': 9, 'NUM_PAD_CLEAR': 12, 'ENTER': 13, 'SHIFT': 16, 'CTRL': 17, 'ALT': 18, 'PAUSE': 19, 'CAPS_LOCK': 20, 'ESCAPE': 27, 'SPACEBAR': 32, 'PAGE_UP': 33, 'PAGE_DOWN': 34, 'END': 35, 'HOME': 36, 'ARROW_LEFT': 37, 'ARROW_UP': 38, 'ARROW_RIGHT': 39, 'ARROW_DOWN': 40, 'PRINT_SCREEN': 44, 'INSERT': 45, 'DELETE': 46, 'SEMICOLON': 59, 'WINDOWS_LEFT': 91, 'WINDOWS_RIGHT': 92, 'SELECT': 93, 'NUM_PAD_ASTERISK': 106, 'NUM_PAD_PLUS_SIGN': 107, 'NUM_PAD_HYPHEN-MINUS': 109, 'NUM_PAD_FULL_STOP': 110, 'NUM_PAD_SOLIDUS': 111, 'NUM_LOCK': 144, 'SCROLL_LOCK': 145, 'SEMICOLON': 186, 'EQUALS_SIGN': 187, 'COMMA': 188, 'HYPHEN-MINUS': 189, 'FULL_STOP': 190, 'SOLIDUS': 191, 'GRAVE_ACCENT': 192, 'LEFT_SQUARE_BRACKET': 219, 'REVERSE_SOLIDUS': 220, 'RIGHT_SQUARE_BRACKET': 221, 'APOSTROPHE': 222 }; 1110 | 1111 | (function () { 1112 | /* 0 - 9 */ 1113 | for (var i = 48; i <= 57; i++) { 1114 | KEY['' + (i - 48)] = i; 1115 | } 1116 | /* A - Z */ 1117 | for (i = 65; i <= 90; i++) { 1118 | KEY['' + String.fromCharCode(i)] = i; 1119 | } 1120 | /* NUM_PAD_0 - NUM_PAD_9 */ 1121 | for (i = 96; i <= 105; i++) { 1122 | KEY['NUM_PAD_' + (i - 96)] = i; 1123 | } 1124 | /* F1 - F12 */ 1125 | for (i = 112; i <= 123; i++) { 1126 | KEY['F' + (i - 112 + 1)] = i; 1127 | } 1128 | })(); 1129 | 1130 | Pacman.WALL = 0; 1131 | Pacman.BISCUIT = 1; 1132 | Pacman.EMPTY = 2; 1133 | Pacman.BLOCK = 3; 1134 | Pacman.PILL = 4; 1135 | 1136 | Pacman.MAP = [ 1137 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1138 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], 1139 | [0, 4, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 4, 0], 1140 | [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0], 1141 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 1142 | [0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0], 1143 | [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0], 1144 | [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0], 1145 | [2, 2, 2, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 2, 2, 2], 1146 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 3, 0, 0, 1, 0, 1, 0, 0, 0, 0], 1147 | [2, 2, 2, 2, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 2, 2, 2, 2], 1148 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0], 1149 | [2, 2, 2, 0, 1, 0, 1, 1, 1, 2, 1, 1, 1, 0, 1, 0, 2, 2, 2], 1150 | [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0], 1151 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], 1152 | [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0], 1153 | [0, 4, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 0], 1154 | [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0], 1155 | [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0], 1156 | [0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0], 1157 | [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], 1158 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1159 | ]; 1160 | 1161 | Pacman.WALLS = [ 1162 | 1163 | [{ "move": [0, 9.5] }, { "line": [3, 9.5] }, 1164 | { "curve": [3.5, 9.5, 3.5, 9] }, { "line": [3.5, 8] }, 1165 | { "curve": [3.5, 7.5, 3, 7.5] }, { "line": [1, 7.5] }, 1166 | { "curve": [0.5, 7.5, 0.5, 7] }, { "line": [0.5, 1] }, 1167 | { "curve": [0.5, 0.5, 1, 0.5] }, { "line": [9, 0.5] }, 1168 | { "curve": [9.5, 0.5, 9.5, 1] }, { "line": [9.5, 3.5] }], 1169 | 1170 | [{ "move": [9.5, 1] }, 1171 | { "curve": [9.5, 0.5, 10, 0.5] }, { "line": [18, 0.5] }, 1172 | { "curve": [18.5, 0.5, 18.5, 1] }, { "line": [18.5, 7] }, 1173 | { "curve": [18.5, 7.5, 18, 7.5] }, { "line": [16, 7.5] }, 1174 | { "curve": [15.5, 7.5, 15.5, 8] }, { "line": [15.5, 9] }, 1175 | { "curve": [15.5, 9.5, 16, 9.5] }, { "line": [19, 9.5] }], 1176 | 1177 | [{ "move": [2.5, 5.5] }, { "line": [3.5, 5.5] }], 1178 | 1179 | [{ "move": [3, 2.5] }, 1180 | { "curve": [3.5, 2.5, 3.5, 3] }, 1181 | { "curve": [3.5, 3.5, 3, 3.5] }, 1182 | { "curve": [2.5, 3.5, 2.5, 3] }, 1183 | { "curve": [2.5, 2.5, 3, 2.5] }], 1184 | 1185 | [{ "move": [15.5, 5.5] }, { "line": [16.5, 5.5] }], 1186 | 1187 | [{ "move": [16, 2.5] }, { "curve": [16.5, 2.5, 16.5, 3] }, 1188 | { "curve": [16.5, 3.5, 16, 3.5] }, { "curve": [15.5, 3.5, 15.5, 3] }, 1189 | { "curve": [15.5, 2.5, 16, 2.5] }], 1190 | 1191 | [{ "move": [6, 2.5] }, { "line": [7, 2.5] }, { "curve": [7.5, 2.5, 7.5, 3] }, 1192 | { "curve": [7.5, 3.5, 7, 3.5] }, { "line": [6, 3.5] }, 1193 | { "curve": [5.5, 3.5, 5.5, 3] }, { "curve": [5.5, 2.5, 6, 2.5] }], 1194 | 1195 | [{ "move": [12, 2.5] }, { "line": [13, 2.5] }, { "curve": [13.5, 2.5, 13.5, 3] }, 1196 | { "curve": [13.5, 3.5, 13, 3.5] }, { "line": [12, 3.5] }, 1197 | { "curve": [11.5, 3.5, 11.5, 3] }, { "curve": [11.5, 2.5, 12, 2.5] }], 1198 | 1199 | [{ "move": [7.5, 5.5] }, { "line": [9, 5.5] }, { "curve": [9.5, 5.5, 9.5, 6] }, 1200 | { "line": [9.5, 7.5] }], 1201 | [{ "move": [9.5, 6] }, { "curve": [9.5, 5.5, 10.5, 5.5] }, 1202 | { "line": [11.5, 5.5] }], 1203 | 1204 | 1205 | [{ "move": [5.5, 5.5] }, { "line": [5.5, 7] }, { "curve": [5.5, 7.5, 6, 7.5] }, 1206 | { "line": [7.5, 7.5] }], 1207 | [{ "move": [6, 7.5] }, { "curve": [5.5, 7.5, 5.5, 8] }, { "line": [5.5, 9.5] }], 1208 | 1209 | [{ "move": [13.5, 5.5] }, { "line": [13.5, 7] }, 1210 | { "curve": [13.5, 7.5, 13, 7.5] }, { "line": [11.5, 7.5] }], 1211 | [{ "move": [13, 7.5] }, { "curve": [13.5, 7.5, 13.5, 8] }, 1212 | { "line": [13.5, 9.5] }], 1213 | 1214 | [{ "move": [0, 11.5] }, { "line": [3, 11.5] }, { "curve": [3.5, 11.5, 3.5, 12] }, 1215 | { "line": [3.5, 13] }, { "curve": [3.5, 13.5, 3, 13.5] }, { "line": [1, 13.5] }, 1216 | { "curve": [0.5, 13.5, 0.5, 14] }, { "line": [0.5, 17] }, 1217 | { "curve": [0.5, 17.5, 1, 17.5] }, { "line": [1.5, 17.5] }], 1218 | [{ "move": [1, 17.5] }, { "curve": [0.5, 17.5, 0.5, 18] }, { "line": [0.5, 21] }, 1219 | { "curve": [0.5, 21.5, 1, 21.5] }, { "line": [18, 21.5] }, 1220 | { "curve": [18.5, 21.5, 18.5, 21] }, { "line": [18.5, 18] }, 1221 | { "curve": [18.5, 17.5, 18, 17.5] }, { "line": [17.5, 17.5] }], 1222 | [{ "move": [18, 17.5] }, { "curve": [18.5, 17.5, 18.5, 17] }, 1223 | { "line": [18.5, 14] }, { "curve": [18.5, 13.5, 18, 13.5] }, 1224 | { "line": [16, 13.5] }, { "curve": [15.5, 13.5, 15.5, 13] }, 1225 | { "line": [15.5, 12] }, { "curve": [15.5, 11.5, 16, 11.5] }, 1226 | { "line": [19, 11.5] }], 1227 | 1228 | [{ "move": [5.5, 11.5] }, { "line": [5.5, 13.5] }], 1229 | [{ "move": [13.5, 11.5] }, { "line": [13.5, 13.5] }], 1230 | 1231 | [{ "move": [2.5, 15.5] }, { "line": [3, 15.5] }, 1232 | { "curve": [3.5, 15.5, 3.5, 16] }, { "line": [3.5, 17.5] }], 1233 | [{ "move": [16.5, 15.5] }, { "line": [16, 15.5] }, 1234 | { "curve": [15.5, 15.5, 15.5, 16] }, { "line": [15.5, 17.5] }], 1235 | 1236 | [{ "move": [5.5, 15.5] }, { "line": [7.5, 15.5] }], 1237 | [{ "move": [11.5, 15.5] }, { "line": [13.5, 15.5] }], 1238 | 1239 | [{ "move": [2.5, 19.5] }, { "line": [5, 19.5] }, 1240 | { "curve": [5.5, 19.5, 5.5, 19] }, { "line": [5.5, 17.5] }], 1241 | [{ "move": [5.5, 19] }, { "curve": [5.5, 19.5, 6, 19.5] }, 1242 | { "line": [7.5, 19.5] }], 1243 | 1244 | [{ "move": [11.5, 19.5] }, { "line": [13, 19.5] }, 1245 | { "curve": [13.5, 19.5, 13.5, 19] }, { "line": [13.5, 17.5] }], 1246 | [{ "move": [13.5, 19] }, { "curve": [13.5, 19.5, 14, 19.5] }, 1247 | { "line": [16.5, 19.5] }], 1248 | 1249 | [{ "move": [7.5, 13.5] }, { "line": [9, 13.5] }, 1250 | { "curve": [9.5, 13.5, 9.5, 14] }, { "line": [9.5, 15.5] }], 1251 | [{ "move": [9.5, 14] }, { "curve": [9.5, 13.5, 10, 13.5] }, 1252 | { "line": [11.5, 13.5] }], 1253 | 1254 | [{ "move": [7.5, 17.5] }, { "line": [9, 17.5] }, 1255 | { "curve": [9.5, 17.5, 9.5, 18] }, { "line": [9.5, 19.5] }], 1256 | [{ "move": [9.5, 18] }, { "curve": [9.5, 17.5, 10, 17.5] }, 1257 | { "line": [11.5, 17.5] }], 1258 | 1259 | [{ "move": [8.5, 9.5] }, { "line": [8, 9.5] }, { "curve": [7.5, 9.5, 7.5, 10] }, 1260 | { "line": [7.5, 11] }, { "curve": [7.5, 11.5, 8, 11.5] }, 1261 | { "line": [11, 11.5] }, { "curve": [11.5, 11.5, 11.5, 11] }, 1262 | { "line": [11.5, 10] }, { "curve": [11.5, 9.5, 11, 9.5] }, 1263 | { "line": [10.5, 9.5] }] 1264 | ]; 1265 | 1266 | Object.prototype.clone = function () { 1267 | var i, newObj = (this instanceof Array) ? [] : {}; 1268 | for (i in this) { 1269 | if (i === 'clone') { 1270 | continue; 1271 | } 1272 | if (this[i] && typeof this[i] === "object") { 1273 | newObj[i] = this[i].clone(); 1274 | } else { 1275 | newObj[i] = this[i]; 1276 | } 1277 | } 1278 | return newObj; 1279 | }; 1280 | --------------------------------------------------------------------------------