├── .deployment ├── .bowerrc ├── app ├── content │ ├── images │ │ ├── amar.png │ │ ├── joe.png │ │ ├── favicon.ico │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-114x114.png │ │ └── apple-icon-144x144.png │ ├── sounds │ │ ├── Smack.wav │ │ ├── swoosh.wav │ │ └── level-up.wav │ └── levels.js ├── styles │ └── app.css ├── src │ ├── requireConfig.js │ ├── main.js │ └── views │ │ ├── SmallCube.js │ │ ├── CubeView.js │ │ ├── FpsMeterView.js │ │ ├── MenuView.js │ │ ├── GameBoard.js │ │ ├── DestroyerCube.js │ │ ├── AboutView.js │ │ ├── MainMenuView.js │ │ ├── LevelSelectionView.js │ │ ├── RotatingLogic.js │ │ ├── demoView.js │ │ └── GameLogic.js └── index.html ├── grunt ├── eslint.js ├── jscs.js ├── bower.js ├── usemin.js ├── clean.js ├── processhtml.js ├── useminPrepare.js ├── rev.js ├── requirejs.js ├── htmlmin.js ├── copy.js ├── connect.js ├── watch.js └── aliases.js ├── app.js ├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── .editorconfig ├── .eslintrc ├── bower.json ├── .jscsrc ├── LICENSE.txt ├── README.md ├── package.json ├── web.config └── deploy.sh /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = bash deploy.sh -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | {"directory":"app/lib","scripts":{"postinstall":"grunt bower"}} -------------------------------------------------------------------------------- /app/content/images/amar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/amar.png -------------------------------------------------------------------------------- /app/content/images/joe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/joe.png -------------------------------------------------------------------------------- /app/content/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/favicon.ico -------------------------------------------------------------------------------- /app/content/sounds/Smack.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/sounds/Smack.wav -------------------------------------------------------------------------------- /app/content/sounds/swoosh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/sounds/swoosh.wav -------------------------------------------------------------------------------- /app/content/sounds/level-up.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/sounds/level-up.wav -------------------------------------------------------------------------------- /app/content/images/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/apple-icon-57x57.png -------------------------------------------------------------------------------- /app/content/images/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/apple-icon-72x72.png -------------------------------------------------------------------------------- /app/content/images/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/apple-icon-114x114.png -------------------------------------------------------------------------------- /app/content/images/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Famospace/Famo.us-Monospace/HEAD/app/content/images/apple-icon-144x144.png -------------------------------------------------------------------------------- /grunt/eslint.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | options: { 3 | config: '.eslintrc' 4 | }, 5 | target: ['<%= config.app %>/src/**/**.js'] 6 | }; 7 | -------------------------------------------------------------------------------- /grunt/jscs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | src: ['<%= config.app %>/src/**/**.js', 'Gruntfile.js'], 3 | options: { 4 | config: '.jscsrc' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /app/styles/app.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: #fff; 3 | } 4 | 5 | .backfaceVisibility { 6 | -webkit-backface-visibility: visible; 7 | backface-visibility: visible; 8 | } 9 | -------------------------------------------------------------------------------- /grunt/bower.js: -------------------------------------------------------------------------------- 1 | // Automagically wire-up installed Bower components into your RequireJS config 2 | module.exports = { 3 | raget: { 4 | rjsConfig: '<%= config.app %>/src/requireConfig.js' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var port = process.env.Port || 9000; 4 | 5 | app.listen(port); 6 | 7 | app.use(express.static(__dirname + '/dist')); 8 | 9 | console.log('Live on port 9000'); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | .DS_Store 4 | ehthumbs.db 5 | Icon? 6 | Thumbs.db 7 | 8 | # Node Files # 9 | ############## 10 | node_modules 11 | 12 | # Project Files # 13 | ################# 14 | dist 15 | .tmp 16 | app/lib 17 | .yo-rc.json 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_install: 5 | - currentfolder=${PWD##*/} 6 | - if [ "$currentfolder" != 'generator-famous' ]; then cd .. && eval "mv $currentfolder generator-famous" && cd generator-famous; fi 7 | - npm install -g grunt-cli bower 8 | 9 | -------------------------------------------------------------------------------- /grunt/usemin.js: -------------------------------------------------------------------------------- 1 | // Performs rewrite based on rev and the useminPrepare configuration 2 | module.exports = { 3 | options: { 4 | assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images'] 5 | }, 6 | html: ['<%= config.dist %>/{,*/}*.html'], 7 | css: ['<%= config.dist %>/css/{,*/}*.css'] 8 | }; 9 | -------------------------------------------------------------------------------- /grunt/clean.js: -------------------------------------------------------------------------------- 1 | // Empties folders to start fresh 2 | module.exports = { 3 | dist: { 4 | files: [{ 5 | dot: true, 6 | src: [ 7 | '.tmp', 8 | '<%= config.dist %>/*', 9 | '!<%= config.dist %>/.git*' 10 | ] 11 | }] 12 | }, 13 | server: '.tmp' 14 | }; 15 | -------------------------------------------------------------------------------- /grunt/processhtml.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dev: { 3 | files: { 4 | '.tmp/index.html': ['<%= config.app %>/index.html'] 5 | } 6 | }, 7 | dist: { 8 | files: { 9 | '<%= config.dist %>/index.html': ['<%= config.app %>/index.html'] 10 | } 11 | }, 12 | options: { 13 | commentMarker: 'process' 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /grunt/useminPrepare.js: -------------------------------------------------------------------------------- 1 | // Reads HTML for usemin blocks to enable smart builds that automatically 2 | // concat, uglify and revision files. Creates configurations in memory so 3 | // additional tasks can operate on them 4 | 5 | module.exports = { 6 | options: { 7 | dest: '<%= config.dist %>' 8 | }, 9 | html: '<%= config.dist %>/index.html' 10 | }; 11 | -------------------------------------------------------------------------------- /app/src/requireConfig.js: -------------------------------------------------------------------------------- 1 | /*globals require*/ 2 | require.config({ 3 | shim: { 4 | 5 | }, 6 | paths: { 7 | famous: '../lib/famous', 8 | requirejs: '../lib/requirejs/require', 9 | almond: '../lib/almond/almond', 10 | 'famous-polyfills': '../lib/famous-polyfills/index', 11 | howler: '../lib/howler/howler' 12 | } 13 | }); 14 | require(['main']); 15 | -------------------------------------------------------------------------------- /grunt/rev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dist: { 3 | files: { 4 | src: [ 5 | '<%= config.dist %>/src/{,*/}*.js', 6 | '<%= config.dist %>/css/{,*/}*.css', 7 | '<%= config.dist %>/images/{,*/}*.*', 8 | '<%= config.dist %>/sounds/{,*/}*.*', 9 | '<%= config.dist %>/css/fonts/{,*/}*.*', 10 | '<%= config.dist %>/*.{ico,png}' 11 | ] 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /grunt/requirejs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | compile: { 3 | options: { 4 | optimize: 'uglify2', 5 | uglify2: { 6 | mangler: { 7 | toplevel: true 8 | } 9 | }, 10 | baseUrl: '<%= config.app %>/src', 11 | mainConfigFile: '<%= config.app %>/src/requireConfig.js', 12 | name: 'almond', 13 | include: 'main', 14 | insertRequire: ['main'], 15 | out: '<%= config.dist %>/src/main.js', 16 | wrap: true 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /grunt/htmlmin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dist: { 3 | options: { 4 | collapseBooleanAttributes: true, 5 | collapseWhitespace: true, 6 | removeAttributeQuotes: true, 7 | removeCommentsFromCDATA: true, 8 | removeEmptyAttributes: true, 9 | removeOptionalTags: true, 10 | removeRedundantAttributes: true, 11 | useShortDoctype: true 12 | }, 13 | files: [{ 14 | expand: true, 15 | cwd: '<%= config.dist %>', 16 | src: '{,*/}*.html', 17 | dest: '<%= config.dist %>' 18 | }] 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | 3 | /*Generated initially from grunt-init, heavily inspired by yo webapp*/ 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | 8 | grunt.file.setBase(__dirname); 9 | 10 | // Time how long tasks take. Can help when optimizing build times 11 | require('time-grunt')(grunt); 12 | 13 | // Load grunt config 14 | require('load-grunt-config')(grunt, { 15 | init: true, 16 | data: { 17 | config: { 18 | // Configurable paths 19 | app: 'app', 20 | dist: 'dist' 21 | } 22 | } 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 4 19 | 20 | [Gruntfile.js] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [grunt/*.js] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.json] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.css] 33 | indent_style = space 34 | indent_size = 2 35 | -------------------------------------------------------------------------------- /grunt/copy.js: -------------------------------------------------------------------------------- 1 | // Copies remaining files to places other tasks can use 2 | module.exports = { 3 | dist: { 4 | files: [{ 5 | expand: true, 6 | dot: true, 7 | cwd: '<%= config.app %>', 8 | dest: '<%= config.dist %>', 9 | src: [ 10 | '**/**.{ico,png,txt,jpg,svg,wof,ttf}', 11 | '.htaccess', 12 | 'images/{,*/}*.webp', 13 | 'sounds/{,*/}*.webp', 14 | 'content/**/**.**', // adds entire content folder 15 | // '{,*/}*.html', 16 | 'styles/fonts/{,*/}*.*', 17 | 'lib/famous/**/**.css' 18 | // 'lib/**/*' 19 | ] 20 | }] 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /grunt/connect.js: -------------------------------------------------------------------------------- 1 | // The actual grunt server settings 2 | module.exports = function (grunt) { 3 | 'use strict'; 4 | return { 5 | options: { 6 | port: grunt.option('port') || 1337, 7 | livereload: grunt.option('livereload') || 35729, 8 | // Change this to '0.0.0.0' to access the server from outside 9 | hostname: '0.0.0.0' 10 | // hostname: '192.168.161.217' || grunt.option('hostname') || 'localhost' 11 | }, 12 | livereload: { 13 | options: { 14 | open: true, 15 | base: [ 16 | '.tmp', 17 | '<%= config.app %>' 18 | ] 19 | } 20 | }, 21 | dist: { 22 | options: { 23 | open: true, 24 | base: '<%= config.dist %>', 25 | livereload: false 26 | } 27 | } 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "amd": true 5 | }, 6 | "globals": { 7 | "DocumentFragment": true 8 | }, 9 | "rules": { 10 | "valid-jsdoc": 0, 11 | "curly": [1, "multi"], 12 | "brace-style": [1, "stroustrup"], 13 | "consistent-this": 2, 14 | "no-constant-condition": 1, 15 | "no-underscore-dangle": 0, 16 | "no-use-before-define": 1, 17 | "func-names": 0, 18 | "func-style": [2, "declaration"], 19 | "new-cap": 1, 20 | "new-parens": 2, 21 | "no-ternary": 0, 22 | "no-unused-vars": [1, {"vars": "local", "args": "none"}], 23 | "quotes": [2, "single"], 24 | "one-var": 0, 25 | "space-infix-ops": 0, 26 | "strict": 0 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "famous-monospace", 3 | "description": "E.g. Seed project to get started with Famo.us.", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/JoeDou/famous-monospace", 6 | "authors": [ 7 | { 8 | "name": "JoeDou", 9 | "url": "http://github.com/JoeDou/" 10 | } 11 | ], 12 | "main": "app/index.html", 13 | "moduleType": [ 14 | "amd" 15 | ], 16 | "keywords": [ 17 | "famo.us" 18 | ], 19 | "license": "ISC", 20 | "private": true, 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "src/lib", 25 | "test", 26 | "tests" 27 | ], 28 | "dependencies": { 29 | "requirejs": "~2.1.11", 30 | "almond": "~0.2.9", 31 | "famous-polyfills": "git+https://github.com/Famous/polyfills.git#0.1.1", 32 | "famous": "git+https://github.com/Famous/famous.git#0.1.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": ["do", "try", "catch"], 3 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], 4 | "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], 5 | "requireRightStickedOperators": ["!"], 6 | "requireLeftStickedOperators": [","], 7 | "disallowKeywords": ["with"], 8 | "disallowMultipleLineBreaks": true, 9 | "requireBlocksOnNewline": true, 10 | "disallowMixedSpacesAndTabs": true, 11 | "disallowTrailingWhitespace": true, 12 | "requireLineFeedAtFileEnd": true, 13 | "requireSpacesInFunctionExpression": { 14 | "beforeOpeningCurlyBrace": true 15 | }, 16 | "disallowSpacesInFunctionExpression": { 17 | "beforeOpeningRoundBrace": true 18 | }, 19 | "validateJSDoc": { 20 | "checkParamNames": true, 21 | "requireParamTypes": false 22 | }, 23 | "validateQuoteMarks": "'", 24 | "disallowMultipleVarDecl": true, 25 | "disallowSpacesInsideParentheses": true 26 | } 27 | -------------------------------------------------------------------------------- /grunt/watch.js: -------------------------------------------------------------------------------- 1 | // Watches files for changes and runs tasks based on the changed files 2 | module.exports = function (grunt) { 3 | 'use strict'; 4 | grunt.event.on('watch', function (action, filepath) { 5 | grunt.config('jscs.src', [filepath]); 6 | grunt.config('eslint.target', filepath); 7 | }); 8 | return { 9 | options: { 10 | livereload: grunt.option('livereload') || true 11 | }, 12 | gruntfile: { 13 | files: ['Gruntfile.js', 'grunt/**/**'], 14 | options: { 15 | reload: true 16 | } 17 | }, 18 | js: { 19 | files: ['<%= config.app %>/src/**/**.js'], 20 | tasks: ['lint'], 21 | options: { 22 | spawn: false, 23 | interrupt: true 24 | } 25 | }, 26 | css: { 27 | files: ['<%= config.app %>/styles/{,*/}*.css'] 28 | }, 29 | html: { 30 | files: ['<%= config.app %>/{,*/}*.html'], 31 | tasks: ['processhtml:dev'] 32 | }, 33 | content: { 34 | files: [ 35 | '<%= config.app %>/content/**/**' 36 | ] 37 | } 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /grunt/aliases.js: -------------------------------------------------------------------------------- 1 | var metrics = require('famous-metrics'); 2 | 3 | module.exports = function (grunt) { 4 | 'use strict'; 5 | grunt.registerTask('serve', function (target) { 6 | 7 | if (!metrics.getTinfoil()) { 8 | metrics.track('grunt serve', {}); 9 | } 10 | 11 | if (target === 'dist') { 12 | return grunt.task.run(['build', 'connect:dist:keepalive']); 13 | } 14 | 15 | grunt.task.run([ 16 | 'clean:server', 17 | 'processhtml:dev', 18 | 'connect:livereload', 19 | 'watch' 20 | ]); 21 | }); 22 | 23 | grunt.registerTask('build', [ 24 | 'clean:dist', 25 | // 'lint', 26 | 'processhtml:dist', 27 | 'useminPrepare', 28 | 'requirejs', 29 | 'concat', 30 | 'cssmin', 31 | 'uglify', 32 | 'copy:dist', 33 | 'rev', 34 | 'usemin', 35 | 'htmlmin' 36 | ]); 37 | 38 | grunt.registerTask('lint', [ 39 | 'jscs', 40 | 'eslint' 41 | ]); 42 | 43 | grunt.registerTask('test', [ 44 | 'lint' 45 | ]); 46 | 47 | grunt.registerTask('default', [ 48 | 'build' 49 | ]); 50 | }; 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Amar Patel and Joe Dou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Famospace 2 | === 3 | This is the public repo for [Famospace](https://www.Famospace.com/) built by [Amar](https://github.com/theamarpatel) and [Joe](https://github.com/joedou) at [Hack Reactor](http://www.hackreactor.com/). 4 | 5 | === 6 | 7 | Famospace is a reengineered and redesigned version of Monospace, a native iOS app by Daniel Lutz, that runs in the browser on any device. 8 | 9 | This is a unique puzzle game where players need to think in both 2 and 3 dimensions. The goal is to collect a certain number of grey cubes in every level. To do so, the player is given the ability to switch between 2 and 3-dimensional views of the game situation. Possibilities and solutions change depending on the players perspective. 10 | 11 | ### Screenshots 12 | | Main Menu | Sample Level | 13 | |:-----------:|:------------:| 14 | | ![Main Menu SS](http://i.imgur.com/wpjKXmB.png)|![Sample Level SS](http://i.imgur.com/mESJEke.png) | 15 | 16 | ### Technical 17 | Famospace uses the following technologies: 18 | - [Famo.us](http://www.famo.us) 19 | - Node 20 | - Express 21 | - HTML5/CSS3 22 | 23 | ### License 24 | Famospace is licensed under the [MIT license.](https://github.com/Famospace/Famo.us-Monospace/blob/master/LICENSE.txt) 25 | -------------------------------------------------------------------------------- /app/src/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | *This file instantiates the entire game and 3 | *receives events to adjust perspective to 3D/2D 4 | */ 5 | define(function(require, exports, module) { 6 | 7 | var Engine = require('famous/core/Engine'); 8 | var Modifier = require('famous/core/Modifier'); 9 | var MenuView = require('views/MenuView'); 10 | var Easing = require('famous/transitions/Easing'); 11 | 12 | if (typeof Howl === 'undefined') { 13 | console.log('Howler not loaded. Reloading.'); 14 | return window.location.reload(true); 15 | } 16 | 17 | var modifier = new Modifier({ 18 | align: [0.5, 0.5], 19 | origin: [0.5, 0.5] 20 | }); 21 | 22 | var mainContext = Engine.createContext(); 23 | // default perspective is 1000; 500 for smaller devices 24 | var perspective = (window.innerWidth < 600 || window.innerHeight < 600) ? 500 : 1000; 25 | 26 | mainContext.setPerspective(perspective); 27 | 28 | // instantiates game 29 | var menuView = new MenuView(); 30 | 31 | mainContext.add(modifier).add(menuView); 32 | 33 | // upon receiving event, perspective is changed over 200ms 34 | menuView._eventOutput.on('is2d', function (boolean) { 35 | if (boolean) { 36 | mainContext.setPerspective(500000, {duration: 200, curve: Easing.inQuint}); 37 | } else { 38 | mainContext.setPerspective(perspective, {duration: 200, curve: Easing.outQuint}); 39 | } 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "famous-monospace", 3 | "description": "E.g. Seed project to get started with Famo.us.", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/JoeDou/famous-monospace", 6 | "author": { 7 | "name": "JoeDou", 8 | "url": "http://github.com/JoeDou/" 9 | }, 10 | "license": "ISC", 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/JoeDou/famous-monospace.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/JoeDou/famous-monospace/issues" 17 | }, 18 | "engines": { 19 | "node": ">= 0.10.0" 20 | }, 21 | "devDependencies": { 22 | "grunt": "~0.4.4", 23 | "grunt-contrib-clean": "^0.5.0", 24 | "grunt-contrib-connect": "^0.7.1", 25 | "grunt-contrib-watch": "^0.6.1", 26 | "grunt-contrib-copy": "^0.5.0", 27 | "grunt-contrib-requirejs": "^0.4.3", 28 | "grunt-contrib-htmlmin": "^0.3.0", 29 | "grunt-contrib-concat": "^0.4.0", 30 | "grunt-contrib-cssmin": "^0.9.0", 31 | "grunt-contrib-uglify": "^0.4.0", 32 | "grunt-rev": "^0.1.0", 33 | "grunt-bower-requirejs": "^0.10.0", 34 | "grunt-processhtml": "^0.3.3", 35 | "grunt-usemin": "^2.1.1", 36 | "grunt-eslint": "^0.4.0", 37 | "grunt-jscs-checker": "^0.4.1", 38 | "load-grunt-config": "^0.9.1", 39 | "time-grunt": "^0.3.1", 40 | "famous-metrics": "0.0.9", 41 | "bower": "^1.3.5", 42 | "grunt-cli": "^0.1.13" 43 | }, 44 | "scripts": { 45 | "test": "grunt test" 46 | }, 47 | "dependencies": { 48 | "express": "^4.4.4" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/views/SmallCube.js: -------------------------------------------------------------------------------- 1 | /* This view is to create small cubes that gets destroyed by the destroyer cube*/ 2 | define(function(require, exports, module) { 3 | var View = require('famous/core/View'); 4 | var Transform = require('famous/core/Transform'); 5 | var Modifier = require('famous/core/Modifier'); 6 | var CubeView = require('views/CubeView'); 7 | 8 | function SmallCube() { 9 | View.apply(this, arguments); 10 | 11 | // Store position for easy access 12 | this.position = this.options.startPosition; 13 | 14 | _createSmallCube.call(this); 15 | } 16 | 17 | SmallCube.prototype = Object.create(View.prototype); 18 | SmallCube.prototype.constructor = SmallCube; 19 | 20 | // To set absolute position of the big cube relative to the center of the cube, 21 | // by updating the transfom position on the cube's modifier 22 | SmallCube.prototype.setPosition = function(pos){ 23 | this.position=pos; 24 | }; 25 | 26 | // To get the absolute position relative to the center of the big cube 27 | SmallCube.prototype.getPosition = function(){ 28 | return this.position; 29 | }; 30 | 31 | // Set default options 32 | SmallCube.DEFAULT_OPTIONS = { 33 | size: 100, 34 | startPosition: [-150, -150, -150], 35 | cubeColor: 'white', 36 | opacity: 1 37 | }; 38 | 39 | // create small cube 40 | function _createSmallCube () { 41 | var smallCube = new CubeView({ size: this.options.size }); 42 | 43 | for (var i=0;i 2 | 3 | 4 | Famospace 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/views/CubeView.js: -------------------------------------------------------------------------------- 1 | /* This is the base view to create all the cubes in this application*/ 2 | define(function(require, exports, module) { 3 | var View = require('famous/core/View'); 4 | var Surface = require('famous/core/Surface'); 5 | var Transform = require('famous/core/Transform'); 6 | var Modifier = require('famous/core/Modifier'); 7 | var Group = require('famous/core/Group'); 8 | 9 | function CubeView() { 10 | View.apply(this, arguments); 11 | 12 | // needed to add surface listeners from ancestor view 13 | this.surfaces = []; 14 | 15 | // Translate to the center of the cube 16 | var rootModifier = new Modifier({ 17 | size: [this.options.size, this.options.size], 18 | translate: Transform.translate([this.options.size/2,this.options.size/2, this.options.size/2]) 19 | }); 20 | 21 | this.rootNode = this.add(rootModifier); 22 | 23 | _createCube.call(this); 24 | } 25 | 26 | CubeView.prototype = Object.create(View.prototype); 27 | CubeView.prototype.constructor = CubeView; 28 | 29 | CubeView.DEFAULT_OPTIONS = { 30 | size: 200 31 | }; 32 | 33 | module.exports = CubeView; 34 | 35 | function _createCube () { 36 | 37 | var surfaceTranslations = [ 38 | [0, 0, this.options.size/2], // front 39 | [0, 0, -this.options.size/2], // back 40 | [this.options.size/2, 0, 0], // right 41 | [-this.options.size/2, 0, 0], // left 42 | [0, this.options.size/2, 0], // bottom 43 | [0, -this.options.size/2, 0] // top 44 | ]; 45 | 46 | var surfaceRotations = [ 47 | [0, 0, 0],// front 48 | [0, Math.PI, 0], // back 49 | [0, -Math.PI/2, 0],// right 50 | [0, Math.PI/2, 0], // left 51 | [-Math.PI/2, 0, 0],// bottom 52 | [Math.PI/2, 0, 0]// top 53 | ]; 54 | 55 | var group = new Group(); 56 | 57 | for (var i=0;i<6;i++) { 58 | 59 | // create initial cube surfaces 60 | var surface = new Surface({ 61 | size: [this.options.size, this.options.size], 62 | properties: { 63 | webkitBackfaceVisibility: 'visible', 64 | backfaceVisibility: 'visible', 65 | mozBackfaceVisibility: 'visible', 66 | msBackfaceVisibility: 'visible', 67 | oBackfaceVisibility: 'visible', 68 | border: '1px solid black', 69 | pointerEvents: 'none' 70 | } 71 | }); 72 | 73 | this.surfaces.push(surface); 74 | 75 | // create initial cube modifiers 76 | var surfaceModifier = new Modifier({ 77 | align: [0.5, 0.5], 78 | origin: [0.5, 0.5] 79 | }); 80 | 81 | var currentSurface = surfaceTranslations[i]; 82 | var currentRotation = surfaceRotations[i]; 83 | 84 | // translate and rotate each side of the cube to proper location 85 | var matrixData = 86 | Transform.multiply( 87 | Transform.translate(currentSurface[0],currentSurface[1],currentSurface[2]), 88 | Transform.rotate(currentRotation[0],currentRotation[1],currentRotation[2]) 89 | ); 90 | surfaceModifier.transformFrom( matrixData ); 91 | 92 | // add to view's context for presentation 93 | group.add(surfaceModifier).add(surface); 94 | } 95 | this.rootNode.add(group); 96 | } 97 | }); -------------------------------------------------------------------------------- /app/src/views/FpsMeterView.js: -------------------------------------------------------------------------------- 1 | // show the FPS meter (created by Augustine Bralley) 2 | define(function(require, exports, module) { 3 | 4 | var Engine, FpsMeter, Modifier, Surface, Timer, Transform, View, 5 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 6 | __hasProp = {}.hasOwnProperty, 7 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 8 | 9 | // require('./scss/fpsmeter.scss'); 10 | View = require('famous/core/View'); 11 | 12 | Surface = require('famous/core/Surface'); 13 | 14 | Modifier = require('famous/core/Modifier'); 15 | 16 | Engine = require('famous/core/Engine'); 17 | 18 | Timer = require('famous/utilities/Timer'); 19 | 20 | Transform = require('famous/core/Transform'); 21 | 22 | FpsMeter = (function(_super) { 23 | __extends(FpsMeter, _super); 24 | 25 | FpsMeter.prototype.currTime = 0; 26 | 27 | FpsMeter.prototype.lastTime = 0; 28 | 29 | FpsMeter.prototype.frameTime = 0; 30 | 31 | FpsMeter.prototype.state = true; 32 | 33 | FpsMeter.prototype.filterStrength = 10; 34 | 35 | FpsMeter.prototype.updateFrequency = 100; 36 | 37 | function FpsMeter() { 38 | this.update = __bind(this.update, this); 39 | this.toggleState = __bind(this.toggleState, this); 40 | this.tick = __bind(this.tick, this); 41 | FpsMeter.__super__.constructor.apply(this, arguments); 42 | this.initTime(); 43 | this.surface = new Surface({ 44 | size: [60, 20], 45 | classes: ['fpsmeter'], 46 | content: '' 47 | }); 48 | this.add(new Modifier({ 49 | origin: [1, 1], 50 | transform: Transform.translate(0, 0, 10) 51 | })).add(this.surface); 52 | this.surface.on('click', this.toggleState); 53 | this.start(); 54 | } 55 | 56 | FpsMeter.prototype.initTime = function() { 57 | var perf, perfNow; 58 | perf = window.performance; 59 | if (perf && (perf.now || perf.webkitNow)) { 60 | perfNow = perf.now ? 'now' : 'webkitNow'; 61 | this.getTime = perf[perfNow].bind(perf); 62 | } 63 | return this.lastTime = this.getTime(); 64 | }; 65 | 66 | FpsMeter.prototype.tick = function() { 67 | var thisFrameTime; 68 | this.currTime = this.getTime(); 69 | thisFrameTime = this.currTime - this.lastTime; 70 | this.frameTime += (thisFrameTime - this.frameTime) / this.filterStrength; 71 | return this.lastTime = this.currTime; 72 | }; 73 | 74 | FpsMeter.prototype.toggleState = function() { 75 | if (this.state) { 76 | this.stop(); 77 | } else { 78 | this.start(); 79 | } 80 | return this.state = !this.state; 81 | }; 82 | 83 | FpsMeter.prototype.start = function() { 84 | Engine.on('prerender', this.tick); 85 | return this.interval = Timer.setInterval(this.update, this.updateFrequency); 86 | }; 87 | 88 | FpsMeter.prototype.stop = function() { 89 | Engine.removeListener('prerender', this.tick); 90 | return Timer.clear(this.interval); 91 | }; 92 | 93 | FpsMeter.prototype.update = function() { 94 | return this.surface.setContent("" + ((1000 / this.frameTime).toFixed(1)) + " fps"); 95 | }; 96 | 97 | FpsMeter.prototype.getTime = function() { 98 | return +new Date(); 99 | }; 100 | 101 | return FpsMeter; 102 | 103 | })(View); 104 | 105 | module.exports = FpsMeter; 106 | }); -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ---------------------- 4 | # KUDU Deployment Script 5 | # Version: 0.1.7 6 | # ---------------------- 7 | 8 | # Helpers 9 | # ------- 10 | 11 | exitWithMessageOnError () { 12 | if [ ! $? -eq 0 ]; then 13 | echo "An error has occurred during web site deployment." 14 | echo $1 15 | exit 1 16 | fi 17 | } 18 | 19 | # Prerequisites 20 | # ------------- 21 | 22 | # Verify node.js installed 23 | hash node 2>/dev/null 24 | exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment." 25 | 26 | # Setup 27 | # ----- 28 | 29 | SCRIPT_DIR="${BASH_SOURCE[0]%\\*}" 30 | SCRIPT_DIR="${SCRIPT_DIR%/*}" 31 | ARTIFACTS=$SCRIPT_DIR/../artifacts 32 | KUDU_SYNC_CMD=${KUDU_SYNC_CMD//\"} 33 | 34 | if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then 35 | DEPLOYMENT_SOURCE=$SCRIPT_DIR 36 | fi 37 | 38 | if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then 39 | NEXT_MANIFEST_PATH=$ARTIFACTS/manifest 40 | 41 | if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then 42 | PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH 43 | fi 44 | fi 45 | 46 | if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then 47 | DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot 48 | else 49 | KUDU_SERVICE=true 50 | fi 51 | 52 | if [[ ! -n "$KUDU_SYNC_CMD" ]]; then 53 | # Install kudu sync 54 | echo Installing Kudu Sync 55 | npm install kudusync -g --silent 56 | exitWithMessageOnError "npm failed" 57 | 58 | if [[ ! -n "$KUDU_SERVICE" ]]; then 59 | # In case we are running locally this is the correct location of kuduSync 60 | KUDU_SYNC_CMD=kuduSync 61 | else 62 | # In case we are running on kudu service this is the correct location of kuduSync 63 | KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync 64 | fi 65 | fi 66 | 67 | # Node Helpers 68 | # ------------ 69 | 70 | selectNodeVersion () { 71 | if [[ -n "$KUDU_SELECT_NODE_VERSION_CMD" ]]; then 72 | SELECT_NODE_VERSION="$KUDU_SELECT_NODE_VERSION_CMD \"$DEPLOYMENT_SOURCE\" \"$DEPLOYMENT_TARGET\" \"$DEPLOYMENT_TEMP\"" 73 | eval $SELECT_NODE_VERSION 74 | exitWithMessageOnError "select node version failed" 75 | 76 | if [[ -e "$DEPLOYMENT_TEMP/__nodeVersion.tmp" ]]; then 77 | NODE_EXE=`cat "$DEPLOYMENT_TEMP/__nodeVersion.tmp"` 78 | exitWithMessageOnError "getting node version failed" 79 | fi 80 | 81 | if [[ -e "$DEPLOYMENT_TEMP/.tmp" ]]; then 82 | NPM_JS_PATH=`cat "$DEPLOYMENT_TEMP/__npmVersion.tmp"` 83 | exitWithMessageOnError "getting npm version failed" 84 | fi 85 | 86 | if [[ ! -n "$NODE_EXE" ]]; then 87 | NODE_EXE=node 88 | fi 89 | 90 | NPM_CMD="\"$NODE_EXE\" \"$NPM_JS_PATH\"" 91 | else 92 | NPM_CMD=npm 93 | NODE_EXE=node 94 | fi 95 | } 96 | 97 | ################################################################################################################################## 98 | # Deployment 99 | # ---------- 100 | 101 | echo Handling node.js deployment. 102 | 103 | # 1. KuduSync 104 | if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then 105 | "$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh" 106 | exitWithMessageOnError "Kudu Sync failed" 107 | fi 108 | 109 | # 2. Select node version 110 | selectNodeVersion 111 | 112 | # # 3. Install npm packages 113 | # if [ -e "$DEPLOYMENT_TARGET/package.json" ]; then 114 | # cd "$DEPLOYMENT_TARGET" 115 | # eval $NPM_CMD install --production 116 | # exitWithMessageOnError "npm failed" 117 | # cd - > /dev/null 118 | # fi 119 | 120 | # # 4. Install bower packages 121 | # if [ -e "$DEPLOYMENT_TARGET/bower.json" ]; then 122 | # cd "$DEPLOYMENT_TARGET" 123 | # eval $NPM_CMD install bower 124 | # exitWithMessageOnError "installing bower failed" 125 | # ./node_modules/.bin/bower install 126 | # exitWithMessageOnError "bower failed" 127 | # cd - > /dev/null 128 | # fi 129 | 130 | # 5. Run grunt 131 | # if [ -e "$DEPLOYMENT_TARGET/Gruntfile.js" ]; then 132 | # cd "$DEPLOYMENT_TARGET" 133 | # eval $NPM_CMD install grunt-cli 134 | # exitWithMessageOnError "installing grunt failed" 135 | # ./node_modules/.bin/grunt --no-color build 136 | # exitWithMessageOnError "grunt failed" 137 | # cd - > /dev/null 138 | # fi 139 | ################################################################################################################################## 140 | 141 | # Post deployment stub 142 | if [[ -n "$POST_DEPLOYMENT_ACTION" ]]; then 143 | POST_DEPLOYMENT_ACTION=${POST_DEPLOYMENT_ACTION//\"} 144 | cd "${POST_DEPLOYMENT_ACTION_DIR%\\*}" 145 | "$POST_DEPLOYMENT_ACTION" 146 | exitWithMessageOnError "post deployment action failed" 147 | fi 148 | 149 | echo "Finished successfully." 150 | -------------------------------------------------------------------------------- /app/src/views/MenuView.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | var View = require('famous/core/View'); 3 | var Transform = require('famous/core/Transform'); 4 | var Modifier = require('famous/core/Modifier'); 5 | var Timer = require('famous/utilities/Timer'); 6 | var RenderNode = require('famous/core/RenderNode'); 7 | var Lightbox = require('famous/views/Lightbox'); 8 | 9 | var AboutView = require('views/AboutView'); 10 | var MainMenuView = require('views/MainMenuView'); 11 | var DemoView = require('views/DemoView'); 12 | var GameLogic = require('views/GameLogic'); 13 | var LevelSelection = require('views/LevelSelectionView'); 14 | 15 | function MenuView() { 16 | View.apply(this, arguments); 17 | 18 | this.views = {}; 19 | // updating boolean to indicate demo is playing 20 | this.playingDemo = true; 21 | // ensures that menu transitions don't overlap 22 | this.ready = true; 23 | 24 | _createViews.call(this); 25 | _createGameboard.call(this); 26 | _createLightbox.call(this); 27 | _setLightboxListeners.call(this); 28 | } 29 | 30 | MenuView.prototype = Object.create(View.prototype); 31 | MenuView.prototype.constructor = MenuView; 32 | 33 | MenuView.DEFAULT_OPTIONS = { 34 | lightboxOpts: { 35 | inOrigin: [0, 0], 36 | outOrigin: [0, 0], 37 | showOrigin: [0, 0], 38 | inTransform: Transform.translate(500, 0, 0), 39 | outTransform: Transform.translate(-500, 0, 0), 40 | inTransition: { duration: 650, curve: 'easeOut' }, 41 | outTransition: { duration: 650, curve: 'easeOut' } 42 | } 43 | }; 44 | 45 | function _createViews () { 46 | this.views.about = new AboutView(); 47 | this.views.mainMenu = new MainMenuView(); 48 | this.views.demoView = new DemoView(); 49 | this.views.levelSelection = new LevelSelection(); 50 | } 51 | 52 | function _createLightbox() { 53 | this.lightbox = new Lightbox(this.options.lightboxOpts); 54 | this.add(this.lightbox); 55 | this.lightbox.show(this.views.demoView); 56 | } 57 | 58 | function _setLightboxListeners() { 59 | this.views.about.pipe(this); 60 | this.views.demoView.pipe(this); 61 | this.views.mainMenu.pipe(this); 62 | this.views.levelSelection.pipe(this); 63 | this.views.levelSelection.pipe(this.game); 64 | this.game.pipe(this.views.levelSelection); 65 | this.game.pipe(this); 66 | 67 | // manage perspective listeners 68 | this._eventInput.on('is2dDemo', function (data) { 69 | if (this.playingDemo) this._eventOutput.emit('is2d', data); 70 | }.bind(this)); 71 | 72 | this._eventInput.on('is2d', function (data) { 73 | this._eventOutput.emit('is2d', data); 74 | }.bind(this)); 75 | 76 | // ensures that demo's game board doesn't interfere with real game board 77 | this._eventInput.on('demoToMainMenu', function () { 78 | if (this.playingDemo) { 79 | this.lightbox.show(this.views.mainMenu); 80 | this.playingDemo = false; 81 | } 82 | }.bind(this)); 83 | 84 | // menu listeners 85 | this._eventInput.on('startGame', function () { 86 | // ensures menu events don't overlap 87 | if (this.ready) { 88 | this.ready = false; 89 | this.lightbox.show(this.views.game); 90 | } 91 | // resets this.ready after 650ms (time of menu transition) 92 | Timer.setTimeout(function () { this.ready = true; }.bind(this), 650); 93 | }.bind(this)); 94 | 95 | this._eventInput.on('about', function () { 96 | if (this.ready) { 97 | this.ready = false; 98 | this.lightbox.show(this.views.about); 99 | } 100 | Timer.setTimeout(function () { this.ready = true; }.bind(this), 650); 101 | }.bind(this)); 102 | 103 | this._eventInput.on('levels', function () { 104 | if (this.ready) { 105 | this.ready = false; 106 | this.lightbox.show(this.views.levelSelection); 107 | } 108 | Timer.setTimeout(function () { this.ready = true; }.bind(this), 650); 109 | }.bind(this)); 110 | 111 | this._eventInput.on('mainMenu', function () { 112 | if (this.ready) { 113 | this.ready = false; 114 | this.lightbox.show(this.views.mainMenu); 115 | } 116 | Timer.setTimeout(function () { this.ready = true; }.bind(this), 650); 117 | }.bind(this)); 118 | } 119 | 120 | function _createGameboard () { 121 | var gameNode = new RenderNode(); 122 | this.game = new GameLogic(); 123 | var mod = new Modifier({ 124 | align: [0.5, 0.5], 125 | origin: [0.5, 0.5] 126 | }); 127 | 128 | gameNode.add(mod).add(this.game); 129 | this.views.game = gameNode; 130 | } 131 | 132 | module.exports = MenuView; 133 | }); 134 | -------------------------------------------------------------------------------- /app/src/views/GameBoard.js: -------------------------------------------------------------------------------- 1 | /* This View creates the Game Board that joins all the cubes. It contains the 2 | main cube which is the game board, the destroyer cube which can move around the 3 | game board to remove small cubes and the small cubes which are targets for the 4 | destroyer cube to remove*/ 5 | define(function(require, exports, module) { 6 | var View = require('famous/core/View'); 7 | var Modifier = require('famous/core/Modifier'); 8 | var CubeView = require('views/CubeView'); 9 | var DestroyerCube = require('views/DestroyerCube'); 10 | var SmallCube = require('views/SmallCube'); 11 | 12 | function GameBoard() { 13 | View.apply(this, arguments); 14 | this.is2D = true; 15 | 16 | var rootModifier = new Modifier(); 17 | 18 | this.node = this.add(rootModifier); 19 | 20 | _createParentCube.call(this); 21 | _createDestroyerCube.call(this); 22 | _createSmallCubes.call(this); 23 | _setListeners.call(this); 24 | } 25 | 26 | GameBoard.prototype = Object.create(View.prototype); 27 | GameBoard.prototype.constructor = GameBoard; 28 | 29 | GameBoard.DEFAULT_OPTIONS = { 30 | mainCubeSize: 400, 31 | destroyer: undefined, 32 | destroyerColor: '#34A4CC', 33 | smallCube: undefined, 34 | smallCubeColor: '#738F99' 35 | }; 36 | 37 | // set the 2D-3D transition flag 38 | GameBoard.prototype.setIs2D = function(bool){ 39 | this.is2D = bool; 40 | }; 41 | 42 | // Sets the destroyer position and will transiton that the set position. It will 43 | // also set the position of the small cube that was just removed by the destroyer 44 | // cube 45 | GameBoard.prototype.setDestroyerPosition = function(pos){ 46 | // convert game board coordinate to pixels 47 | var posPix = _convertToPixels.call(this,pos); 48 | this.destroyerCube.setPosition(posPix, true); 49 | for (var i = 0; i < this.smallCubes.length; i++){ 50 | var cubePos = this.smallCubes[i].getPosition(); 51 | // Checks for small cube position that matches the new destroyer cube position 52 | if (cubePos[0] === posPix[0] && cubePos[1] === posPix[1] && cubePos[2] === posPix[2]) { 53 | // remove the cube by setting it far far away 54 | this.smallCubes[i].setPosition( _convertToPixels.call(this, [-10000,-10000,0])); 55 | } 56 | } 57 | }; 58 | 59 | // Starting a new game; reset small cubes and destroyer cube position 60 | GameBoard.prototype.startNewGame = function(starter){ 61 | this.destroyerCube.setPosition(_convertToPixels.call(this,starter.destroyer)); 62 | _resetSmallCubes.call(this, starter.smallCube); 63 | }; 64 | 65 | // Create the destroyer cube 66 | function _createDestroyerCube () { 67 | this.destroyerCube = new DestroyerCube({ 68 | startPosition: _convertToPixels.call(this,this.options.destroyer), 69 | color: this.options.destroyerColor, 70 | size: this.options.mainCubeSize/4 71 | }); 72 | this.node.add(this.destroyerCube); 73 | } 74 | 75 | // Create the the game board/parent cube 76 | function _createParentCube () { 77 | this.cube = new CubeView({ 78 | size: this.options.mainCubeSize 79 | }); 80 | 81 | this.node.add(this.cube); 82 | } 83 | 84 | // Create all the small cubes; location should be passed in at creation 85 | function _createSmallCubes() { 86 | this.smallCubes = []; 87 | for (var i=0; i= 0.99999){ 66 | this.transitionable.halt(); // halt transitionable 67 | this.position = this.newPos; 68 | this.transitionable.reset(0); 69 | } 70 | 71 | var translate = Transform.translate( 72 | this.position[0] + (this.newPos[0]-this.position[0])*trans, 73 | this.position[1] + (this.newPos[1]-this.position[1])*trans, 74 | this.position[2] + (this.newPos[2]-this.position[2])*trans); 75 | return translate; 76 | }.bind(this) 77 | }); 78 | 79 | // save a reference to the dstroyer cube 80 | this.destroyerCube = destroyerCube; 81 | 82 | this.add(destroyerModifier).add(this.destroyerCube); 83 | } 84 | 85 | // Listen to the mouse movement on the destroyer cube and emit event which will 86 | // include the direction that the cube should move 87 | function _setMovementListeners () { 88 | var movement; 89 | 90 | var mouseSync = new MouseSync(); 91 | var touchSync = new TouchSync(); 92 | 93 | mouseSync.on('start', function (data) { 94 | // initialize pos data 95 | this.posData = {x:0, y:0}; 96 | }.bind(this)); 97 | 98 | mouseSync.on('update', function (data) { 99 | //update x, y position 100 | this.posData.x += data.delta[0]; 101 | this.posData.y += data.delta[1]; 102 | }.bind(this)); 103 | 104 | mouseSync.on('end', function () { 105 | // calculate total movement to determin up/down/right/left and emit movement event 106 | movement = _calculateMovement(this.posData); 107 | this._eventOutput.emit('movingCubeToGB', movement); 108 | }.bind(this)); 109 | 110 | touchSync.on('start', function (data) { 111 | // initialize pos data 112 | this.posData = {x:0, y:0}; 113 | }.bind(this)); 114 | 115 | touchSync.on('update', function (data) { 116 | //update x, y position 117 | this.posData.x += data.delta[0]; 118 | this.posData.y += data.delta[1]; 119 | }.bind(this)); 120 | 121 | touchSync.on('end', function () { 122 | // calculate total movement to determin up/down/right/left and emit movement event 123 | movement = _calculateMovement(this.posData); 124 | this._eventOutput.emit('movingCubeToGB', movement); 125 | }.bind(this)); 126 | 127 | for (var i=0;i 5 || yDelta > 5) { // mouse must move at least 5 px 141 | // vertical 142 | if (yDelta > xDelta) { //more y movement than x 143 | // move up 144 | if (posData.y < 0) output = [0,1]; 145 | // move down 146 | if (posData.y > 0) output = [0,-1]; 147 | // horizontal 148 | } else { //more x movement than y 149 | // move left 150 | if (posData.x < 0) output = [-1,0]; 151 | // move right 152 | if (posData.x > 0) output = [1,0]; 153 | } 154 | } 155 | return output; 156 | } 157 | 158 | module.exports = DestroyerCube; 159 | }); 160 | -------------------------------------------------------------------------------- /app/src/views/AboutView.js: -------------------------------------------------------------------------------- 1 | /* this view is for creater contact info */ 2 | define(function(require, exports, module) { 3 | var View = require('famous/core/View'); 4 | var Surface = require('famous/core/Surface'); 5 | var Modifier = require('famous/core/Modifier'); 6 | var Transform = require('famous/core/Transform'); 7 | var StateModifier = require('famous/modifiers/StateModifier'); 8 | var GridLayout = require("famous/views/GridLayout"); 9 | 10 | function AboutView() { 11 | View.apply(this, arguments); 12 | 13 | _createAuthors.call(this); 14 | _createGithubLink.call(this); 15 | _createMenu.call(this); 16 | } 17 | 18 | AboutView.prototype = Object.create(View.prototype); 19 | AboutView.prototype.constructor = AboutView; 20 | 21 | AboutView.DEFAULT_OPTIONS = { 22 | bioProps: { 23 | backgroundColor: 'white', 24 | color: "#404040", 25 | lineHeight: '50px', 26 | textAlign: 'center', 27 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif' 28 | } 29 | }; 30 | 31 | function _createGithubLink () { 32 | var banner = new Surface({ 33 | size: [window.innerWidth * 0.05, window.innerHeight * 0.05], 34 | content: '' + 35 | '' + 38 | '', 39 | properties: { 40 | zIndex: 5 41 | } 42 | }); 43 | 44 | var bannerMod = new Modifier({ 45 | align: [0, 0], 46 | origin: [0, 0], 47 | transform: function () { 48 | if (window.innerWidth < 800) { 49 | return Transform.scale(0.7, 0.7, 0.7); 50 | } 51 | }.bind(this) 52 | }); 53 | 54 | this.add(bannerMod).add(banner); 55 | } 56 | 57 | function _createAuthors () { 58 | 59 | var amar, joe; 60 | 61 | var grid = new GridLayout({ 62 | dimensions: [2, 1] 63 | }); 64 | 65 | var surfaces = []; 66 | grid.sequenceFrom(surfaces); 67 | // If the window width is less than 800 px use this layout (target mobile phone) 68 | if (window.innerWidth < 800) { 69 | // used html for contents on the surface 70 | amar = new Surface({ 71 | content: '

Amar Patel


amarpatel.io', 72 | size: [undefined, undefined], 73 | properties: this.options.bioProps 74 | }); 75 | 76 | amar.setProperties({borderRight: '1px solid lightgrey'}); 77 | 78 | joe = new Surface({ 79 | content: '

Joe Dou


whatwouldjoedou.com', 80 | size: [undefined, undefined], 81 | properties: this.options.bioProps 82 | }); 83 | } else { // If the window width is greater than 800 px use this layout (target desktop) 84 | amar = new Surface({ 85 | content: '

Amar Patel





amarpatel.io', 86 | size: [undefined, undefined], 87 | properties: this.options.bioProps 88 | }); 89 | 90 | amar.setProperties({borderRight: '1px solid lightgrey'}); 91 | 92 | joe = new Surface({ 93 | content: '

Joe Dou





whatwouldjoedou.com', 94 | size: [undefined, undefined], 95 | properties: this.options.bioProps 96 | }); 97 | } 98 | 99 | surfaces.push(amar, joe); 100 | 101 | this.add(grid); 102 | } 103 | 104 | // create back button 105 | function _createMenu () { 106 | var menuButton = new Surface({ 107 | content: 'Back', 108 | properties: { 109 | textAlign: 'center', 110 | fontSize: '.8rem', 111 | fontFamily: 'HelveticaNeue-Light', 112 | zIndex: 4, 113 | lineHeight: '45px', 114 | cursor: 'pointer' 115 | } 116 | }); 117 | 118 | var menuButtonMod = new Modifier({ 119 | size: [50, 50], 120 | align: [1, 0], 121 | origin: [1, 0] 122 | }); 123 | 124 | this.add(menuButtonMod).add(menuButton); 125 | 126 | menuButton.on('click', function () { 127 | this._eventOutput.emit('mainMenu'); 128 | }.bind(this)); 129 | } 130 | 131 | module.exports = AboutView; 132 | }); 133 | -------------------------------------------------------------------------------- /app/src/views/MainMenuView.js: -------------------------------------------------------------------------------- 1 | /* 2 | *Creates the main menu view 3 | */ 4 | define(function(require, exports, module) { 5 | var View = require('famous/core/View'); 6 | var Surface = require('famous/core/Surface'); 7 | var Modifier = require('famous/core/Modifier'); 8 | var Transform = require('famous/core/Transform'); 9 | var StateModifier = require('famous/modifiers/StateModifier'); 10 | var GridLayout = require('famous/views/GridLayout'); 11 | 12 | function MainMenuView() { 13 | View.apply(this, arguments); 14 | 15 | _createTitle.call(this); 16 | _createGithubLink.call(this); 17 | _createCreatedBy.call(this); 18 | _createInspiredBy.call(this); 19 | _createPlayButton.call(this); 20 | _createAbout.call(this); 21 | // _createStarButton.call(this); 22 | } 23 | 24 | MainMenuView.prototype = Object.create(View.prototype); 25 | MainMenuView.prototype.constructor = MainMenuView; 26 | 27 | MainMenuView.DEFAULT_OPTIONS = { 28 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 29 | }; 30 | 31 | function _createGithubLink () { 32 | var banner = new Surface({ 33 | size: [window.innerWidth * 0.05, window.innerHeight * 0.05], 34 | content: '' + 35 | '' + 38 | '', 39 | properties: { 40 | zIndex: 5 41 | } 42 | }); 43 | 44 | var bannerMod = new Modifier({ 45 | align: [0, 0], 46 | origin: [0, 0], 47 | transform: function () { 48 | if (window.innerWidth < 800) { 49 | return Transform.scale(0.7, 0.7, 0.7); 50 | } 51 | }.bind(this) 52 | }); 53 | 54 | this.add(bannerMod).add(banner); 55 | } 56 | 57 | function _createTitle () { 58 | var menu = new Surface({ 59 | size: [undefined, 50], 60 | content: 'Famospace', 61 | properties: { 62 | fontFamily: this.options.fontFamily, 63 | textAlign: 'center', 64 | fontSize: '2.5rem' 65 | } 66 | }); 67 | var menuMod = new StateModifier({ 68 | align: [0.5, 0], 69 | origin: [0.5, 0], 70 | transform: Transform.translate(0, 75, 0) 71 | }); 72 | 73 | this.add(menuMod).add(menu); 74 | } 75 | 76 | function _createCreatedBy () { 77 | var created = new Surface({ 78 | size: [undefined, 10], 79 | content: 'By ' + 80 | 'Amar ' + 81 | 'and ' + 82 | 'Joe', 83 | properties: { 84 | fontFamily: this.options.fontFamily, 85 | textAlign: 'center', 86 | fontSize: '.75rem' 87 | } 88 | }); 89 | var createdMod = new StateModifier({ 90 | align: [0.5, 0], 91 | origin: [0.5, 0], 92 | transform: Transform.translate(0, 130, 0) 93 | }); 94 | 95 | this.add(createdMod).add(created); 96 | } 97 | 98 | function _createInspiredBy () { 99 | var inspired = new Surface({ 100 | size: [undefined, 10], 101 | content: 'Inspired by Monospace
Daniel Lutz', 102 | properties: { 103 | fontFamily: this.options.fontFamily, 104 | textAlign: 'center', 105 | fontSize: '.75rem' 106 | } 107 | }); 108 | var inspiredMod = new StateModifier({ 109 | align: [0.5, 0.9], 110 | origin: [0.5, 0.9] 111 | }); 112 | 113 | this.add(inspiredMod).add(inspired); 114 | } 115 | 116 | function _createPlayButton () { 117 | var play = new Surface({ 118 | size: [150, 65], 119 | content: 'Play', 120 | properties: { 121 | fontFamily: this.options.fontFamily, 122 | textAlign: 'center', 123 | fontSize: '3rem', 124 | borderRadius: '10px', 125 | backgroundColor: '#34A4CC', 126 | border: '2px solid #738F99', 127 | cursor: 'pointer', 128 | color: 'white' 129 | } 130 | }); 131 | var playMod = new StateModifier({ 132 | align: [0.5, 0], 133 | origin: [0.5, 0], 134 | transform: Transform.translate(0, 230, 0) 135 | }); 136 | 137 | play.on('touchstart', function () { 138 | this._eventOutput.emit('levels'); 139 | }.bind(this)); 140 | 141 | play.on('click', function () { 142 | this._eventOutput.emit('levels'); 143 | }.bind(this)); 144 | 145 | this.add(playMod).add(play); 146 | } 147 | 148 | function _createAbout () { 149 | var about = new Surface({ 150 | size: [150, 65], 151 | content: 'About', 152 | properties: { 153 | fontFamily: this.options.fontFamily, 154 | textAlign: 'center', 155 | fontSize: '1rem', 156 | cursor: 'pointer' 157 | } 158 | }); 159 | var aboutMod = new StateModifier({ 160 | align: [0.5, 0], 161 | origin: [0.5, 0], 162 | transform: Transform.translate(0, 350, 0) 163 | }); 164 | 165 | about.on('touchstart', function () { 166 | this._eventOutput.emit('about'); 167 | }.bind(this)); 168 | 169 | about.on('click', function () { 170 | this._eventOutput.emit('about'); 171 | }.bind(this)); 172 | 173 | this.add(aboutMod).add(about); 174 | } 175 | 176 | function _createStarButton () { 177 | 178 | var about = new Surface({ 179 | size: [150, 65], 180 | content: '', 181 | properties: { 182 | fontFamily: this.options.fontFamily, 183 | textAlign: 'center', 184 | fontSize: '1rem', 185 | cursor: 'pointer' 186 | } 187 | }); 188 | 189 | var aboutMod = new StateModifier({ 190 | align: [0.5, 0], 191 | origin: [0.5, 0], 192 | transform: Transform.translate(4, 375, 0) 193 | }); 194 | 195 | this.add(aboutMod).add(about); 196 | } 197 | 198 | module.exports = MainMenuView; 199 | }); 200 | -------------------------------------------------------------------------------- /app/content/levels.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module) { 2 | 3 | var levels = { 4 | demoLevel: { 5 | smallCube: [ 6 | // back face 7 | [1, 2, 0], 8 | [2, 0, 0], 9 | [3, 1, 0], 10 | 11 | // md back 12 | [0, 0, 1], 13 | [2, 3, 1], 14 | 15 | // md front 16 | [1, 0, 2], 17 | [3, 3, 2], 18 | 19 | // front 20 | [0, 2, 3], 21 | [1, 3, 3], 22 | [2, 1, 3], 23 | [3, 0, 3] 24 | ], 25 | 26 | destroyer: [0, 3, 0] 27 | }, 28 | 29 | introVideo: { 30 | smallCube: [ 31 | // back face 32 | 33 | // md back 34 | [0, 3, 1], 35 | [1, 0, 1], 36 | 37 | // md front 38 | [ 2, 0, 2], 39 | [ 0, 2, 2], 40 | 41 | // front 42 | [ 3, 0, 3], 43 | [ 0, 1, 3] 44 | ], 45 | 46 | destroyer: [3, 3, 0] 47 | }, 48 | 49 | levels: [ 50 | {// level 1 (*1) 51 | smallCube: [ 52 | [1, 3, 3], 53 | [2, 3, 3], 54 | [3, 3, 3] 55 | ], 56 | 57 | destroyer: [0, 3, 3] 58 | }, 59 | 60 | {// level 2 (*4) 61 | smallCube: [ 62 | [0, 2, 0], 63 | [3, 1, 0], 64 | [3, 0, 3] 65 | ], 66 | 67 | destroyer: [0, 3, 3] 68 | }, 69 | 70 | {// level 3(*5) 71 | smallCube: [ 72 | [0, 0, 3], 73 | [0, 1, 3], 74 | [0, 2, 3], 75 | [1, 3, 3], 76 | [2, 3, 3], 77 | [3, 3, 3] 78 | ], 79 | 80 | destroyer: [0, 3, 3] 81 | }, 82 | 83 | {//level 4(*8) 84 | smallCube: [ 85 | [0, 0, 0], 86 | [1, 0, 1], 87 | [2, 0, 2], 88 | [3, 1, 0], 89 | [3, 2, 1], 90 | [3, 3, 2] 91 | ], 92 | 93 | destroyer: [0, 3, 3] 94 | }, 95 | 96 | {// level 5 (*9) 97 | smallCube: [ 98 | // back face 99 | [1, 2, 0], 100 | [2, 0, 0], 101 | [3, 1, 0], 102 | 103 | // md back 104 | [0, 0, 1], 105 | [2, 3, 1], 106 | 107 | // md front 108 | [1, 0, 2], 109 | [3, 3, 2], 110 | 111 | // front 112 | [0, 2, 3], 113 | [1, 3, 3], 114 | [2, 1, 3], 115 | [3, 0, 3] 116 | ], 117 | 118 | destroyer: [0, 3, 0] 119 | }, 120 | 121 | {// level 6 (*10) 122 | smallCube: [ 123 | [0, 0, 0], 124 | [0, 1, 0], 125 | [0, 2, 0], 126 | [1, 0, 0], 127 | [2, 0, 0], 128 | [3, 0, 0], 129 | [3, 0, 1], 130 | [3, 0, 2], 131 | [3, 0, 3], 132 | [3, 1, 3], 133 | [3, 2, 3], 134 | [3, 3, 3] 135 | ], 136 | 137 | destroyer: [0, 3, 0] 138 | }, 139 | 140 | {// level 7 (*11) 141 | smallCube: [ 142 | [0, 0, 0], 143 | [0, 0, 1], 144 | [0, 0, 2], 145 | [1, 0, 3], 146 | [2, 0, 3], 147 | [3, 0, 3], 148 | [2, 0, 0], 149 | [3, 0, 1], 150 | [3, 3, 0] 151 | ], 152 | 153 | destroyer: [0, 3, 3] 154 | }, 155 | 156 | {// level 8 (*12) 157 | smallCube: [ 158 | [0, 0, 0], 159 | [0, 0, 1], 160 | [0, 0, 2], 161 | [3, 1, 0], 162 | [3, 2, 0], 163 | [3, 3, 0], 164 | [1, 0, 3], 165 | [2, 0, 3], 166 | [3, 0, 3], 167 | [0, 1, 3], 168 | [0, 2, 3], 169 | [0, 3, 3] 170 | ], 171 | 172 | destroyer: [0, 0, 3] 173 | }, 174 | 175 | {// level 9 (*13) 176 | smallCube: [ 177 | [1, 2, 0], 178 | [3, 2, 1], 179 | [0, 3, 2], 180 | [1, 0, 2], 181 | [2, 0, 2], 182 | [3, 0, 2] 183 | ], 184 | 185 | destroyer: [2, 3, 3] 186 | }, 187 | 188 | {// level 10 (*14) 189 | smallCube: [ 190 | [0, 0, 0], 191 | [1, 0, 0], 192 | [0, 1, 0], 193 | [0, 2, 0], 194 | [1, 0, 2], 195 | [3, 0, 2], 196 | [3, 0, 3], 197 | [3, 1, 3], 198 | [3, 2, 3] 199 | ], 200 | 201 | destroyer: [0, 3, 3] 202 | }, 203 | 204 | {// level 11 (*15) 205 | smallCube: [ 206 | [3, 0, 0], 207 | [0, 1, 0], 208 | [3, 2, 0], 209 | [0, 3, 0], 210 | [2, 3, 0], 211 | [0, 3, 1], 212 | [3, 3, 1], 213 | [0, 3, 2], 214 | [3, 1, 3], 215 | [1, 3, 3], 216 | [2, 3, 3], 217 | [3, 3, 3] 218 | ], 219 | 220 | destroyer: [0, 3, 3] 221 | }, 222 | 223 | {// level 12 (*16) 224 | smallCube: [ 225 | [0, 1, 0], 226 | [0, 3, 0], 227 | [3, 1, 0], 228 | [3, 2, 0], 229 | [3, 3, 0], 230 | [0, 0, 3], 231 | [0, 1, 3], 232 | [0, 2, 3], 233 | [3, 2, 3] 234 | ], 235 | 236 | destroyer: [3, 0, 3] 237 | } 238 | ], 239 | }; 240 | 241 | module.exports = levels; 242 | }); 243 | -------------------------------------------------------------------------------- /app/src/views/LevelSelectionView.js: -------------------------------------------------------------------------------- 1 | /* This view is for displaying level selection; user will be able to choose levels in this view*/ 2 | define(function(require, exports, module) { 3 | var View = require('famous/core/View'); 4 | var Surface = require('famous/core/Surface'); 5 | var Modifier = require('famous/core/Modifier'); 6 | var Transform = require('famous/core/Transform'); 7 | var Levels = require('../../content/levels'); 8 | 9 | 10 | function LevelSelection() { 11 | View.apply(this, arguments); 12 | 13 | var rootModifier = new Modifier({ 14 | size: [undefined, undefined] 15 | }); 16 | 17 | this.node = this.add(rootModifier); //root modifier 18 | this.levelSurfaces = []; //save reference to each level surface 19 | 20 | _loadLocalStorage.call(this); 21 | _checkBoxSize.call(this); 22 | _createMenu.call(this); 23 | _createHeaderBlock.call(this); 24 | _createLevelBlock.call(this); 25 | _setListeners.call(this); 26 | _setLevelCompletedListener.call(this); 27 | _createGithubLink.call(this); 28 | } 29 | 30 | LevelSelection.DEFAULT_OPTIONS = { 31 | row: 3, 32 | column: 4, 33 | boxSize: 75, 34 | headerFontSize: '3rem', 35 | lineHeight: '65px', 36 | boxFontSize: '1.8rem' 37 | 38 | }; 39 | 40 | LevelSelection.prototype = Object.create(View.prototype); 41 | LevelSelection.prototype.constructor = LevelSelection; 42 | 43 | 44 | function _createGithubLink () { 45 | var banner = new Surface({ 46 | size: [window.innerWidth * 0.05, window.innerHeight * 0.05], 47 | content: '' + 48 | '' + 51 | '', 52 | properties: { 53 | zIndex: 5 54 | } 55 | }); 56 | 57 | var bannerMod = new Modifier({ 58 | align: [0, 0], 59 | origin: [0, 0], 60 | transform: function () { 61 | if (window.innerWidth < 800) { 62 | return Transform.scale(0.7, 0.7, 0.7); 63 | } 64 | }.bind(this) 65 | }); 66 | 67 | this.add(bannerMod).add(banner); 68 | } 69 | 70 | // load local storage to save completed level 71 | function _loadLocalStorage () { 72 | 73 | // checks to see if localstorage is enabled 74 | if (!window.localStorage || window.localStorage === null) { 75 | this.localStorage = false; 76 | return; 77 | } 78 | 79 | // check for previously saved game 80 | if (window.localStorage.getItem('famospace') === null) { 81 | // set up one if first time playing 82 | window.localStorage.setItem('famospace',[0,0,0,0,0,0,0,0,0,0,0,0]); 83 | } 84 | // retrieve data from local storage 85 | this.localStorage = window.localStorage.getItem('famospace').split(','); 86 | } 87 | 88 | // determine the size of the window to determine the square size and font size 89 | function _checkBoxSize(){ 90 | var tempWidth = this.options.boxSize * (this.options.row+3); 91 | var tempHeight = this.options.boxSize * (this.options.column+5); 92 | 93 | // console.log('temp: ', tempWidth, tempHeight); 94 | // console.log('actual:', window.innerWidth, window.innerHeight); 95 | 96 | if (window.innerWidth < tempWidth || window.innerHeight < tempHeight){ 97 | this.options.boxSize = 50; 98 | this.options.headerFontSize = '1.8rem'; 99 | this.options.lineHeight = '40px'; 100 | this.options.boxFontSize = '1rem'; 101 | } 102 | } 103 | 104 | // Set listeners when levels are completed and update local storage with complete level 105 | function _setLevelCompletedListener () { 106 | this._eventInput.on('levelCompleted', function (levelIndex) { 107 | // update localStorage 108 | this.localStorage[levelIndex] = 1; 109 | window.localStorage.setItem('famospace',this.localStorage); 110 | 111 | // change surface color of completed level 112 | this.levelSurfaces[levelIndex].setProperties({color: 'black'}); 113 | }.bind(this)); 114 | } 115 | 116 | // create the header for level selection 117 | function _createHeaderBlock(){ 118 | var modifier = new Modifier({ 119 | size: [window.innerWidth, this.options.boxSize], 120 | origin: [0.5, 0.1], 121 | align: [ 0, 0] 122 | }); 123 | 124 | var surface = new Surface({ 125 | content: 'Level Selection', 126 | properties: { 127 | textAlign: 'center', 128 | verticalAlign: 'middle', 129 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 130 | fontSize: this.options.headerFontSize 131 | } 132 | }); 133 | 134 | this.node.add(modifier).add(surface); 135 | } 136 | // create a square for each box with listeners to load the proper level 137 | function _createLevelBlock(){ 138 | var index = 1; 139 | // cycle through each row and column 140 | for (var j=0; j 0.99999){ 91 | // set the the current variables to the new state 92 | this.rotation = mapStateTransition(this.state, this.nVec); 93 | this.index = [0,0,0]; 94 | this.position = [0,0]; 95 | this.left = 0; 96 | this.down = 0; 97 | this.transitionable.reset(0); 98 | this.transitionable.halt(); // halt transitionable 99 | // update new normal vector and right vector to send to game logic 100 | var updateObj = { 101 | nVec: this.nVec, 102 | rVec: this.rVec 103 | }; 104 | this._eventOutput.emit('coordinateUpdate', updateObj); 105 | } 106 | // the actual transition in the transfrom function 107 | var rotTrans = Transform.rotate((this.index[0]*rotateAng)*Math.PI/2, 108 | (this.index[1]*rotateAng)*Math.PI/2, 109 | (this.index[2]*rotateAng)*Math.PI/2); 110 | return Transform.multiply(this.rotation, rotTrans); 111 | }.bind(this) 112 | }); 113 | 114 | this.node = this.add(rotateModifier); 115 | } 116 | // This is the function determines the movement of the mouse/touch to determine 117 | // rotation of the cube 118 | function _setBackgroundListeners () { 119 | // set mouse sync and touch sync 120 | this.parentCubeMouseSync = new MouseSync(); 121 | this.parentCubeTouchSync = new TouchSync(); 122 | // pipe sync to surface 123 | this.backgroundSurface.pipe(this.parentCubeMouseSync); 124 | this.backgroundSurface.pipe(this.parentCubeTouchSync); 125 | 126 | // on update, add delta position 127 | this.parentCubeMouseSync.on('update', function (data) { 128 | this.position[0] += data.delta[0]; 129 | this.position[1] += data.delta[1]; 130 | }.bind(this)); 131 | 132 | // on end determine the delta in x and y direction to determine the cube movement 133 | this.parentCubeMouseSync.on('end', function () { 134 | // one direction must exceed 5 px to avoid single clicks 135 | if (Math.abs(this.position[0]) < 5 && Math.abs(this.position[1]) < 5) { 136 | this.position = [0, 0]; 137 | return false; 138 | } 139 | // determine left/right/up/down 140 | if (Math.abs(this.position[0]) > Math.abs(this.position[1])) { 141 | this.left = this.position[0] > 0 ? -1 : 1; 142 | } else{ 143 | this.down = this.position[1] > 0 ? 1 : -1; 144 | } 145 | 146 | // calculate state transition 147 | updateStateTransition.call(this,this.left,this.down); 148 | 149 | // set transitionable and restart transition to new state 150 | this.transitionable.set(1, { 151 | duration: 500, curve: Easing.outBack 152 | }); 153 | 154 | }.bind(this)); 155 | 156 | // on update, add delta position 157 | this.parentCubeTouchSync.on('update', function (data) { 158 | this.position[0] += data.delta[0]; 159 | this.position[1] += data.delta[1]; 160 | }.bind(this)); 161 | 162 | // on end determine the delta in x and y direction to determine the cube movement 163 | this.parentCubeTouchSync.on('end', function () { 164 | if (Math.abs(this.position[0]) < 5 && Math.abs(this.position[1]) < 5) { 165 | this.position = [0, 0]; 166 | return false; 167 | } 168 | if (Math.abs(this.position[0]) > Math.abs(this.position[1])){ 169 | this.left = this.position[0] > 0 ? -1 : 1; 170 | } else{ 171 | this.down = this.position[1] > 0 ? 1 : -1; 172 | } 173 | 174 | updateStateTransition.call(this,this.left,this.down); 175 | 176 | this.transitionable.set(1, { 177 | duration: 500, curve: Easing.outBack 178 | }); 179 | }.bind(this)); 180 | } 181 | 182 | // set listeners for this view 183 | function _setListeners() { 184 | 185 | this.gameBoard.pipe(this._eventInput); 186 | this.gameBoard.subscribe(this._eventOutput); 187 | 188 | this._eventInput.on('movingCubeToRL', function(data){ 189 | this._eventOutput.emit('movingCubeToGL', data); 190 | }.bind(this)); 191 | // 2D to 3D transition to pipe and unpipe background listeners 192 | this._eventInput.on('is2d', function(data){ 193 | if (data){ 194 | this.backgroundSurface.unpipe(this.parentCubeMouseSync); 195 | this.backgroundSurface.setProperties({pointerEvents: 'none'}); 196 | } else{ 197 | this.backgroundSurface.pipe(this.parentCubeMouseSync); 198 | this.backgroundSurface.setProperties({pointerEvents: 'auto'}); 199 | } 200 | this._eventOutput.emit('is2d', data); 201 | }.bind(this)); 202 | } 203 | 204 | // this function takes the current state and orientation and determines the next state 205 | // give rotation of right or left or up or down. 206 | function updateStateTransition(left, down){ 207 | var tempState; 208 | if(this.state[2] === 1){ //[0,0,1] 209 | tempState = this.state; 210 | if (this.nVec[1] !== 0){ //[0,1,0] or [0,-1,0] 211 | if (down !== 0){ 212 | this.index = [down*this.nVec[1], 0, 0]; 213 | this.state = [0,down*this.nVec[1],0]; 214 | this.nVec = [0,0,-down*tempState[2]]; 215 | }else{ 216 | this.index = [0, left*this.nVec[1], 0]; 217 | this.state = [-left*this.nVec[1],0,0]; 218 | this.rVec = [0,0,-left*tempState[2]]; 219 | } 220 | }else{ //this.nVec = [1,0,0] or [-1,0,0] 221 | if (down !== 0){ 222 | this.index = [0, -down*this.nVec[0], 0]; 223 | this.state = [down*this.nVec[0],0,0]; 224 | this.nVec = [0,0,-down*tempState[2]]; 225 | }else{ 226 | this.index = [left*this.nVec[0], 0, 0]; 227 | this.state = [0, left*this.nVec[0],0]; 228 | this.rVec = [0,0,-left*tempState[2]]; 229 | } 230 | } 231 | }else if(this.state[2] === -1){ //[0,0,-1] 232 | tempState = this.state; 233 | if (this.nVec[1] !== 0){ //[0,1,0] or [0,-1,0] 234 | if (down !== 0){ 235 | this.index = [-down*this.nVec[1], 0, 0]; 236 | this.state = [0,down*this.nVec[1],0]; 237 | this.nVec = [0,0,-down*tempState[2]]; 238 | }else{ 239 | this.index = [0, left*this.nVec[1], 0]; 240 | this.state = [left*this.nVec[1],0,0]; 241 | this.rVec = [0,0,-left*tempState[2]]; 242 | } 243 | }else{ 244 | if (down !== 0){ //[1,0,0] or [-1,0,0] 245 | this.index = [0, down*this.nVec[0], 0]; 246 | this.state = [down*this.nVec[0],0,0]; 247 | this.nVec = [0,0,-down*tempState[2]]; 248 | }else{ 249 | this.index = [left*this.nVec[0], 0, 0]; 250 | this.state = [0, -left*this.nVec[0],0]; 251 | this.rVec = [0,0,-left*tempState[2]]; 252 | } 253 | } 254 | }else if(this.state[1] === 1){//[0,1,0] 255 | tempState = this.state; 256 | if (this.nVec[2] !== 0){ //[0,0,1] or [0,0,-1] 257 | if (down !== 0){ 258 | this.index = [-down*this.nVec[2], 0, 0]; 259 | this.state = [0,0,down*this.nVec[2]]; 260 | this.nVec = [0,-down*tempState[1],0]; 261 | }else{ 262 | this.index = [0, 0, left*this.nVec[2]]; 263 | this.state = [left*this.nVec[2],0,0]; 264 | this.rVec = [0,-left*tempState[1],0]; 265 | } 266 | }else{ 267 | if (down !== 0){ //[1,0,0] or [-1,0,0] 268 | this.index = [0, 0, down*this.nVec[0]]; 269 | this.state = [down*this.nVec[0],0,0]; 270 | this.nVec = [0,-down*tempState[1],0]; 271 | }else{ 272 | this.index = [left*this.nVec[0], 0, 0]; 273 | this.state = [0,0,-left*this.nVec[0]]; 274 | this.rVec = [0,-left*tempState[1],0]; 275 | } 276 | } 277 | }else if(this.state[1] === -1){ //[0,-1,0] 278 | tempState = this.state; 279 | if (this.nVec[2] !== 0){ //[0,0,1] or [0,0,-1] 280 | if (down !== 0){ 281 | this.index = [down*this.nVec[2], 0, 0]; 282 | this.state = [0,0,down*this.nVec[2]]; 283 | this.nVec = [0,-down*tempState[1],0]; 284 | }else{ 285 | this.index = [0, 0, left*this.nVec[2]]; 286 | this.state = [-left*this.nVec[2],0,0]; 287 | this.rVec = [0,-left*tempState[1],0]; 288 | } 289 | }else{ 290 | if (down !== 0){ //[1,0,0] or [-1,0,0] 291 | this.index = [0, 0, -down*this.nVec[0]]; 292 | this.state = [down*this.nVec[0],0,0]; 293 | this.nVec = [0,-down*tempState[1],0]; 294 | }else{ 295 | this.index = [left*this.nVec[0], 0, 0]; 296 | this.state = [0,0,left*this.nVec[0]]; 297 | this.rVec = [0,-left*tempState[1],0]; 298 | } 299 | } 300 | }else if(this.state[0] === 1){ //[1,0,0] 301 | tempState = this.state; 302 | if (this.nVec[2] !== 0){ //[0,0,1] or [0,0,-1] 303 | if (down !== 0){ 304 | this.index = [0, down*this.nVec[2], 0]; 305 | this.state = [0,0,down*this.nVec[2]]; 306 | this.nVec = [-down*tempState[0],0,0]; 307 | }else{ 308 | this.index = [0, 0, left*this.nVec[2]]; 309 | this.state = [0,-left*this.nVec[2],0]; 310 | this.rVec = [-left*tempState[0],0,0]; 311 | } 312 | }else{ 313 | if (down !== 0){ //[0,1,0] or [0,-1,0] 314 | this.index = [0, 0, -down*this.nVec[1]]; 315 | this.state = [0,down*this.nVec[1],0]; 316 | this.nVec = [-down*tempState[0],0,0]; 317 | }else{ 318 | this.index = [0, left*this.nVec[1], 0]; 319 | this.state = [0,0,left*this.nVec[1]]; 320 | this.rVec = [-left*tempState[0],0,0]; 321 | } 322 | } 323 | }else{ //[-1,0,0] 324 | tempState = this.state; 325 | if (this.nVec[2] !== 0){ //[0,0,1] or [0,0,-1] 326 | if (down !== 0){ 327 | this.index = [0, -down*this.nVec[2], 0]; 328 | this.state = [0,0,down*this.nVec[2]]; 329 | this.nVec = [-down*tempState[0],0,0]; 330 | }else{ 331 | this.index = [0, 0, left*this.nVec[2]]; 332 | this.state = [0,left*this.nVec[2],0]; 333 | this.rVec = [-left*tempState[0],0,0]; 334 | } 335 | }else{ 336 | if (down !== 0){ //[0,1,0] or [0,-1,0] 337 | this.index = [0, 0, down*this.nVec[1]]; 338 | this.state = [0,down*this.nVec[1],0]; 339 | this.nVec = [-down*tempState[0],0,0]; 340 | }else{ 341 | this.index = [0, left*this.nVec[1], 0]; 342 | this.state = [0,0, -left*this.nVec[1]]; 343 | this.rVec = [-left*tempState[0],0,0]; 344 | } 345 | } 346 | } 347 | } 348 | // this function maps the rotation from starting state to current state 349 | // with maximum of axial rotation 350 | function mapStateTransition(state, nVec){ 351 | var rotationMap = { 352 | '1,0,0': {'rotate': [0,-1,0], 353 | '0,-1,0': [0,0,0], 354 | '0,1,0': [2,0,0], 355 | '0,0,1': [1,0,0], 356 | '0,0,-1': [-1,0,0]}, 357 | '-1,0,0': {'rotate': [0,1,0], 358 | '0,-1,0': [0,0,0], 359 | '0,1,0': [2,0,0], 360 | '0,0,1': [1,0,0], 361 | '0,0,-1': [-1,0,0]}, 362 | '0,-1,0': {'rotate': [-1,0,0], 363 | '0,0,-1': [0,0,0], 364 | '0,0,1': [0,2,0], 365 | '1,0,0': [0,1,0], 366 | '-1,0,0': [0,-1,0]}, 367 | '0,1,0': {'rotate': [1,0,0], 368 | '0,0,1': [0,0,0], 369 | '0,0,-1': [0,2,0], 370 | '1,0,0': [0,-1,0], 371 | '-1,0,0': [0,1,0]}, 372 | '0,0,-1': {'rotate': [0,2,0], 373 | '0,-1,0': [0,0,0], 374 | '0,1,0': [0,0,2], 375 | '1,0,0': [0,0,-1], 376 | '-1,0,0': [0,0,1]}, 377 | '0,0,1': {'rotate': [0,0,0], 378 | '0,-1,0': [0,0,0], 379 | '0,1,0': [0,0,2], 380 | '1,0,0': [0,0,-1], 381 | '-1,0,0': [0,0,1]} 382 | }; 383 | 384 | 385 | var first = rotationMap[state].rotate; 386 | var second = rotationMap[state][nVec]; 387 | 388 | var trans = Transform.multiply(Transform.rotate(first[0]*Math.PI/2,first[1]*Math.PI/2,first[2]*Math.PI/2), 389 | Transform.rotate(second[0]*Math.PI/2,second[1]*Math.PI/2,second[2]*Math.PI/2)); 390 | 391 | return trans; 392 | } 393 | 394 | module.exports = RotatingLogic; 395 | }); 396 | -------------------------------------------------------------------------------- /app/src/views/demoView.js: -------------------------------------------------------------------------------- 1 | /* 2 | *This view is created for a play by play demo to teach users how to play the game. 3 | *Game play is manually injected and used timer to manipulate board and simulate user interaction. 4 | */ 5 | define(function(require, exports, module) { 6 | var View = require('famous/core/View'); 7 | var Surface = require('famous/core/Surface'); 8 | var Transform = require('famous/core/Transform'); 9 | var StateModifier = require('famous/modifiers/StateModifier'); 10 | var Transitionable = require('famous/transitions/Transitionable'); 11 | var Timer = require('famous/utilities/Timer'); 12 | var Modifier = require('famous/core/Modifier'); 13 | var Easing = require('famous/transitions/Easing'); 14 | var GameLogic = require('views/GameLogic'); 15 | var Levels = require('../../content/levels'); 16 | 17 | function DemoView() { 18 | View.apply(this, arguments); 19 | this.rootModifier = new Modifier(); 20 | this.node = this.add(this.rootModifier); 21 | 22 | // allows sounds to be muted if demo is skipped 23 | this.skip = false; 24 | 25 | _createGithubLink.call(this); 26 | 27 | 28 | // creates skip button to bybass intro animation 29 | // takes 5.1 seconds 30 | _createSkipButton.call(this); 31 | // instantiates first three blurbs explaining game premise 32 | _startWordCrash.call(this); 33 | 34 | // after _startWordCrash has finished, cube animation begins 35 | // takes 21.5 seconds 36 | Timer.setTimeout(function () { 37 | if(!this.skip){ 38 | _startDemoPlay.call(this); 39 | _startDemoText.call(this); 40 | } 41 | }.bind(this), 6000); 42 | } 43 | 44 | DemoView.prototype = Object.create(View.prototype); 45 | DemoView.prototype.constructor = DemoView; 46 | 47 | 48 | function _createGithubLink () { 49 | var banner = new Surface({ 50 | size: [window.innerWidth * 0.05, window.innerHeight * 0.05], 51 | content: '' + 52 | '' + 55 | '', 56 | properties: { 57 | zIndex: 5, 58 | } 59 | }); 60 | 61 | var bannerMod = new StateModifier({ 62 | opacity: 0, 63 | align: [0, 0], 64 | origin: [0, 0], 65 | transform: function () { 66 | if (window.innerWidth < 800) { 67 | return Transform.scale(0.7, 0.7, 0.7); 68 | } 69 | }.bind(this) 70 | }); 71 | 72 | this.add(bannerMod).add(banner); 73 | 74 | Timer.setTimeout(function () { 75 | bannerMod.setOpacity(1, {duration: 1000, curve: 'easeInOut'}); 76 | }, 6000); 77 | } 78 | 79 | DemoView.DEFAULT_OPTIONS = { 80 | crashTextProps: { 81 | // backup fonts for browsers that don't support HelveticaNeue-Light 82 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 83 | textAlign: 'center', 84 | fontSize: '1.2rem' 85 | }, 86 | instrucTextProps: { 87 | textAlign: 'center', 88 | fontSize: '2rem', 89 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 90 | zIndex: 30 91 | } 92 | }; 93 | 94 | // skip button for user to skip the demo and move to menu page 95 | function _createSkipButton () { 96 | var skip = new Surface({ 97 | size: [25, 25], 98 | content: 'Skip', 99 | properties: { 100 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 101 | textAlign: 'center', 102 | fontSize: '1.5rem', 103 | cursor: 'pointer' 104 | } 105 | }); 106 | 107 | var skipMod = new StateModifier({ 108 | align: [1, 0], 109 | origin: [1, 0], 110 | transform: Transform.translate(-22, 0, 0) 111 | }); 112 | 113 | skip.on('touchstart', function () { 114 | this.skip = true; 115 | this._eventOutput.emit('demoToMainMenu'); 116 | }.bind(this)); 117 | 118 | skip.on('click', function () { 119 | this.skip = true; 120 | this._eventOutput.emit('demoToMainMenu'); 121 | }.bind(this)); 122 | 123 | // skip button fades out before cube slides out so it doesn't transition out in the middle of the screen 124 | // lightbox is only translating 500px 125 | Timer.setTimeout(function () { 126 | skipMod.setOpacity(0, {curve: 'easeInOut', duration: 250}); 127 | }.bind(this), 25000); 128 | 129 | this.node.add(skipMod).add(skip); 130 | 131 | } 132 | 133 | function _startWordCrash () { 134 | 135 | // words crash down 136 | var crush = new Surface({ 137 | size: [200, 100], 138 | content: 'Crush the Grey Cubes', 139 | properties: this.options.crashTextProps 140 | }); 141 | 142 | var crushMod = new StateModifier({ 143 | align: [0.5, 0], 144 | origin: [0.5, 0], 145 | transform: Transform.multiply( 146 | Transform.rotateY(Math.PI/2), 147 | Transform.translate(100, window.innerHeight/2 - 50, 400) 148 | ) 149 | }); 150 | 151 | var manipulate = new Surface({ 152 | size: [200, 100], 153 | content: 'Manipulate in 3D', 154 | properties: this.options.crashTextProps 155 | }); 156 | 157 | var manipulateMod = new StateModifier({ 158 | align: [0.5, 0], 159 | origin: [0.5, 0], 160 | transform: Transform.multiply( 161 | Transform.rotateX(-Math.PI/2), 162 | Transform.translate(0, window.innerHeight/2 + 1000, 1000) 163 | ) 164 | }); 165 | 166 | var destroy = new Surface({ 167 | size: [200, 100], 168 | content: 'Destroy in 2D', 169 | properties: this.options.crashTextProps 170 | }); 171 | 172 | var destroyMod = new StateModifier({ 173 | align: [0.5, 0], 174 | origin: [0.5, 0], 175 | transform: Transform.multiply( 176 | Transform.rotateY(-Math.PI/2), 177 | Transform.translate(100, window.innerHeight/2 + 50, 400) 178 | ) 179 | }); 180 | 181 | this.node.add(crushMod).add(crush); 182 | this.node.add(manipulateMod).add(manipulate); 183 | this.node.add(destroyMod).add(destroy); 184 | 185 | // Timer used for fading blurbs in 186 | Timer.setTimeout(function () { 187 | crushMod.setTransform( 188 | Transform.multiply( 189 | Transform.translate(0, window.innerHeight/2 - 50, 0), 190 | Transform.rotateY(0) 191 | ), 192 | {duration: 1000, curve: 'easeInOut'} 193 | ); 194 | }, 650); 195 | 196 | Timer.setTimeout(function () { 197 | manipulateMod.setTransform( 198 | Transform.multiply( 199 | Transform.translate(0, window.innerHeight/2, 0), 200 | Transform.rotateX(0) 201 | ), 202 | {duration: 1000, curve: 'easeInOut'} 203 | ); 204 | }, 2150); 205 | 206 | Timer.setTimeout(function () { 207 | destroyMod.setTransform( 208 | Transform.multiply( 209 | Transform.translate(0, window.innerHeight/2 + 50, 0), 210 | Transform.rotateY(0) 211 | ), 212 | {duration: 1000, curve: 'easeInOut'} 213 | ); 214 | }, 3650); 215 | 216 | // words slide out 217 | Timer.setTimeout(function () { 218 | crushMod.setOpacity(0, {duration: 500, curve: Easing.inCubic}); 219 | manipulateMod.setOpacity(0, {duration: 500, curve: Easing.inCubic}); 220 | destroyMod.setOpacity(0, {duration: 500, curve: Easing.inCubic}); 221 | }.bind(this), 5500); 222 | } 223 | 224 | function _startDemoText () { 225 | // acts as master timer for demo text 226 | // allows for incremental additions without subsequent changes 227 | var demoTextTimer = 2000; 228 | var swipeTransitionable = new Transitionable(0); 229 | var perspecTransitionable = new Transitionable(0); 230 | var destroyerTellTrans = new Transitionable(0); 231 | var objectiveTellTrans = new Transitionable(0); 232 | 233 | // create swipe instructional text 234 | var swipeTell = new Surface({ 235 | opacity: 0, 236 | properties: this.options.instrucTextProps 237 | }); 238 | 239 | var swipeTellMod = new Modifier({ 240 | size: [500, 50], 241 | align: [0.5, 0.5], 242 | origin: [0.5, 0.5], 243 | transform: Transform.translate(1000, 0, 0), 244 | opacity: function () { 245 | return swipeTransitionable.get(); 246 | } 247 | }); 248 | 249 | this.node.add(swipeTellMod).add(swipeTell); 250 | 251 | Timer.setTimeout(function () { 252 | swipeTell.setContent('Swipe to Rotate'); 253 | swipeTransitionable.set(1, { duration: 1000, curve: Easing.inBack }); 254 | swipeTellMod.setTransform(Transform.translate(0, 0, 0), {duration: 1500, curve: 'easeInOut'}); 255 | swipeTransitionable.set(0, { duration: 1000, curve: Easing.inBack }); 256 | }, demoTextTimer); 257 | 258 | // create perspective change instructional text 259 | var perspecChange = new Surface({ 260 | opacity: 0, 261 | properties: this.options.instrucTextProps 262 | }); 263 | 264 | var perspecChangeMod = new Modifier({ 265 | size: [500, 50], 266 | align: [0.5, 0.5], 267 | origin: [0.5, 0.5], 268 | transform: Transform.translate(0, 0, 0), 269 | opacity: function () { 270 | return perspecTransitionable.get(); 271 | } 272 | }); 273 | 274 | this.node.add(perspecChangeMod).add(perspecChange); 275 | 276 | demoTextTimer += 2300; 277 | Timer.setTimeout(function () { 278 | perspecChange.setContent('Toggle
2D and 3D'); 279 | perspecTransitionable.set(1, { duration: 1000, curve: Easing.inBack }); 280 | perspecTransitionable.set(0, { duration: 1000, curve: Easing.inBack }); 281 | }, demoTextTimer); 282 | 283 | // create destroyer instructional text 284 | var destroyerTell = new Surface({ 285 | opacity: 0, 286 | properties: this.options.instrucTextProps 287 | }); 288 | 289 | var destroyerTellMod = new Modifier({ 290 | size: [500, 50], 291 | align: [0.5, 0.5], 292 | origin: [0.5, 0.5], 293 | transform: Transform.translate(0, -25, 0), 294 | opacity: function () { 295 | return destroyerTellTrans.get(); 296 | } 297 | }); 298 | 299 | this.node.add(destroyerTellMod).add(destroyerTell); 300 | 301 | demoTextTimer += 2000; 302 | Timer.setTimeout(function () { 303 | destroyerTell.setContent('Crush
adjacent
cubes'); 304 | destroyerTellTrans.set(1, { duration: 1000, curve: Easing.inBack }); 305 | destroyerTellTrans.set(0, { duration: 1000, curve: Easing.inBack }); 306 | }, demoTextTimer); 307 | 308 | // create game objective instructional text 309 | var objectiveTell = new Surface({ 310 | opacity: 0, 311 | properties: this.options.instrucTextProps 312 | }); 313 | 314 | var objectiveTellMod = new Modifier({ 315 | size: [500, 50], 316 | align: [0.5, 0.5], 317 | origin: [0.5, 0.5], 318 | opacity: function () { 319 | return objectiveTellTrans.get(); 320 | } 321 | }); 322 | 323 | this.node.add(objectiveTellMod).add(objectiveTell); 324 | 325 | demoTextTimer += 3000; 326 | Timer.setTimeout(function () { 327 | objectiveTell.setContent('Clear the Cube'); 328 | objectiveTellTrans.set(1, { duration: 1000, curve: Easing.inBack }); 329 | objectiveTellTrans.set(0, { duration: 1000, curve: Easing.inBack }); 330 | }, demoTextTimer); 331 | } 332 | 333 | function _startDemoPlay () { 334 | // acts as master timer for demo text 335 | // allows for incremental additions without subsequent changes 336 | var demoTimer = 5000; 337 | // centers cube 338 | var rootMod = new Modifier(); 339 | this.gameLogic = new GameLogic(); 340 | this.gameLogic.startNewGame({level: Levels.introVideo}); 341 | //remove sound 342 | this.gameLogic.setSoundOff(true); 343 | 344 | var demoBoardModifier = new Modifier({ 345 | align: [0.5, 0.5], 346 | origin: [0.5, 0.5], 347 | transform: Transform.multiply( 348 | Transform.rotate(Math.PI/2, Math.PI/2, Math.PI/2), 349 | Transform.translate(1500, 0, 0) 350 | ) 351 | }); 352 | 353 | this.node = this.add(rootMod); 354 | this.node.add(demoBoardModifier).add(this.gameLogic.rotatingLogic); 355 | this.node.add(this.gameLogic.perspectiveButtonMod).add(this.gameLogic.perspectiveButton); 356 | 357 | // disables clicks on perspective button 358 | this.gameLogic.perspectiveButton.setProperties({pointerEvents: 'none'}); 359 | 360 | // perspectiveButton slides in 361 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.translate(0, 2000, 0),{duration: 0, curve: 'easeInOut'}); 362 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.translate(0, 0, 0),{duration: 2000, curve: 'easeInOut'}); 363 | 364 | // board slides in 365 | demoBoardModifier.setTransform(Transform.translate(0,0,0),{duration: 2500, curve: 'easeInOut'}); 366 | 367 | // rotate right 368 | demoBoardModifier.setTransform(Transform.rotate(0, -Math.PI/2, 0), {duration: 1000, curve: 'easeInOut'}); 369 | 370 | // switch to 2D 371 | Timer.setTimeout(function () { 372 | this._eventOutput.emit('is2dDemo', true); 373 | this.gameLogic.perspectiveButton.setContent('3D'); 374 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 375 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 376 | }.bind(this), demoTimer); 377 | 378 | // crush 379 | demoTimer += 2700; 380 | Timer.setTimeout(function () { 381 | this.gameLogic.rotatingLogic.setDestroyerPosition([0, 3, 1]); 382 | this.gameLogic.removeSmallCube([0, 3, 1]); 383 | }.bind(this), demoTimer); 384 | 385 | // switch to 3D 386 | demoTimer += 2000; 387 | Timer.setTimeout(function () { 388 | this._eventOutput.emit('is2dDemo', false); 389 | this.gameLogic.perspectiveButton.setContent('2D'); 390 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 391 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 392 | }.bind(this), demoTimer); 393 | 394 | // rotate up 395 | demoTimer += 1200; 396 | Timer.setTimeout(function () { 397 | demoBoardModifier.setTransform(Transform.rotate(Math.PI/2, 3 * Math.PI/2, 0), {duration: 1000, curve: 'easeInOut'}); 398 | }.bind(this), demoTimer); 399 | 400 | // switch to 2D 401 | demoTimer += 1500; 402 | Timer.setTimeout(function () { 403 | this._eventOutput.emit('is2dDemo', true); 404 | this.gameLogic.perspectiveButton.setContent('3D'); 405 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 406 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 407 | }.bind(this), demoTimer); 408 | 409 | // crush 410 | demoTimer += 1200; 411 | Timer.setTimeout(function () { 412 | this.gameLogic.rotatingLogic.setDestroyerPosition([0, 2, 2]); 413 | this.gameLogic.removeSmallCube([0, 2, 2]); 414 | }.bind(this), demoTimer); 415 | 416 | // crush 417 | demoTimer += 600; 418 | Timer.setTimeout(function () { 419 | this.gameLogic.rotatingLogic.setDestroyerPosition([0, 1, 3]); 420 | this.gameLogic.removeSmallCube([0, 1, 3]); 421 | }.bind(this), demoTimer); 422 | 423 | // switch to 3D 424 | demoTimer += 1000; 425 | Timer.setTimeout(function () { 426 | this._eventOutput.emit('is2dDemo', false); 427 | this.gameLogic.perspectiveButton.setContent('2D'); 428 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 429 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 430 | }.bind(this), demoTimer); 431 | 432 | // rotate up 433 | demoTimer += 1200; 434 | Timer.setTimeout(function () { 435 | demoBoardModifier.setTransform(Transform.rotate(Math.PI, -Math.PI/2, 0), {duration: 1000, curve: 'easeInOut'}); 436 | }.bind(this), demoTimer); 437 | 438 | // switch to 2D 439 | demoTimer += 1500; 440 | Timer.setTimeout(function () { 441 | this._eventOutput.emit('is2dDemo', true); 442 | this.gameLogic.perspectiveButton.setContent('3D'); 443 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 444 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 445 | }.bind(this), demoTimer); 446 | 447 | // crush 448 | demoTimer += 1200; 449 | Timer.setTimeout(function () { 450 | this.gameLogic.rotatingLogic.setDestroyerPosition([3, 0, 3]); 451 | this.gameLogic.removeSmallCube([3, 0, 3]); 452 | }.bind(this), demoTimer); 453 | 454 | // crush 455 | demoTimer += 600; 456 | Timer.setTimeout(function () { 457 | this.gameLogic.rotatingLogic.setDestroyerPosition([2, 0, 2]); 458 | this.gameLogic.removeSmallCube([2, 0, 2]); 459 | }.bind(this), demoTimer); 460 | 461 | // crush 462 | demoTimer += 600; 463 | Timer.setTimeout(function () { 464 | this.gameLogic.rotatingLogic.setDestroyerPosition([1, 0, 1]); 465 | this.gameLogic.removeSmallCube([1, 0, 1]); 466 | }.bind(this), demoTimer); 467 | 468 | // win 469 | // switch to 3D 470 | demoTimer += 1200; 471 | Timer.setTimeout(function () { 472 | this._eventOutput.emit('is2dDemo', false); 473 | this.gameLogic.perspectiveButton.setContent('2D'); 474 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(0.95,0.95,1), {duration: 200, curve: 'easeInOut'}); 475 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.scale(1,1,1), {duration: 200, curve: 'easeInOut'}); 476 | }.bind(this), demoTimer); 477 | 478 | // board and perspectiveButton slide out 479 | demoTimer += 1000; 480 | Timer.setTimeout(function () { 481 | demoBoardModifier.setTransform(Transform.translate(-1000, 0, 0), {duration: 1000, curve: 'easeInOut'}); 482 | this.gameLogic.perspectiveButtonMod.setTransform(Transform.translate(-1000, 0, 0), {duration: 1000, curve: 'easeInOut'}); 483 | }.bind(this), demoTimer); 484 | 485 | // lightbox shows mainMenu 486 | demoTimer += 500; 487 | Timer.setTimeout(function () { 488 | this._eventOutput.emit('demoToMainMenu'); 489 | }.bind(this), demoTimer); 490 | 491 | } 492 | 493 | module.exports = DemoView; 494 | }); 495 | -------------------------------------------------------------------------------- /app/src/views/GameLogic.js: -------------------------------------------------------------------------------- 1 | /* This the heart of the game, controlls all the cube movement and game play logic*/ 2 | define(function(require, exports, module) { 3 | var View = require('famous/core/View'); 4 | var Surface = require('famous/core/Surface'); 5 | var Transform = require('famous/core/Transform'); 6 | var Modifier = require('famous/core/Modifier'); 7 | var Timer = require('famous/utilities/Timer'); 8 | var RotatingLogic = require('views/RotatingLogic'); 9 | // var Howler = require('howler'); // Invoked as Howl 10 | 11 | function GameLogic() { 12 | View.apply(this, arguments); 13 | 14 | // Create Root Modifier 15 | var rootModifier = new Modifier(); 16 | this.node = this.add(rootModifier); 17 | 18 | this.isInDemoView = false; // boolean to terminate sound if in demo view 19 | this.showMenu = false; // boolean to show or hide menu 20 | this.ready = true; // waiting for the menu transition is complete 21 | 22 | // Create sound objects 23 | this.crushSound = new Howl({ 24 | urls: ['content/sounds/Smack.wav'] 25 | }); 26 | 27 | this.completeSound = new Howl({ 28 | urls: ['content/sounds/level-up.wav'] 29 | }); 30 | 31 | this.transitionSound = new Howl({ 32 | urls: ['content/sounds/swoosh.wav'] 33 | }); 34 | 35 | this.twoDDataStructure = {}; // data structure to track of cubes in 2D mode 36 | this.is2d = false; // 2D 3D state 37 | this.board = undefined; // reference to game board 38 | this.destroyerCubeLocation = undefined; //reference to destroyer cube location 39 | 40 | _determineCubeSize.call(this); 41 | _createRotatingLogic.call(this); 42 | _createPerspectiveButton.call(this); 43 | _destroyerMovement.call(this); 44 | _setListeners.call(this); 45 | _createMenu.call(this); 46 | _createGithubLink.call(this); 47 | } 48 | 49 | GameLogic.prototype = Object.create(View.prototype); 50 | GameLogic.prototype.constructor = GameLogic; 51 | 52 | 53 | function _createGithubLink () { 54 | var banner = new Surface({ 55 | size: [window.innerWidth * 0.05, window.innerHeight * 0.05], 56 | content: '' + 57 | '' + 60 | '', 61 | properties: { 62 | zIndex: 5 63 | } 64 | }); 65 | 66 | var bannerMod = new Modifier({ 67 | align: [0, 0], 68 | origin: [0, 0], 69 | transform: function () { 70 | if (window.innerWidth < 800) { 71 | return Transform.scale(0.7, 0.7, 0.7); 72 | } 73 | }.bind(this) 74 | }); 75 | 76 | this.add(bannerMod).add(banner); 77 | } 78 | 79 | // Set Game logic default options with default game board 80 | GameLogic.DEFAULT_OPTIONS = { 81 | mainCubeSize: 200, 82 | destroyer: [-1000, -1000, -1000], 83 | smallCube: [ 84 | [-1000, -1000, -1000], 85 | [-1000, -1000, -1000], 86 | [-1000, -1000, -1000], 87 | [-1000, -1000, -1000], 88 | 89 | [-1000, -1000, -1000], 90 | [-1000, -1000, -1000], 91 | [-1000, -1000, -1000], 92 | [-1000, -1000, -1000], 93 | 94 | [-1000, -1000, -1000], 95 | [-1000, -1000, -1000], 96 | [-1000, -1000, -1000], 97 | [-1000, -1000, -1000], 98 | 99 | [-1000, -1000, -1000], 100 | [-1000, -1000, -1000], 101 | [-1000, -1000, -1000], 102 | [-1000, -1000, -1000] 103 | ] 104 | }; 105 | 106 | // Create menu for game logic view to restart, move to menu view, move to level view 107 | function _createMenu () { 108 | var menuButton = new Surface({ 109 | content: 'Menu', 110 | properties: { 111 | textAlign: 'center', 112 | fontSize: '.8rem', 113 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 114 | zIndex: 4, 115 | lineHeight: '45px', 116 | cursor: 'pointer' 117 | } 118 | }); 119 | 120 | // Create menu modifier to align top right corner 121 | var menuButtonMod = new Modifier({ 122 | size: [50, 50], 123 | align: [1, 0], 124 | origin: [1, 0] 125 | }); 126 | 127 | // Create restart button surface 128 | var restartButton = new Surface({ 129 | content:'Restart', 130 | properties: { 131 | textAlign: 'center', 132 | fontSize: '.8rem', 133 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 134 | zIndex: 4, 135 | lineHeight: '45px', 136 | cursor: 'pointer' 137 | } 138 | }); 139 | 140 | var restartButtonMod = new Modifier({ 141 | size: [50, 50], 142 | align: [1, 0], 143 | origin: [1, 0], 144 | transform: Transform.translate(-50, -50, 0) 145 | }); 146 | 147 | // Create button surface to move back to level selection view 148 | var levelSelectButton = new Surface({ 149 | content:'Levels', 150 | properties: { 151 | textAlign: 'center', 152 | fontSize: '.8rem', 153 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 154 | zIndex: 4, 155 | lineHeight: '45px', 156 | cursor: 'pointer' 157 | } 158 | }); 159 | 160 | var levelSelectButtonMod = new Modifier({ 161 | size: [50, 50], 162 | align: [1, 0], 163 | origin: [1, 0], 164 | transform: Transform.translate(-100, -50, 0) 165 | }); 166 | 167 | // Create button surface to move back to menu view 168 | var exitButton = new Surface({ 169 | content:'Exit', 170 | properties: { 171 | textAlign: 'center', 172 | fontSize: '.8rem', 173 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 174 | zIndex: 4, 175 | lineHeight: '45px', 176 | cursor: 'pointer' 177 | } 178 | }); 179 | 180 | var exitButtonMod = new Modifier({ 181 | size: [50, 50], 182 | align: [1, 0], 183 | origin: [1, 0], 184 | transform: Transform.translate(-140, -50, 0) 185 | 186 | }); 187 | 188 | // adding all the buttons to the root modifier 189 | this.node.add(menuButtonMod).add(menuButton); 190 | this.node.add(levelSelectButtonMod).add(levelSelectButton); 191 | this.node.add(restartButtonMod).add(restartButton); 192 | this.node.add(exitButtonMod).add(exitButton); 193 | 194 | // create event listeners on each button 195 | menuButton.on('click', function () { 196 | if (!this.ready) return; 197 | if (this.showMenu) { 198 | this.ready = false; 199 | _hideMenu.call(this); 200 | this.showMenu = !this.showMenu; 201 | } else { 202 | this.ready = false; 203 | _showMenu.call(this); 204 | this.showMenu = !this.showMenu; 205 | } 206 | }.bind(this)); 207 | 208 | restartButton.on('click', function () { 209 | if (!this.showMenu) return false; 210 | // console.log('clicked restart'); 211 | _restartGame.call(this); 212 | this.showMenu = false; 213 | Timer.setTimeout(_hideMenu, 500); 214 | }.bind(this)); 215 | 216 | exitButton.on('click', function () { 217 | if (!this.showMenu) return false; 218 | this._eventOutput.emit('mainMenu'); 219 | this.showMenu = false; 220 | Timer.setTimeout(_hideMenu, 500); 221 | }.bind(this)); 222 | 223 | levelSelectButton.on('click', function () { 224 | if (!this.showMenu) return false; 225 | this._eventOutput.emit('levels'); 226 | this.showMenu = false; 227 | Timer.setTimeout(_hideMenu, 500); 228 | }.bind(this)); 229 | 230 | // hide the menu and transform each button off the page 231 | function _hideMenu () { 232 | restartButtonMod.setTransform(Transform.translate(-50, -50, 0), {duration: 500, curve: 'easeInOut'}); 233 | levelSelectButtonMod.setTransform(Transform.translate(-100, -50, 0), {duration: 400, curve: 'easeInOut'}); 234 | exitButtonMod.setTransform(Transform.translate(-140, -50, 0), {duration: 300, curve: 'easeInOut'}); 235 | Timer.setTimeout(function () {this.ready = true;}.bind(this), 500); 236 | } 237 | // hide the menu and transform each button on to the page 238 | function _showMenu () { 239 | restartButtonMod.setTransform(Transform.translate(-50, 0, 0), {duration: 300, curve: 'easeInOut'}); 240 | levelSelectButtonMod.setTransform(Transform.translate(-100, 0, 0), {duration: 400, curve: 'easeInOut'}); 241 | exitButtonMod.setTransform(Transform.translate(-140, 0, 0), {duration: 500, curve: 'easeInOut'}); 242 | Timer.setTimeout(function () {this.ready = true;}.bind(this), 500); 243 | } 244 | } 245 | 246 | function _saveToLocalStorage (levelIndex) { 247 | // checks to see if localstorage is enabled 248 | if (!window.localStorage || window.localStorage === null) return; 249 | 250 | var localStorage = window.localStorage.getItem('famospace').split(','); 251 | 252 | //send event to LevelSelectionView to change level color and update localStorage 253 | if (localStorage[levelIndex] === '0') this._eventOutput.emit('levelCompleted', levelIndex); 254 | } 255 | 256 | // determine the game board (main cube) size base on window width; 257 | // greater than 800: 400x400x400 cube 258 | // less than 800: 200x200x200 cube 259 | function _determineCubeSize(){ 260 | if (window.innerWidth > 825 || window.innerHeight > 825){ 261 | this.options.mainCubeSize = 400; 262 | } 263 | } 264 | // method to start a new game base on input data of cube location (starter package); 265 | // reset all variables 266 | function _startNewGame (starter){ 267 | this.exitAnimMod.setTransform(Transform.rotate(0, 0, 0)); 268 | this.levelIndex = starter.levelNum; 269 | this.starter = starter; 270 | this.board = _forceSlice(starter.level.smallCube); 271 | this.destroyerCubeLocation = starter.level.destroyer; 272 | this.rotatingLogic.startNewGame(starter.level); 273 | this._eventOutput.trigger('is2d', false); 274 | this.perspectiveButton.setContent('2D'); 275 | this.is2d = false; 276 | this.twoDDataStructure = {}; 277 | } 278 | 279 | // restart current level by startNewGame with the current starter package 280 | function _restartGame(){ 281 | _startNewGame.call(this,this.starter); 282 | } 283 | 284 | // set startNewGame to prototype for external access 285 | GameLogic.prototype.startNewGame = _startNewGame; 286 | 287 | // set soundOff variabale for demo view 288 | GameLogic.prototype.setSoundOff = function(bool){ 289 | this.isInDemoView = bool; 290 | }; 291 | 292 | // Create the button to change 2D/3D perspective 293 | function _createPerspectiveButton () { 294 | // Create button surface 295 | this.perspectiveButton = new Surface({ 296 | size: [undefined, undefined], 297 | content: '
2D
', 298 | properties: { 299 | fontSize: '3rem', 300 | fontFamily: 'HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif', 301 | textAlign: 'center', 302 | lineHeight: '65px', 303 | verticalAlign: 'middle', 304 | color: 'white', 305 | backgroundColor: '#34A4CC', 306 | borderRadius: '20px', 307 | border: '3px solid #738F99', 308 | cursor: 'pointer', 309 | zIndex: 5 310 | } 311 | }); 312 | 313 | // create a modifier that will dynamically relocate the button base on the 314 | // size of the window with respect to cubesize 315 | this.perspectiveButtonMod = new Modifier ({ 316 | size: [75, 75], 317 | align: [0.5, 0.5], 318 | origin: function () { 319 | if (this.options.mainCubeSize < 400){ 320 | if (window.innerWidth < window.innerHeight) { 321 | return [0.5, 0.98]; 322 | }else{ 323 | return [0.98, 0.5]; 324 | } 325 | }else{ 326 | if (window.innerHeight > window.innerWidth) { 327 | if (window.innerHeight < 1000){ 328 | return [0.5, 0.97]; 329 | }else{ 330 | return [0.5, 0.83]; 331 | } 332 | }else{ 333 | if (window.innerWidth < 1100){ 334 | return [0.95, 0.5]; 335 | }else{ 336 | return [0.83, 0.5]; 337 | } 338 | } 339 | } 340 | }.bind(this) 341 | }); 342 | 343 | // Create event listeners for 2D/3D transition 344 | this.perspectiveButton.on('click', function () { 345 | if (this.is2d === false && _ableToConvertTo2d.call(this) === true) { 346 | if (!this.isInDemoView) this.transitionSound.play(); 347 | this._eventOutput.trigger('is2d', true); 348 | this.perspectiveButton.setContent('3D'); 349 | this.is2d = !this.is2d; 350 | } else if (this.is2d === false && _ableToConvertTo2d.call(this) === false) { 351 | _deny3D.call(this); 352 | } else { 353 | if (!this.isInDemoView) this.transitionSound.play(); 354 | this._eventOutput.trigger('is2d', false); 355 | this.perspectiveButton.setContent('2D'); 356 | this.is2d = !this.is2d; 357 | _convertTo3d.call(this); 358 | } 359 | }.bind(this)); 360 | 361 | this.node.add(this.perspectiveButtonMod).add(this.perspectiveButton); 362 | } 363 | 364 | // Create the rotating logic which controls the orientation of the game board 365 | function _createRotatingLogic () { 366 | this.exitAnimMod = new Modifier(); 367 | 368 | this.rotatingLogic = new RotatingLogic({ 369 | mainCubeSize: this.options.mainCubeSize, 370 | destroyer: this.options.destroyer, 371 | smallCube: this.options.smallCube 372 | }); 373 | 374 | this.node.add(this.exitAnimMod).add(this.rotatingLogic); 375 | } 376 | 377 | // set even listeners to main.js and rotating logic view 378 | function _setListeners (){ 379 | this.rotatingLogic.pipe(this._eventInput); 380 | this.rotatingLogic.subscribe(this._eventOutput); 381 | this._eventInput.on('startGame', function(data){ 382 | _startNewGame.call(this,data); 383 | }.bind(this)); 384 | } 385 | 386 | // A visual effect created for an illegal 3D to 2D transition 387 | function _deny3D () { 388 | this.transitionSound.play(); 389 | this._eventOutput.trigger('is2d', true); 390 | Timer.setTimeout(function () { 391 | this.transitionSound.play(); 392 | this._eventOutput.trigger('is2d', false); 393 | }.bind(this), 600); 394 | } 395 | 396 | // Determine the destroyers movement when reciving a moving cube event 397 | function _destroyerMovement(){ 398 | this._eventInput.on('movingCubeToGL', function(data){ 399 | // make a copy of the current cube location 400 | var requestedPos = this.destroyerCubeLocation.slice(); 401 | //an update flag, if movement update is required 402 | var update = false; 403 | for (var i =0; i= 0 && tempUpdate <= 3){ //determine 409 | requestedPos[i] = tempUpdate; 410 | update = true; 411 | } 412 | } 413 | if(update){ // if movement update is required check adjacent space 414 | var newPos = _DCcanMove.call(this, requestedPos); 415 | 416 | if (newPos){ 417 | //if a new positions is returned, move the destroyer cube and remove small cube 418 | this.destroyerCubeLocation = newPos; 419 | _removeSmallCube.call(this, newPos); 420 | this.rotatingLogic.setDestroyerPosition(newPos); 421 | } 422 | } 423 | }.bind(this)); 424 | } 425 | 426 | // function to determine if destroyer cube can move to the appointed location by determining 427 | // whethere is is a small cube in that location 428 | function _DCcanMove (newPos) { 429 | // Determine th current axial position in 2D view 430 | var currentAxis = _findCurrentXY(this.rotatingLogic.nVec, this.rotatingLogic.rVec, this.rotatingLogic.state); 431 | 432 | // create key for 2D data structure object 433 | var newPos2D = [newPos[currentAxis.x], newPos[currentAxis.y]].join(''); 434 | 435 | // determine if key returns an array, if yes, pop the last element and return 436 | if (this.twoDDataStructure[newPos2D]) { 437 | if(this.twoDDataStructure[newPos2D].length > 0){ 438 | var output = this.twoDDataStructure[newPos2D].pop(); 439 | return output; 440 | } 441 | } 442 | return false; 443 | } 444 | // remove small cube from given position 445 | function _removeSmallCube(pos){ 446 | for(var i =0; i < this.board.length; i++){ 447 | // if position matches the position at i in the game board 448 | if (this.board[i][0] === pos[0] 449 | && this.board[i][1] === pos[1] 450 | && this.board[i][2] === pos[2]){ 451 | 452 | // remove the piece from array 453 | this.board.splice(i,1); 454 | //play sound if allowed 455 | if (!this.isInDemoView) this.crushSound.play(); 456 | if(this.board.length < 1){ 457 | // if board is less than 1 (last piece); play sound move back to levels view 458 | if (!this.isInDemoView) _endLevel.call(this); 459 | 460 | Timer.setTimeout(function(){ 461 | this._eventOutput.emit('levels'); 462 | }.bind(this), 500); 463 | 464 | } 465 | return; 466 | } 467 | } 468 | // console.log('no cube removed', pos); 469 | } 470 | 471 | // expose removeSmallCube function for external use (demo view) 472 | GameLogic.prototype.removeSmallCube = _removeSmallCube; 473 | 474 | 475 | function _endLevel () { 476 | this.completeSound.play(); 477 | _saveToLocalStorage.call(this, this.levelIndex); 478 | this._eventOutput.trigger('is2d', false); 479 | 480 | this.exitAnimMod.setTransform( 481 | Transform.rotate(Math.PI * 4 * Math.random(), Math.PI * 4 * Math.random(), Math.PI * 4 * Math.random()), 482 | {duration: 1200, curve: 'easeInOut'} 483 | ); 484 | } 485 | 486 | 487 | 488 | // determine wheter converting to 2D is a legal move (when a small cube is above, 489 | // in terms of depth, of the destroyer cube in 3D mode, 2D mode is not allowed) 490 | function _ableToConvertTo2d () { 491 | _create2dDataStructure.call(this); 492 | // find current zAxis and zPosNeg 493 | var currentAxis = _findCurrentXY.call(this, this.rotatingLogic.nVec, this.rotatingLogic.rVec, this.rotatingLogic.state); 494 | // find current destroyerCubeLocation 495 | var dcLocation = this.destroyerCubeLocation; 496 | var key = ''; 497 | key += dcLocation[currentAxis.x]; 498 | key += dcLocation[currentAxis.y]; 499 | 500 | // find current twoDDataStructure 501 | var currentSmallCubePos = this.twoDDataStructure; 502 | var match = currentSmallCubePos[key]; 503 | 504 | // if match is not null and check the depth between destroyer and small cubes 505 | return ( match && 506 | ( 507 | ( currentAxis.zPosNeg > 0 && (dcLocation[currentAxis.z] < match[0][currentAxis.z])) || 508 | ( currentAxis.zPosNeg < 0 && (dcLocation[currentAxis.z] > match[0][currentAxis.z])) 509 | ) 510 | ) ? false : true; 511 | } 512 | 513 | // determine the current state of axial position 514 | function _findCurrentXY (nVec, rVec, state) { 515 | // identifies which index is current X, Y, and Z 516 | var result = {}, i, j, k; 517 | 518 | // normal vector is y axis in 2D 519 | for (i=0;i