├── README.md
├── css
└── main.css
├── img
├── apple-touch-icon-114x114.png
├── apple-touch-icon-72x72.png
├── bg.png
├── blank.png
├── door.png
├── end.png
├── favicon.ico
├── game-window.png
├── goal.png
├── loading-text.png
├── loading.gif
├── menu-button.png
├── new-game.png
├── next-button.png
├── platform-left.png
├── platform-middle.png
├── platform-right.png
├── player.png
├── reset.png
├── scorecard-background.png
├── screenshot-1.png
├── screenshot-2.png
├── screenshot-3.png
├── screenshot-4.png
├── selected.png
├── splash-screen.png
├── star-off.png
├── star-on.png
├── switch.png
└── touch-icon-iphone.png
├── index.htm
└── js
├── gamejs
├── gamejs.min.js
└── yabble.js
├── jquery-1.8.2.js
├── lib
├── block.js
├── camera.js
├── door.js
├── gates
│ ├── andGate.js
│ ├── notGate.js
│ └── orGate.js
├── goal.js
├── io.js
├── levels
│ ├── level_1.js
│ ├── level_2.js
│ ├── level_3.js
│ ├── level_4.js
│ └── level_5.js
├── lever.js
├── menu.js
├── menuItem.js
├── platform.js
├── playable.js
├── player.js
├── scorecard.js
├── startMenu.js
├── tooltip.js
├── utils.js
└── world.js
└── main.js
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ## Synopsis
5 |
6 | Alge has been trapped in Area 51! Help him escape by moving him through the levels to the escape tube at the end.
7 | Clone your way top victory by turning the correct combination of switches on to turn off the lasers and escape to
8 | freedom.
9 |
10 | Can you get three stars on every level?
11 |
12 | ## Controls
13 |
14 | Control Alge by using the following keys:
15 |
16 | * D or Right arrow to move Alge right
17 | * A or Left arrow to move Alge left
18 | * W, Up arrow or Space to jump
19 | * E or Enter to activate or deactivate a switch
20 | * C to clone Alge
21 |
22 | ##Browser Compatibility
23 |
24 | Tested in Firefox Safari, and Chrome on Windows, Linux and Mac.
25 |
26 | ##Technology Used
27 |
28 | * HTML5
29 | * JavaScript
30 | * [GameJs](http://gamejs.org/)
31 |
32 | ##Screenshots
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | body
2 | {
3 | background-color:#ECECEC;
4 | overflow:hidden;
5 | }
6 |
7 | #preload, #gameWindow
8 | {
9 | margin:auto;
10 | }
11 |
12 | #gameWindow
13 | {
14 | padding:19px 29px;
15 | width:800px;
16 | height:600px;
17 | background-image:url('../img/game-window.png');
18 | background-repeat:no-repeat;
19 | display:none;
20 | }
21 |
22 | canvas
23 | {
24 | -moz-box-shadow: 0 0 10px #000000;
25 | -webkit-box-shadow: 0 0 10px #000000;
26 | box-shadow: 0 0 10px #000000;
27 | }
28 |
29 | #gameEnd
30 | {
31 | position:relative;
32 | display:none;
33 | width:800px;
34 | height:600px;
35 | background-image:url('../img/end.png');
36 | top:-604px;
37 | }
38 |
39 | #preload
40 | {
41 | padding-top:250px;
42 | width:800px;
43 | height:600px;
44 | text-align:center;
45 | }
46 |
47 | #preload img
48 | {
49 | display:block;
50 | margin: auto;
51 | }
52 |
53 | #game_tooltip
54 | {
55 | position:relative;
56 | width:800px;
57 | background: -webkit-linear-gradient(#ECECEC, #CACACA);
58 | background: -moz-linear-gradient(#ECECEC, #CACACA);
59 | background: -o-linear-gradient(#ECECEC, #CACACA);
60 | background: -ms-linear-gradient(#ECECEC, #CACACA);
61 | background: linear-gradient(#ECECEC, #CACACA);
62 | border-bottom: 1px solid #9A9A9A;
63 | border-radius: 0px 0px 10px 10px;
64 | color:#585351;
65 | text-shadow: 0px 1px 0px white;
66 | padding-top:10px;
67 | padding-bottom:10px;
68 | font-weight: bold;
69 | text-align: center;
70 | top:-605px;
71 | display:none;
72 | }
73 |
74 | #game_scorecard_bg
75 | {
76 | position:relative;
77 | width:800px;
78 | height:600px;
79 | top:-624px;
80 | background-color: rgba(0,0,0,0.4);
81 | display:none;
82 | }
83 |
84 | #game_scorecard
85 | {
86 | background-image:url('../img/scorecard-background.png');
87 | position:relative;
88 | top:68px;
89 | left:140px;
90 | width:500px;
91 | height:460px;
92 | z-index: 999;
93 | font-family: Helvetica,arial,sans-serif;
94 | }
95 |
96 | #game_scorecard h1
97 | {
98 | font-size:30px;
99 | text-transform:uppercase;
100 | padding-top:37px;
101 | margin-left:41px;
102 | }
103 |
104 | #game_scorecard p
105 | {
106 | font-size:20px;
107 | font-weight: bold;
108 | margin-left:38px;
109 | border-bottom:1px solid #000000;
110 | width:418px;
111 | margin-top:10px;
112 | }
113 |
114 | #game_scorecard p.scoreholder
115 | {
116 | height:38px;
117 | }
118 |
119 | #game_scorecard p span
120 | {
121 | float:right;
122 | }
123 |
124 |
125 | #game_scorecard .score .star
126 | {
127 | width:42px;
128 | height:41px;
129 | background-image:url(../img/star-off.png);
130 | background-repeat: no-repeat;
131 | float:left;
132 | position:relative;
133 | top:-10px;
134 | }
135 |
136 | #game_scorecard .score .star.enabled
137 | {
138 | background-image:url(../img/star-on.png);
139 | }
140 |
141 | #game_scorecard .nextLevel,
142 | #game_scorecard .resetLevel,
143 | #game_scorecard .mainMenu
144 | {
145 | display:block;
146 | font-size:-1px;
147 | text-indent:-9999px;
148 | outline: none;
149 | background-repeat:no-repeat;
150 | position:relative;
151 | margin-top:60px;
152 | }
153 |
154 | #game_scorecard .nextLevel
155 | {
156 | background-image:url(../img/next-button.png);
157 | width:180px;
158 | height:92px;
159 | top:-313px;
160 | margin-left:277px;
161 | background-position: 0px 0px;
162 | }
163 |
164 | #game_scorecard .nextLevel:hover
165 | {
166 | background-position: -190px 0px;
167 | }
168 |
169 | #game_scorecard .mainMenu
170 | {
171 | background-image:url(../img/menu-button.png);
172 | width:180px;
173 | height:92px;
174 | margin-left:28px;
175 | background-position: 0px 0px;
176 | }
177 |
178 | #game_scorecard .mainMenu:hover
179 | {
180 | background-position: -190px 0px;
181 | }
182 |
183 | #game_scorecard .resetLevel
184 | {
185 | background-image:url(../img/reset.png);
186 | width:100px;
187 | height:101px;
188 | top:-160px;
189 | margin-left:198px;
190 | }
191 |
192 | #game_scorecard .disabled
193 | {
194 | opacity:0.5;
195 | }
196 |
197 | #game_scorecard .disabled:hover
198 | {
199 | cursor: default;
200 | background-position: 0px 0px;
201 | }
202 |
--------------------------------------------------------------------------------
/img/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/img/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/bg.png
--------------------------------------------------------------------------------
/img/blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/blank.png
--------------------------------------------------------------------------------
/img/door.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/door.png
--------------------------------------------------------------------------------
/img/end.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/end.png
--------------------------------------------------------------------------------
/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/favicon.ico
--------------------------------------------------------------------------------
/img/game-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/game-window.png
--------------------------------------------------------------------------------
/img/goal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/goal.png
--------------------------------------------------------------------------------
/img/loading-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/loading-text.png
--------------------------------------------------------------------------------
/img/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/loading.gif
--------------------------------------------------------------------------------
/img/menu-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/menu-button.png
--------------------------------------------------------------------------------
/img/new-game.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/new-game.png
--------------------------------------------------------------------------------
/img/next-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/next-button.png
--------------------------------------------------------------------------------
/img/platform-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/platform-left.png
--------------------------------------------------------------------------------
/img/platform-middle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/platform-middle.png
--------------------------------------------------------------------------------
/img/platform-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/platform-right.png
--------------------------------------------------------------------------------
/img/player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/player.png
--------------------------------------------------------------------------------
/img/reset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/reset.png
--------------------------------------------------------------------------------
/img/scorecard-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/scorecard-background.png
--------------------------------------------------------------------------------
/img/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/screenshot-1.png
--------------------------------------------------------------------------------
/img/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/screenshot-2.png
--------------------------------------------------------------------------------
/img/screenshot-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/screenshot-3.png
--------------------------------------------------------------------------------
/img/screenshot-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/screenshot-4.png
--------------------------------------------------------------------------------
/img/selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/selected.png
--------------------------------------------------------------------------------
/img/splash-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/splash-screen.png
--------------------------------------------------------------------------------
/img/star-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/star-off.png
--------------------------------------------------------------------------------
/img/star-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/star-on.png
--------------------------------------------------------------------------------
/img/switch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/switch.png
--------------------------------------------------------------------------------
/img/touch-icon-iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dave-and-Mike/game-off-2012/7780711180fc2b5a074928f804d47c7344851264/img/touch-icon-iphone.png
--------------------------------------------------------------------------------
/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 | Alge's Escapade
24 |
25 |
26 |
27 |

28 |

29 |
30 |
34 |
35 |
--------------------------------------------------------------------------------
/js/gamejs/yabble.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010 James Brantly
3 | *
4 | * Permission is hereby granted, free of charge, to any person
5 | * obtaining a copy of this software and associated documentation
6 | * files (the "Software"), to deal in the Software without
7 | * restriction, including without limitation the rights to use,
8 | * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the
10 | * Software is furnished to do so, subject to the following
11 | * conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be
14 | * included in all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | * OTHER DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | (function(globalEval) {
27 |
28 | var Yabble = function() {
29 | throw "Synchronous require() is not supported.";
30 | };
31 |
32 | Yabble.unit = {};
33 |
34 | var _moduleRoot = '',
35 | _modules,
36 | _callbacks,
37 | _fetchFunc,
38 | _timeoutLength = 20000,
39 | _mainProgram;
40 |
41 |
42 | var head = document.getElementsByTagName('head')[0];
43 |
44 | // Shortcut to native hasOwnProperty
45 | var hasOwnProperty = Object.prototype.hasOwnProperty;
46 |
47 | // A for..in implementation which uses hasOwnProperty and fixes IE non-enumerable issues
48 | if ((function() {for (var prop in {hasOwnProperty: true}) { return prop; }})() == 'hasOwnProperty') {
49 | var forIn = function(obj, func, ctx) {
50 | for (var prop in obj) {
51 | if (hasOwnProperty.call(obj, prop)) {
52 | func.call(ctx, prop);
53 | }
54 | }
55 | };
56 | }
57 | else {
58 | var ieBadProps = [
59 | 'isPrototypeOf',
60 | 'hasOwnProperty',
61 | 'toLocaleString',
62 | 'toString',
63 | 'valueOf'
64 | ];
65 |
66 | var forIn = function(obj, func, ctx) {
67 | for (var prop in obj) {
68 | if (hasOwnProperty.call(obj, prop)) {
69 | func.call(ctx, prop);
70 | }
71 | }
72 |
73 | for (var i = ieBadProps.length; i--;) {
74 | var prop = ieBadProps[i];
75 | if (hasOwnProperty.call(obj, prop)) {
76 | func.call(ctx, prop);
77 | }
78 | }
79 | };
80 | }
81 |
82 | // Array convenience functions
83 | var indexOf = function(arr, val) {
84 | for (var i = arr.length; i--;) {
85 | if (arr[i] == val) { return i; }
86 | }
87 | return -1;
88 | };
89 |
90 | var removeWhere = function(arr, func) {
91 | var i = 0;
92 | while (i < arr.length) {
93 | if (func.call(null, arr[i], i) === true) {
94 | arr.splice(i, 1);
95 | }
96 | else {
97 | i++;
98 | }
99 | }
100 | };
101 |
102 | var combinePaths = function(relPath, refPath) {
103 | var relPathParts = relPath.split('/');
104 | refPath = refPath || '';
105 | if (refPath.length && refPath.charAt(refPath.length-1) != '/') {
106 | refPath += '/';
107 | }
108 | var refPathParts = refPath.split('/');
109 | refPathParts.pop();
110 | var part;
111 | while (part = relPathParts.shift()) {
112 | if (part == '.') { continue; }
113 | else if (part == '..'
114 | && refPathParts.length
115 | && refPathParts[refPathParts.length-1] != '..') { refPathParts.pop(); }
116 | else { refPathParts.push(part); }
117 | }
118 | return refPathParts.join('/');
119 | };
120 |
121 | // Takes a relative path to a module and resolves it according to the reference path
122 | var resolveModuleId = Yabble.unit.resolveModuleId = function(relModuleId, refPath) {
123 | if (relModuleId.charAt(0) != '.') {
124 | return relModuleId;
125 | }
126 | else {
127 | return combinePaths(relModuleId, refPath);
128 | }
129 | };
130 |
131 | // Takes a module's ID and resolves a URI according to the module root path
132 | var resolveModuleUri = function(moduleId) {
133 | if (moduleId.charAt(0) != '.') {
134 | return _moduleRoot+moduleId+'.js';
135 | }
136 | else {
137 | return this._resolveModuleId(moduleId, _moduleRoot)+'.js';
138 | }
139 | };
140 |
141 | // Returns a module object from the module ID
142 | var getModule = function(moduleId) {
143 | if (!hasOwnProperty.call(_modules, moduleId)) {
144 | return null;
145 | }
146 | return _modules[moduleId];
147 | };
148 |
149 | // Adds a callback which is executed when all deep dependencies are loaded
150 | var addCallback = function(deps, cb) {
151 | _callbacks.push([deps.slice(0), cb]);
152 | };
153 |
154 | // Generic implementation of require.ensure() which takes a reference path to
155 | // use when resolving relative module IDs
156 | var ensureImpl = function(deps, cb, refPath) {
157 | var unreadyModules = [];
158 |
159 | for (var i = deps.length; i--;) {
160 | var moduleId = resolveModuleId(deps[i], refPath),
161 | module = getModule(moduleId);
162 |
163 | if (!areDeepDepsDefined(moduleId)) {
164 | unreadyModules.push(moduleId);
165 | }
166 | }
167 |
168 | if (unreadyModules.length) {
169 | addCallback(unreadyModules, function() {
170 | cb(createRequireFunc(refPath));
171 | });
172 | queueModules(unreadyModules);
173 | }
174 | else {
175 | setTimeout(function() {
176 | cb(createRequireFunc(refPath));
177 | }, 0);
178 | }
179 | };
180 |
181 | // Creates a require function that is passed into module factory functions
182 | // and require.ensure() callbacks. It is bound to a reference path for
183 | // relative require()s
184 | var createRequireFunc = function(refPath) {
185 | var require = function(relModuleId) {
186 | var moduleId = resolveModuleId(relModuleId, refPath),
187 | module = getModule(moduleId);
188 |
189 | if (!module) {
190 | throw "Module not loaded";
191 | }
192 | else if (module.error) {
193 | throw "Error loading module";
194 | }
195 |
196 | if (!module.exports) {
197 | module.exports = {};
198 | var moduleDir = moduleId.substring(0, moduleId.lastIndexOf('/')+1),
199 | injects = module.injects,
200 | args = [];
201 |
202 | for (var i = 0, n = injects.length; i= 0;
456 | });
457 |
458 | transport.modules.push({
459 | id: arguments[0],
460 | factory: arguments[2],
461 | injects: arguments[1]
462 | });
463 | }
464 | return transport;
465 | };
466 |
467 | // Set the uri which forms the conceptual module namespace root
468 | Yabble.setModuleRoot = function(path) {
469 | if (!(/^http(s?):\/\//.test(path))) {
470 | var href = window.location.href;
471 | href = href.substr(0, href.lastIndexOf('/')+1);
472 | path = combinePaths(path, href);
473 | }
474 |
475 | if (path.length && path.charAt(path.length-1) != '/') {
476 | path += '/';
477 | }
478 |
479 | _moduleRoot = path;
480 | };
481 |
482 | // Set a timeout period for async module loading
483 | Yabble.setTimeoutLength = function(milliseconds) {
484 | _timeoutLength = milliseconds;
485 | };
486 |
487 | // Use script tags with wrapped code instead of XHR+eval()
488 | Yabble.useScriptTags = function() {
489 | _fetchFunc = loadModuleByScript;
490 | };
491 |
492 | // Define a module per various transport specifications
493 | Yabble.def = Yabble.define = function() {
494 | var transport = normalizeTransport.apply(null, arguments);
495 |
496 | var unreadyModules = [],
497 | definedModules = [];
498 |
499 | var deps = transport.deps;
500 |
501 | for (var i = transport.modules.length; i--;) {
502 | var moduleDef = transport.modules[i],
503 | moduleId = moduleDef.id,
504 | module = getModule(moduleId);
505 |
506 | if (!module) {
507 | module = _modules[moduleId] = {};
508 | }
509 | module.module = {
510 | id: moduleId,
511 | uri: resolveModuleUri(moduleId)
512 | };
513 |
514 | module.defined = true;
515 | module.deps = deps.slice(0);
516 | module.injects = moduleDef.injects;
517 | module.factory = moduleDef.factory;
518 | definedModules.push(module);
519 | }
520 |
521 | for (var i = deps.length; i--;) {
522 | var moduleId = deps[i],
523 | module = getModule(moduleId);
524 |
525 | if (!module || !areDeepDepsDefined(moduleId)) {
526 | unreadyModules.push(moduleId);
527 | }
528 | }
529 |
530 | if (unreadyModules.length) {
531 | setTimeout(function() {
532 | queueModules(unreadyModules);
533 | }, 0);
534 | }
535 |
536 | fireCallbacks();
537 | };
538 |
539 | Yabble.isKnown = function(moduleId) {
540 | return getModule(moduleId) != null;
541 | };
542 |
543 | Yabble.isDefined = function(moduleId) {
544 | var module = getModule(moduleId);
545 | return !!(module && module.defined);
546 | };
547 |
548 | // Do an async lazy-load of modules
549 | Yabble.ensure = function(deps, cb) {
550 | ensureImpl(deps, cb, '');
551 | };
552 |
553 | // Start an application via a main program module
554 | Yabble.run = function(program, cb) {
555 | program = _mainProgram = resolveModuleId(program, '');
556 | Yabble.ensure([program], function(require) {
557 | require(program);
558 | if (cb != null) { cb(); }
559 | });
560 | };
561 |
562 | // Reset internal state. Used mostly for unit tests.
563 | Yabble.reset = function() {
564 | _mainProgram = null;
565 | _modules = {};
566 | _callbacks = [];
567 |
568 | // Built-in system module
569 | Yabble.define({
570 | 'system': function(require, exports, module) {}
571 | });
572 | };
573 |
574 | Yabble.reset();
575 |
576 | // Export to the require global
577 | window.require = Yabble;
578 | })(function(code) {
579 | with (window) {
580 | return eval(code);
581 | };
582 | });
--------------------------------------------------------------------------------
/js/lib/block.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a block that the player may hit
3 | *
4 | * @author David North
5 | */
6 | function block()
7 | {
8 | //Load the variables required by gamejs.sprite.Sprite
9 | block.superConstructor.apply(this, [0, 0]);
10 | this.image = gamejs.image.load('img/blank.png');
11 |
12 | var _size = this.image.getSize();
13 | this.rect = new gamejs.Rect([0, 0], [800, 20]);
14 |
15 | /**
16 | * Sets the position of the object
17 | *
18 | * @param float x The X co-ordinate
19 | * @param float y The Y co-ordinate
20 | *
21 | * @return block
22 | */
23 | this.setPosition = function(x, y){
24 | if ( typeof(x) !== 'number' )
25 | {
26 | throw 'X must be a number';
27 | }
28 |
29 | if ( typeof(y) !== 'number' )
30 | {
31 | throw 'Y must be a number';
32 | }
33 |
34 | this.rect.x = x;
35 | this.rect.y = y;
36 |
37 | return this;
38 | }
39 |
40 | /**
41 | * Returns the position of the object
42 | *
43 | * @return object Contains an x and y property
44 | */
45 | this.getPosition = function(){
46 | return {"x": this.rect.x, "y": this.rect.y};
47 | }
48 |
49 | /**
50 | * Handles the collision between a playable and this object
51 | *
52 | * @return block
53 | */
54 | this.handleCollision = function( playable ){
55 | playerCollides(playable, this.rect);
56 |
57 | return this;
58 | }
59 | }
60 |
61 | //Extend the playable object so that the parent is the sprite
62 | gamejs.utils.objects.extend(block, gamejs.sprite.Sprite);
--------------------------------------------------------------------------------
/js/lib/camera.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents the a camera, which is capable of panning around the world
3 | *
4 | * @author David North
5 | */
6 | function camera( world )
7 | {
8 | //The maximum speed that the camera can move at
9 | const MAX_VELOCITY = 300;
10 |
11 | /**
12 | * @var world The world that is being looked at
13 | */
14 | var _world = world;
15 |
16 | /**
17 | * @var boolean|gamejs.Rect Whether or not the camera is currently
18 | * tracking an object. This variable contains the object being tracked if so.
19 | */
20 | var _track = false;
21 |
22 | /**
23 | * @var boolean Whether or not the camera is animating to position
24 | */
25 | var _animating = false;
26 |
27 | /**
28 | * @var gamejs.Rect The size and position of the viewport
29 | * (what the player can see)
30 | */
31 | var _viewport = new gamejs.Rect([0, 0], [0, 0]);
32 |
33 | /**
34 | * Sets the width of the viewport
35 | *
36 | * @param float width
37 | *
38 | * @return camera
39 | */
40 | this.setWidth = function( width ){
41 | if ( typeof(width) != 'number' )
42 | {
43 | throw 'Width must be a number';
44 | }
45 |
46 | _viewport.width = width;
47 | return this;
48 | }
49 |
50 | /**
51 | * Sets the height of the viewport
52 | *
53 | * @param float height
54 | *
55 | * @return camera
56 | */
57 | this.setHeight = function( height ){
58 | if ( typeof(height) != 'number' )
59 | {
60 | throw 'Height must be a number';
61 | }
62 |
63 | _viewport.height = height;
64 | return this;
65 | }
66 |
67 | /**
68 | * Sets the new position of the camera. Sanatises the position so that it
69 | * never looks outside of the world
70 | *
71 | * @param float x
72 | * @param float y
73 | *
74 | * @return camera
75 | */
76 | this.setPosition = function( x, y ){
77 | if ( typeof(x) != 'number' )
78 | {
79 | throw 'X position must be a number';
80 | }
81 |
82 | if ( typeof(y) != 'number' )
83 | {
84 | throw 'Y position must be a number';
85 | }
86 |
87 | //Get the last position that the camera was at before being moved
88 | var oldX = _viewport.x;
89 | var oldY = _viewport.y;
90 |
91 | //Sanitise the position and set the camera
92 | var newPosition = _getSanatisedPosition(x, y);
93 | _viewport.x = newPosition.x;
94 | _viewport.y = newPosition.y;
95 |
96 | //Update the objects in the world (i.e. shift them, the number of pixels
97 | //the camera has 'moved', giving the impression of movement)
98 | _updateObjects((_viewport.x - oldX), (_viewport.y - oldY));
99 |
100 | return this;
101 | }
102 |
103 | /**
104 | * Whether or not the camera is currently animating towards an object
105 | *
106 | * @return boolean
107 | */
108 | this.isAnimating = function(){
109 | return _animating;
110 | }
111 |
112 | /**
113 | * Focuses the camera on a rectangle, with the option to track it
114 | * continuously and to animate the movement
115 | *
116 | * @param gamejs.Rect rect The rectangle to focus on
117 | * @param boolean track Optional. Whether to track the object continuously
118 | * @param boolean animate Optional. Whether to animate the camera or not
119 | *
120 | * @return camera
121 | */
122 | this.focusOn = function( rect, track, animate ){
123 | //Mmmmm.. type hinting
124 | if ( !(rect instanceof gamejs.Rect) )
125 | {
126 | throw 'Rectangle must be an instance of gamejs.Rect';
127 | }
128 |
129 | //If track is not defined, then set the default value to false.
130 | //Otherwise, ensure that it's a boolean
131 | if (typeof(track) === "undefined")
132 | {
133 | track = false;
134 | }
135 | else if ( typeof(track) !== 'boolean' )
136 | {
137 | throw 'Optional track flag must be a boolean';
138 | }
139 |
140 | //If animate is not defined, then set the default value to false.
141 | //Otherwise, ensure that it's a boolean
142 | if (typeof(animate) === "undefined")
143 | {
144 | animate = false;
145 | }
146 | else if ( typeof(animate) !== 'boolean' )
147 | {
148 | throw 'Optional animate flag must be a boolean';
149 | }
150 |
151 | //The new Camera position should have the middle of the camera pointing
152 | //at the middle of the rectangle
153 | var newCameraX = rect.center[0] - (_viewport.width / 2);
154 | var newCameraY = rect.center[1] - (_viewport.height / 2);
155 |
156 | //If we are contantly tracking this object, then set that here
157 | if ( track )
158 | {
159 | _track = rect;
160 | }
161 | else
162 | {
163 | _track = false;
164 | }
165 |
166 | //If we are animating, don't move the camera (let the update method
167 | //do it), otherwise set the position now
168 | if ( animate )
169 | {
170 | _animating = true;
171 | }
172 | else
173 | {
174 | this.setPosition( newCameraX, newCameraY );
175 | }
176 |
177 | return this;
178 | }
179 |
180 | /**
181 | * Updates the camera position if it is tracking and also moves the camera
182 | * animation if animating
183 | *
184 | * @param int msDuration
185 | */
186 | this.update = function( msDuration ){
187 | if ( _track )
188 | {
189 | //Get the new X and Y c-ordinates, so that the camera is focused
190 | //on the middle of the object
191 | var destinationX = _track.center[0] - (_viewport.width / 2);
192 | var destinationY = _track.center[1] - (_viewport.height / 2);
193 |
194 | destinationX += _viewport.x
195 | destinationY += _viewport.y
196 |
197 | //If the tracking is animated then get the next frame, before
198 | //setting the new position to that instead
199 | if ( _animating )
200 | {
201 | var pos = _getNextAnimatedPosition(
202 | destinationX, destinationY, msDuration
203 | );
204 |
205 | destinationX = pos.x;
206 | destinationY = pos.y;
207 | }
208 |
209 | this.setPosition( destinationX, destinationY );
210 | }
211 | }
212 |
213 | /**
214 | * Ensures that the camera is not intersecting the level (i.e. going over
215 | * the bounding box). This is so that the camera is always focused on
216 | * objects inside the level, not outside it
217 | *
218 | * @param float x The proposed X position
219 | * @param float y The proposed Y position
220 | *
221 | * @return object An object containing the x and y position
222 | */
223 | var _getSanatisedPosition = function( x, y ){
224 | var position = { 'x': x, 'y': y };
225 | var level = _world.getBoundingRect();
226 |
227 | //Set up the collision test object (essentially a copy of the cameras
228 | //viewport, with the new x and y co-ordinates)
229 | var collideTest = new gamejs.Rect(
230 | [x, y], [_viewport.width, _viewport.height]
231 | );
232 |
233 | //Set up the edges of trhe level to test collisions on
234 | var rightEdge = [
235 | [level.right, level.top],
236 | [level.right, level.bottom]
237 | ];
238 |
239 | var leftEdge = [
240 | [level.left, level.top],
241 | [level.left, level.bottom]
242 | ];
243 |
244 | var topEdge = [
245 | [level.left, level.top],
246 | [level.right, level.top]
247 | ];
248 |
249 | var bottomEdge = [
250 | [level.left, level.bottom],
251 | [level.right, level.bottom]
252 | ];
253 |
254 | //Test the left and right edges, setting as appropriate
255 | if ( collideTest.collideLine(rightEdge[0], rightEdge[1]) )
256 | {
257 | position['x'] = level.right - collideTest.width;
258 | }
259 | else if ( collideTest.collideLine(leftEdge[0], leftEdge[1]) )
260 | {
261 | position['x'] = level.left;
262 | }
263 |
264 | //Test the top and bottom edges, setting as appropriate
265 | if ( collideTest.collideLine( topEdge[0], topEdge[1]) )
266 | {
267 | position['y'] = level.top;
268 | }
269 | else if ( collideTest.collideLine(bottomEdge[0], bottomEdge[1]) )
270 | {
271 | position['y'] = (level.bottom - collideTest.height);
272 | }
273 |
274 | return position;
275 | }
276 |
277 | /**
278 | * Gets the next frame for the camera animation
279 | *
280 | * @param float destinationX The target destination X position
281 | * @param float destinationY Tha target destination Y position
282 | * @param int msDuration The amount of time that has passed since the
283 | * last frame
284 | *
285 | * @return object An object containing the new X and Y
286 | */
287 | var _getNextAnimatedPosition = function(
288 | destinationX, destinationY, msDuration
289 | ){
290 | //Make sure that the new destination is not outside the world
291 | var sanePosition = _getSanatisedPosition(destinationX, destinationY);
292 |
293 | var position = { 'x': sanePosition.x, 'y': sanePosition.y };
294 |
295 | var targetX = position.x;
296 | var targetY = position.y;
297 | var deltaX = _viewport.x - targetX;
298 | var deltaY = _viewport.y - targetY;
299 | var velocityX = velocityY = MAX_VELOCITY;
300 |
301 | //If the delta Y is zero, then the velocity is zero as the camera is
302 | //not moving anywhere along the Y axis
303 | if ( 0 === deltaY )
304 | {
305 | velocityY = 0;
306 | }
307 |
308 | //If the delta X is zero, then the velocity is zero as the camera is
309 | //not moving anywhere along the X axis
310 | if ( 0 === deltaX )
311 | {
312 | velocityX = 0;
313 | }
314 |
315 | //Find out if the difference on the X or Y axis is bigger and slow
316 | //down the smaller of the two. This gives a nice diagonal effect so
317 | //that the camera doesn't look like it's panning around trying to find
318 | //the object
319 | if ( Math.abs(deltaX) > Math.abs(deltaY) )
320 | {
321 | if ( 0 != deltaY )
322 | {
323 | velocityY *= (deltaX / deltaY);
324 | }
325 | }
326 | else if ( Math.abs(deltaX) < Math.abs(deltaY) )
327 | {
328 | if ( 0 != deltaX )
329 | {
330 | velocityX *= (deltaX / deltaY);
331 | }
332 | }
333 |
334 | //A small delta X means that the camera needs to move to the right, and
335 | //a large delta means to move it to the left
336 | if ( deltaX < 0 )
337 | {
338 | position.x = _viewport.x + ( velocityX * (msDuration / 1000) );
339 | }
340 | else
341 | {
342 | position.x = _viewport.x - ( velocityX * (msDuration / 1000) );
343 | }
344 |
345 | //A small delta X means that the camera needs to move down, and
346 | //a large delta means to move it up
347 | if ( deltaY < 0 )
348 | {
349 | position.y = _viewport.y + ( velocityY * (msDuration / 1000) );
350 | }
351 | else if ( deltaY < 0 )
352 | {
353 | position.y = _viewport.y - ( velocityY * (msDuration / 1000) );
354 | }
355 |
356 |
357 | //Check to see if the camera is close to the target. If it is, then move
358 | //it to the target so that on the next frame it doesn't overshoot
359 | if ( position.x <= (targetX + 5) && position.x >= (targetX - 5) )
360 | {
361 | position.x = targetX;
362 | }
363 |
364 | if ( position.y <= (targetY + 5) && position.y >= (targetY - 5) )
365 | {
366 | position.y = targetY;
367 | }
368 |
369 | //If the camera has reached it's target, then stop it animating
370 | if ( targetX == position.x && targetY == position.y )
371 | {
372 | _animating = false;
373 | }
374 |
375 | return position;
376 | }
377 |
378 | /**
379 | * Updates all objects with their new position. This gives the illusion that
380 | * the camera has moved, when in reality it's all the objects that
381 | * have moved
382 | *
383 | * @param float distanceX The distance the camera has travelled on the X
384 | * @param float distanceY The distance the camera has travelled on the Y
385 | */
386 | var _updateObjects = function( distanceX, distanceY ){
387 | var objects = _world.getObjects();
388 | for ( var i = 0; i < objects.length; i++)
389 | {
390 | objects[i].forEach(function(obj){
391 | obj.rect.x -= distanceX;
392 | obj.rect.y -= distanceY;
393 | });
394 | }
395 | }
396 | }
--------------------------------------------------------------------------------
/js/lib/door.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a door which can be passed only when the state is set to true
3 | *
4 | * @author David North
5 | */
6 | function door()
7 | {
8 | door.prototype.constructor.call(this);
9 |
10 | //Set up the variables required by the sprite inheritance
11 | this.image = gamejs.image.load('img/door.png');
12 | this.image.crop( new gamejs.Rect( [0,0], [83,466] ));
13 |
14 | var _size = this.image.getSize();
15 | this.rect = new gamejs.Rect([0, 0], [_size[0], _size[1]]);
16 |
17 | /**
18 | * Overrides the setState method of the parent so that the object changes
19 | * depending on whether it is on or off
20 | *
21 | * @param boolean state The state to apply
22 | *
23 | * @return door
24 | */
25 | this.setState = function( state ){
26 | //Only update if the state has actually changed
27 | if ( state != this.getState() )
28 | {
29 | if ( state )
30 | {
31 | this.image.crop( new gamejs.Rect( [83,0], [83,466] ));
32 | }
33 | else
34 | {
35 | this.image.crop( new gamejs.Rect( [0,0], [83,466] ));
36 | }
37 | }
38 |
39 | //Update the state using the parent setState method
40 | return door.prototype.setState.call(this, state);
41 | }
42 |
43 | /**
44 | * Handles the collision between a playable and this object
45 | *
46 | * @return door
47 | */
48 | this.handleCollision = function( playable ){
49 |
50 | if ( !this.getState() )
51 | {
52 | //Modify the rectangle. The player shouldn't hit the door
53 | //until they are at the beam
54 | var targetX = this.rect.x + 26;
55 | var targetY = this.rect.y;
56 |
57 | var targetWidth = this.rect.width - 52;
58 | var targetHeight = this.rect.height;
59 |
60 | var rect = new gamejs.Rect(
61 | [targetX, targetY], [targetWidth, targetHeight]
62 | );
63 |
64 | playerCollides(playable, rect);
65 | }
66 |
67 | return this;
68 | }
69 | }
70 |
71 | //Set the parent of the door to io
72 | include_once(['lib/io.js']);
73 | door.prototype = new io();
74 |
--------------------------------------------------------------------------------
/js/lib/gates/andGate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simulates an AND gate in JavaScript. As an io object this can be used to
3 | * chain together logic operators and objects
4 | *
5 | * @author David North
6 | */
7 | function andGate()
8 | {
9 | andGate.prototype.constructor.call(this);
10 |
11 | /**
12 | * Overrides the setState method of the parent so that the state may only
13 | * be changed to true if all inputs are also set to true
14 | *
15 | * @param boolean state The state to attempt to change to
16 | *
17 | * @return andGate
18 | */
19 | this.setState = function( state ){
20 | state = true;
21 |
22 | //Keep the state at true unless a false value is found
23 | for( var i = 0; i < this.getInputs().length; i++ )
24 | {
25 | if ( !(this.getInputs()[i].getState()) )
26 | {
27 | state = false;
28 | break;
29 | }
30 | }
31 |
32 | //Update the state using the parent setState method
33 | return andGate.prototype.setState.call(this, state);
34 | }
35 | }
36 |
37 | //Set the parent of the andGate to io
38 | include_once(['lib/io.js']);
39 | andGate.prototype = new io();
40 |
--------------------------------------------------------------------------------
/js/lib/gates/notGate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simulates a NOT gate in JavaScript. As an io object this can be used to
3 | * chain together logic operators and objects
4 | *
5 | * @author David North
6 | */
7 | function notGate()
8 | {
9 | notGate.prototype.constructor.call(this);
10 |
11 | /**
12 | * Overrides the addInput method of the parent so that only a single
13 | * input can be added to this object. This is because the NOT gate can only
14 | * operate by setting itself to a modified state of it's single input
15 | *
16 | * @param io input The input to add
17 | *
18 | * @return io
19 | */
20 | this.addInput = function( input ){
21 | notGate.prototype.addInput.call(this, input);
22 |
23 | if ( this.getInputs().length > 1 )
24 | {
25 | throw 'You may only have one input assigned to a Not gate';
26 | }
27 |
28 | return this;
29 | }
30 |
31 | /**
32 | * Overrides the setState method of the parent so that the state that this
33 | * object is set to is the opposite to that provided
34 | *
35 | * @param boolean state The state to apply
36 | *
37 | * @return notGate
38 | */
39 | this.setState = function( state ){
40 | return notGate.prototype.setState.call(this, !state);
41 | }
42 | }
43 |
44 | //Set the parent of the notGate to io
45 | include_once(['lib/io.js']);
46 | notGate.prototype = new io();
47 |
--------------------------------------------------------------------------------
/js/lib/gates/orGate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simulates an OR gate in JavaScript. As an io object this can be used to
3 | * chain together logic operators and objects
4 | *
5 | * @author David North
6 | */
7 | function orGate()
8 | {
9 | orGate.prototype.constructor.call(this);
10 |
11 | /**
12 | * Overrides the setState method of the parent so that the state may only
13 | * be changed to false if all inputs are also set to false. If even a single
14 | * input is true then the state is true
15 | *
16 | * @param boolean state The state to attempt to change to
17 | *
18 | * @return orGate
19 | */
20 | this.setState = function( state ){
21 | state = false;
22 |
23 | //Keep the state at false unless a true value is found
24 | for( var i = 0; i < this.getInputs().length; i++ )
25 | {
26 | if ( this.getInputs()[i].getState() )
27 | {
28 | state = true;
29 | break;
30 | }
31 | }
32 |
33 | //Update the state using the parent setState method
34 | return orGate.prototype.setState.call(this, state);
35 | }
36 | }
37 |
38 | //Set the parent of the orGate to io
39 | include_once(['lib/io.js']);
40 | orGate.prototype = new io();
41 |
--------------------------------------------------------------------------------
/js/lib/goal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The end goal of the game. All of the goals present in the game need to be
3 | * active before the game is complete
4 | *
5 | * @author David North
6 | */
7 | function goal()
8 | {
9 | //Load the variables required by gamejs.sprite.Sprite
10 | goal.superConstructor.apply(this, [0, 0]);
11 | this.image = gamejs.image.load('img/goal.png');
12 |
13 | var _size = this.image.getSize();
14 | this.rect = new gamejs.Rect([0, 0], [_size[0], _size[1]]);
15 |
16 | /**
17 | * @var boolean Whether the goal has been activated
18 | */
19 | var _active = false;
20 |
21 | /**
22 | * Sets the position of the object
23 | *
24 | * @param float x The X co-ordinate
25 | * @param float y The Y co-ordinate
26 | *
27 | * @return goal
28 | */
29 | this.setPosition = function(x, y){
30 | this.rect.x = x;
31 | this.rect.y = y;
32 |
33 | return this;
34 | }
35 |
36 | /**
37 | * Returns whether or not the goal has been activated
38 | *
39 | * @return boolean
40 | */
41 | this.isActive = function(){
42 | return _active;
43 | }
44 |
45 | /**
46 | * Updates the object, ready for the next draw request
47 | *
48 | * @param msDuration
49 | *
50 | * @return goal
51 | */
52 | this.update = function(msDuration){
53 | //The default state for thisobject is deactivated, unless a
54 | //player has collided with it
55 | _active = false;
56 |
57 | return this;
58 | }
59 |
60 | /**
61 | * Handles the collision between a playable and this object
62 | *
63 | * @return goal
64 | */
65 | this.handleCollision = function( playable ){
66 | //Modify the rectangle. The player shouldn't end the level until
67 | //they are fully within the tube
68 | var targetX = this.rect.x + (this.rect.width / 2);
69 | targetX += (playable.rect.width / 2);
70 |
71 | var targetY = this.rect.y;
72 | var rect = new gamejs.Rect([targetX, targetY], [40, 144]);
73 |
74 | //Check if the player has collided with this goal, and whether it
75 | //should be activated
76 | _active = playable.rect.collideRect(rect);
77 |
78 | return this;
79 | }
80 | }
81 |
82 | //Extend the playable object so that the parent is the sprite
83 | gamejs.utils.objects.extend(goal, gamejs.sprite.Sprite);
--------------------------------------------------------------------------------
/js/lib/io.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Basic input/output class. Listens for state changes on the inputs and
3 | * notifies outputs if a change happens
4 | *
5 | * @author David North
6 | */
7 | function io()
8 | {
9 | /**
10 | * @var boolean
11 | */
12 | this._state = false;
13 |
14 | /**
15 | * @var array An array of inputs, that determine the state of this object
16 | */
17 | this._inputs = [];
18 |
19 | /**
20 | * @var array An array of outputs, to send the state of this object to
21 | */
22 | this._outputs = [];
23 |
24 | //Load the variables required by gamejs.sprite.Sprite
25 | io.superConstructor.apply(this, [0, 0]);
26 | this.image = gamejs.image.load('img/blank.png');
27 | this.rect = new gamejs.Rect([0,0]);
28 |
29 | /**
30 | * Sets the position of the object
31 | *
32 | * @param float x The X co-ordinate
33 | * @param float y The Y co-ordinate
34 | *
35 | * @return io
36 | */
37 | this.setPosition = function(x, y){
38 | this.rect.x = x;
39 | this.rect.y = y;
40 | return this;
41 | }
42 |
43 | /**
44 | * Private method fired when the state of the object has changed
45 | * (such as when a switch is pressed). Once the state has changed,
46 | * all other outputs need to be made aware of the change
47 | *
48 | * @return io
49 | */
50 | var _stateChange = function(obj){
51 | //Change the state of all outputs to the new state of this object,
52 | //effectively causing a knock-on affect down the chain
53 | for( var i = 0; i < obj.getOutputs().length; i++ )
54 | {
55 | obj.getOutputs()[i].setState( obj.getState() );
56 | }
57 |
58 | return this;
59 | };
60 |
61 | /**
62 | * Sets the state of this object
63 | *
64 | * @param boolean state The state to change to
65 | *
66 | * @return io
67 | */
68 | this.setState = function(state){
69 | if ( typeof(state) != 'boolean' )
70 | {
71 | throw 'State must be a boolean';
72 | }
73 |
74 | //Set the new state and set the state change event only if the state
75 | //has actually changed, otherwise we could waste time notifying objects
76 | ///that don't require notification
77 | if ( state != this.getState() )
78 | {
79 | this._state = state;
80 | _stateChange(this);
81 | }
82 |
83 | return this;
84 | };
85 |
86 | /**
87 | * Gets the state of the object
88 | *
89 | * @return boolean
90 | */
91 | this.getState = function(){
92 | return this._state;
93 | };
94 |
95 | /**
96 | * Gets the inputs assigned to this object
97 | *
98 | * @return array
99 | */
100 | this.getInputs = function(){
101 | return this._inputs;
102 | };
103 |
104 | /**
105 | * Gets the outputs assigned to this object
106 | *
107 | * @return array
108 | */
109 | this.getOutputs = function(){
110 | return this._outputs;
111 | };
112 |
113 | /**
114 | * Adds a new input to the object. Essentially this adds subscribes
115 | * this object to the stateChange event of the input
116 | *
117 | * @param io input
118 | *
119 | * @return io
120 | */
121 | this.addInput = function( input ){
122 | if ( !(input instanceof io) )
123 | {
124 | throw 'Input must be of type \'io\'';
125 | }
126 |
127 | //Ensure that the input has not already been added. We don't want to
128 | //attempt to add it again, that's just damned inefficient
129 | if ( $.inArray(input, this._inputs) == -1 )
130 | {
131 | this._inputs.push(input);
132 | input.addOutput(this);
133 | }
134 |
135 | return this;
136 | };
137 |
138 | /**
139 | * Adds an output to this object. Effectively this subscribes 'output'
140 | * to the stateChange event of this object
141 | *
142 | * @param io output
143 | *
144 | * @return io
145 | */
146 | this.addOutput = function( output ){
147 | if ( !(output instanceof io) )
148 | {
149 | throw 'Output must be of type \'io\'';
150 | }
151 |
152 | //Ensure that the output has not already been added. We don't want to
153 | //attempt to add it again, that would be silly
154 | if ( $.inArray(output, this._outputs) == -1 )
155 | {
156 | this._outputs.push(output);
157 | output.addInput(this);
158 | _stateChange(this);
159 | }
160 |
161 | return this;
162 | };
163 | }
164 |
165 | //Some IOs need to be drawn. To deal with this, extend the sprite
166 | gamejs.utils.objects.extend(io, gamejs.sprite.Sprite);
167 |
--------------------------------------------------------------------------------
/js/lib/levels/level_1.js:
--------------------------------------------------------------------------------
1 | [
2 | {"type":"stats","clonePar":1,"timePar":10},
3 | {"type":"wall","x":0,"y":680,"repeat-x":2},
4 | {"type":"player","x":150,"y": 550},
5 | {"type":"lever","x":350,"y": 641,"outputs":[
6 | {"type":"door", "x":700,"y":218}
7 | ]},
8 | {
9 | "type":"tooltip","x":150,"y":641,"width":50,"height":50,
10 | "text":"Press the left arrow or a to move left
Press the right arrow or d to move right"
11 | },
12 | {
13 | "type":"tooltip","x":350,"y":641,"width":50,"height":50,
14 | "text":"Press e or enter to activate switches
At least one clone must remain at a switch to keep it active"
15 | },
16 | {
17 | "type":"tooltip","x":650,"y":641,"width":50,"height":50,
18 | "text":"Press c to create a clone
Press tab to switch between them"
19 | },
20 | {"type":"goal","x":890,"y":540}
21 | ]
--------------------------------------------------------------------------------
/js/lib/levels/level_2.js:
--------------------------------------------------------------------------------
1 | [
2 | {"type":"stats","clonePar":2,"timePar":10},
3 | {"type":"wall","x":0,"y":680,"repeat-x":2},
4 | {"type":"player","x":150,"y": 550},
5 | {"type":"andGate","inputs":[
6 | {"type":"lever","x":250,"y": 641},
7 | {"type":"lever","x":450,"y": 641}
8 | ], "outputs": [
9 | {"type":"door", "x":700,"y":218}
10 | ]},
11 | {
12 | "type":"tooltip","x":250,"y":641,"width":100,"height":100,
13 | "text":"Some doors can only be opened when two or more
switches are held down at the same time"},
14 | {
15 | "type":"tooltip","x":650,"y":641,"width":50,"height":50,
16 | "text":"Press c to create a clone
Press tab to switch between them"},
17 | {"type":"goal","x":890,"y":540}
18 | ]
--------------------------------------------------------------------------------
/js/lib/levels/level_3.js:
--------------------------------------------------------------------------------
1 | [
2 | {"type":"stats","clonePar":1,"timePar":15},
3 | {"type":"wall","x":0,"y":680,"repeat-x":2},
4 | {"type":"player","x":150,"y": 550},
5 | {"type":"lever","x":350,"y": 641,"outputs":[
6 | {"type":"door", "x":500,"y":218},
7 | {"type":"notGate", "outputs":[
8 | {"type":"door", "x":700,"y":218}
9 | ]}
10 | ]},
11 | {
12 | "type":"tooltip","x":300,"y":641,"width":100,"height":100,
13 | "text":"Press e or enter to activate switches
Some switches will only activate a door when they are off"},
14 | {
15 | "type":"tooltip","x":550,"y":641,"width":50,"height":50,
16 | "text":"Press c to create a clone
Press tab to switch between them"},
17 | {"type":"goal","x":890,"y":540}
18 | ]
--------------------------------------------------------------------------------
/js/lib/levels/level_4.js:
--------------------------------------------------------------------------------
1 | [
2 | {"type":"stats","clonePar":2,"timePar":15},
3 | {"type":"wall","x":0,"y":680,"repeat-x":2},
4 | {"type":"player","x":150,"y": 550},
5 | {"type":"lever","x":350,"y": 641,"outputs":[
6 | {"type":"door", "x":500,"y":218},
7 | {"type":"notGate", "outputs":[
8 | {"type":"andGate", "outputs":[
9 | {"type":"door", "x":700,"y":218}
10 | ],"inputs":[
11 | {"type":"lever","x":600,"y": 641}
12 | ]}
13 | ]}
14 | ]},
15 | {
16 | "type":"tooltip","x":300,"y":641,"width":100,"height":100,
17 | "text":"The right combination of switches will release certain doors.
Experiment to find the correct combination"},
18 | {
19 | "type":"tooltip","x":550,"y":641,"width":50,"height":50,
20 | "text":"Press c to create a clone
Press tab to switch between them"},
21 | {"type":"goal","x":890,"y":540}
22 | ]
--------------------------------------------------------------------------------
/js/lib/levels/level_5.js:
--------------------------------------------------------------------------------
1 | [
2 | {"type":"stats","clonePar":1,"timePar":20,"lastLevel":true},
3 | {"type":"wall","x":0,"y":680,"repeat-x":2},
4 | {"type":"player","x":50,"y": 550},
5 | {"type":"platform", "x": 150, "y": 520,"width":250,"height":26},
6 | {"type":"orGate","x":0,"y":0,"outputs":[
7 | {"type":"door", "x":150,"y":68},
8 | {"type":"door", "x":315,"y":68}
9 | ],"inputs":[
10 | {"type":"lever","x":450,"y": 641},
11 | {"type":"lever","x":800,"y": 641,"outputs":[
12 | {"type":"door", "x":900,"y":218},
13 | {"type":"orGate","x":0,"y":0,"outputs":[
14 | {"type":"door", "x":600,"y":218}
15 | ],"inputs":[
16 | {"type":"lever","x":235,"y": 492}
17 | ]}
18 | ]}
19 | ]},
20 | {"type":"goal","x":1090,"y":540}
21 | ]
--------------------------------------------------------------------------------
/js/lib/lever.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a lever. Levers can be pulled and turned on or off, but must be
3 | * held down or they will revert to their off state
4 | *
5 | * @author David North
6 | */
7 | function lever()
8 | {
9 | lever.prototype.constructor.call(this);
10 |
11 | //Fulfil the requirements of the gamejs.sprite.Sprite object
12 | this.image = gamejs.image.load('img/switch.png');
13 | this.image.crop( new gamejs.Rect( [0,0], [83,43] ));
14 |
15 | var _size = this.image.getSize();
16 |
17 | /**
18 | * @var boolean Whether or the lever is being held down
19 | */
20 | var _heldDown = false;
21 |
22 | this.rect = new gamejs.Rect([0, 0], [_size[0], _size[1]]);
23 |
24 | /**
25 | * Overrides the setState method of the parent so that the object changes
26 | * depending on whether it is on or off
27 | *
28 | * @param boolean state The state to apply
29 | *
30 | * @return lever
31 | */
32 | this.setState = function( state ){
33 | if ( state != this.getState() )
34 | {
35 | if ( state )
36 | {
37 | this.image.crop( new gamejs.Rect( [83,0], [83,43] ));
38 | }
39 | else
40 | {
41 | this.image.crop( new gamejs.Rect( [0,0], [83,43] ));
42 | }
43 | }
44 |
45 | //Update the state using the parent setState method
46 | lever.prototype.setState.call(this, state);
47 | }
48 |
49 | /**
50 | * Updates the object, ready for the next draw request
51 | *
52 | * @param msDuration
53 | *
54 | * @return lever
55 | */
56 | this.update = function( msDuration ){
57 | //If the lever isn't held down then turn it off
58 | if ( !_heldDown )
59 | {
60 | this.setState(false);
61 | }
62 |
63 | //the default state is to be not held down, unless otherwise told
64 | _heldDown = false;
65 |
66 | return this;
67 | }
68 |
69 | /**
70 | * Handles the collision between a playable and this object
71 | *
72 | * @param playable playable The playable that collision has happened on
73 | *
74 | * @return lever
75 | */
76 | this.handleCollision = function( playable ){
77 |
78 | if ( _hasCollided(this, playable ) )
79 | {
80 | //If the player is colliding with this lever then it is held down
81 | _heldDown = true;
82 | }
83 |
84 | return this;
85 | }
86 |
87 | /**
88 | * Handles player input. If the player activates the switch then actions
89 | * may need to be taken
90 | *
91 | * @param world world The world this event came from
92 | * @param gamejs.Event event The event that fired
93 | */
94 | this.handleInput = function(world, event){
95 | var playable = world.getPlayer().getCurrentPlayable();
96 |
97 | //Only check the event if a key has been pushed and the player is
98 | //colliding with the lever
99 | if ( event.type === gamejs.event.KEY_DOWN
100 | && _hasCollided(this, playable) )
101 | {
102 | //If the key was 'enter' or 'e' then set the new state of the lever
103 | switch( event.key )
104 | {
105 | case gamejs.event.K_e:
106 | case gamejs.event.K_ENTER:
107 | this.setState( !this.getState() );
108 | break;
109 | }
110 | }
111 | }
112 |
113 | var _hasCollided = function ( self, playable )
114 | {
115 | var _collideRect = new gamejs.Rect(
116 | [self.rect.x + 34, self.rect.y],
117 | [32 , _size[1]]
118 | );
119 |
120 | return _collideRect.collideRect(playable.rect);
121 | }
122 | }
123 |
124 | //Set the parent of the lever to io
125 | include_once(['lib/io.js']);
126 | lever.prototype = new io();
127 |
--------------------------------------------------------------------------------
/js/lib/menu.js:
--------------------------------------------------------------------------------
1 |
2 | include_once(['lib/menuItem.js'])
3 | function menu()
4 | {
5 | var _items = [];
6 | var _currentActive = 0;
7 | var _x = 0;
8 | var _y = 0;
9 |
10 | this.setPosition = function(x, y)
11 | {
12 | if ( typeof(x) != 'number' )
13 | {
14 | throw 'X position must be a number';
15 | }
16 |
17 | if ( typeof(y) != 'number' )
18 | {
19 | throw 'Y position must be a number';
20 | }
21 |
22 | _x = x;
23 | _y = y;
24 | return this;
25 | }
26 |
27 | this.addItem = function( mItem ){
28 | if ( !(mItem instanceof menuItem) )
29 | {
30 | throw 'Argument must be of type menuItem';
31 | }
32 |
33 | if ( _items.length === 0 )
34 | {
35 | mItem.setActive(true);
36 | }
37 |
38 | _items.push(mItem);
39 | return this;
40 | }
41 |
42 | this.activate = function(index){
43 | for( var i = 0; i < _items; i++ )
44 | {
45 | if ( i != index )
46 | {
47 | _items[i].setActive(false);
48 | }
49 | else
50 | {
51 | _items[i].setActive(true);
52 | }
53 | }
54 | }
55 |
56 | this.activateNext = function(){
57 | _currentActive++;
58 |
59 | if ( _currentActive >= _items.length )
60 | {
61 | _currentActive = 0;
62 | }
63 |
64 | return this.activate(_currentActive);
65 | }
66 |
67 | this.draw = function( surface ){
68 | var dest = new gamejs.Rect([_x, _y]);
69 |
70 | for( var i = 0; i < _items.length; i++ )
71 | {
72 | surface.blit( _items[i].getCanvas(), dest );
73 | dest.y += 30;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/js/lib/menuItem.js:
--------------------------------------------------------------------------------
1 |
2 | function menuItem( image )
3 | {
4 | if ( typeof(image) != 'string' )
5 | {
6 | throw 'Constructor argument must be text';
7 | }
8 |
9 | var _image = gamejs.image.load(image);
10 | var _size = _image.getSize();
11 | var _active = false;
12 | var _callback = null;
13 |
14 | this.setActive = function( active ){
15 | if ( typeof(active) != 'boolean' )
16 | {
17 | throw 'Active flag must be boolean';
18 | }
19 |
20 | var rect = new gamejs.Rect([0,0],[parseInt(_size[0] / 2), _size[1]]);
21 |
22 | if ( active )
23 | {
24 | rect.x = parseInt(_size[0] / 2);
25 | }
26 |
27 | _image.crop(rect);
28 | _active = active;
29 |
30 | return this;
31 | }
32 |
33 | this.addCallback = function( callback ){
34 | if ( typeof(callback) !== 'function' )
35 | {
36 | throw 'Callback must be a function';
37 | }
38 |
39 | _callback = callback;
40 | return this;
41 | }
42 |
43 | this.setText = function( text ){
44 | if ( typeof(text) != 'string' )
45 | {
46 | throw 'Text argument must be text';
47 | }
48 |
49 | _text = text;
50 | return this;
51 | }
52 |
53 | this.getCanvas = function(){
54 | return _image;
55 | }
56 | }
--------------------------------------------------------------------------------
/js/lib/platform.js:
--------------------------------------------------------------------------------
1 | function platform()
2 | {
3 | //Load the variables required by gamejs.sprite.Sprite
4 | platform.superConstructor.apply(this, [0, 0]);
5 | this.image = new gamejs.Surface([0,0]);
6 |
7 | var _size = this.image.getSize();
8 | this.rect = new gamejs.Rect([0, 0]);
9 |
10 | var _leftImg = gamejs.image.load('img/platform-left.png');
11 | var _rightImg = gamejs.image.load('img/platform-right.png');
12 | var _midImg = gamejs.image.load('img/platform-middle.png');
13 |
14 | this.setDimensions = function(width, height){
15 | this.rect.width = width;
16 | this.rect.height = height;
17 | }
18 |
19 | this.setPosition = function(x, y){
20 | this.rect.x = x;
21 | this.rect.y = y;
22 | }
23 |
24 | this.draw = function( surface ){
25 | var rightSize = _rightImg.getSize();
26 | var leftSize = _leftImg.getSize();
27 |
28 | var rightSide = new gamejs.Rect(
29 | [(this.rect.right - rightSize[0]), this.rect.top],
30 | [rightSize[0], this.rect.height]
31 | );
32 |
33 | var leftSide = new gamejs.Rect(
34 | [this.rect.left, this.rect.top],
35 | [leftSize[0], this.rect.height]
36 | );
37 |
38 | var middle = new gamejs.Rect(
39 | [(this.rect.left + leftSize[0]) , this.rect.top],
40 | [(rightSide.left - leftSide.right), this.rect.height]
41 | );
42 |
43 | surface.blit(_midImg, middle);
44 | surface.blit(_leftImg, leftSide);
45 | surface.blit(_rightImg, rightSide);
46 | }
47 |
48 | this.handleCollision = function(playable){
49 | var collider = new gamejs.Rect(
50 | [this.rect.x, this.rect.y + (this.rect.height / 2) ],
51 | [this.rect.width, this.rect.height / 2]
52 | );
53 |
54 | playerCollides(playable, collider);
55 |
56 | return this;
57 | }
58 | }
59 |
60 | //Extend the playable object so that the parent is the sprite
61 | gamejs.utils.objects.extend(platform, gamejs.sprite.Sprite);
--------------------------------------------------------------------------------
/js/lib/playable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a playable sprite. This is manipulated by the player object which
3 | * determines which playable should be modified
4 | *
5 | * @author David North
6 | */
7 | function playable()
8 | {
9 | //Load the variables required by gamejs.sprite.Sprite
10 | playable.superConstructor.apply(this, [0, 0]);
11 | this.image = gamejs.image.load('img/player.png');
12 |
13 | this.rect = new gamejs.Rect([0, 0]);
14 |
15 | /**
16 | * @var int The amount of health the playable has
17 | */
18 | var _health = 100;
19 |
20 | /**
21 | * @var float The velocity left to right that the playable is experiencing
22 | */
23 | var _velocityX = 0.0;
24 |
25 | /**
26 | * @var float The velocity bottom to top that this playable is experiencing
27 | */
28 | var _velocityY = 0.0;
29 |
30 | /**
31 | * @var string How the player is currently moving (walk, jump, fall etc.)
32 | */
33 | var _moveType = '';
34 |
35 | /**
36 | * Sets how the player is currently moving, and generates the correct
37 | * sprite image
38 | *
39 | * @param string type The type of movement
40 | *
41 | * @return playable
42 | */
43 | this.setMovement = function( type ){
44 | //Only change the type if it has actually changed. No need to do any
45 | //processing otherwise
46 | if ( type != _moveType )
47 | {
48 | //Get the old size so that we can modify the X and Y co-ordinates
49 | //accordingly if the sprite changes height or width
50 | var oldSize = this.image.getSize();
51 |
52 | //Set the correct sprite image depending on the type of movement
53 | switch(type)
54 | {
55 | case 'walk':
56 | this.image.crop( new gamejs.Rect([0,0], [46,55] ));
57 | break;
58 | case 'jump':
59 | this.image.crop( new gamejs.Rect([0,55], [46,69] ));
60 | break;
61 | case 'fall':
62 | this.image.crop( new gamejs.Rect([46,55], [46,69] ));
63 | break;
64 | }
65 |
66 | //Get and set the new sizes
67 | var _size = this.image.getSize();
68 | this.rect.width = _size[0];
69 | this.rect.height = _size[1];
70 |
71 | //Set the new X and Y co-ordinates
72 | this.rect.x += oldSize[0] - _size[0];
73 | this.rect.y += oldSize[1] - _size[1];
74 |
75 | _moveType = type;
76 | }
77 |
78 | return this;
79 | }
80 |
81 | /**
82 | * Returns the current movement type of the playable
83 | *
84 | * @return string
85 | */
86 | this.getMovement = function(){
87 | return _moveType;
88 | }
89 |
90 | /**
91 | * Sets the position of the object
92 | *
93 | * @param float x The X co-ordinate
94 | * @param float y The Y co-ordinate
95 | *
96 | * @return playable
97 | */
98 | this.setPosition = function(x, y){
99 | this.setX(x);
100 | this.setY(y);
101 |
102 | return this;
103 | }
104 |
105 | /**
106 | * Sets the X value of the playable (useful when initiating a new playable,
107 | * teleporting, etc)
108 | *
109 | * @param float x
110 | *
111 | * @return playable
112 | */
113 | this.setX = function( x ){
114 | if ( typeof(x) != 'number')
115 | {
116 | throw 'Value for X must be a number';
117 | }
118 |
119 | this.rect.x = x;
120 | return this;
121 | }
122 |
123 | /**
124 | * Gets the current X position
125 | *
126 | * @return float
127 | */
128 | this.getX = function(){
129 | return this.rect.x;
130 | }
131 |
132 | /**
133 | * Sets the Y value of the playable (useful when initiating a new playable,
134 | * teleporting, etc)
135 | *
136 | * @param float y
137 | *
138 | * @return playable
139 | */
140 | this.setY = function( y ){
141 | if ( typeof(y) != 'number')
142 | {
143 | throw 'Value for Y must be a number';
144 | }
145 |
146 | this.rect.y = y;
147 | return this;
148 | }
149 |
150 | /**
151 | * Gets the current Y position
152 | *
153 | * @return float
154 | */
155 | this.getY = function(){
156 | return this.rect.y;
157 | }
158 |
159 | /**
160 | * Sets the velocity of the playable so that the speed in one way or
161 | * another can be set
162 | *
163 | * @param float x The velocity left to right
164 | * @param float y The velocity bottom to top
165 | *
166 | * @return playable
167 | */
168 | this.setVelocity = function( x, y ){
169 | if ( typeof(x) != 'number')
170 | {
171 | throw 'Value for X must be a number';
172 | }
173 |
174 | if ( typeof(y) != 'number')
175 | {
176 | throw 'Value for Y must be a number';
177 | }
178 |
179 | _velocityX = x;
180 | _velocityY = y;
181 | return this;
182 | }
183 |
184 | /**
185 | * Gets the X and Y velocity for this playable object by returning an object
186 | * with an x and y parameter
187 | *
188 | * @return object
189 | */
190 | this.getVelocity = function(){
191 | return { 'x':_velocityX, 'y':_velocityY };
192 | }
193 |
194 | /**
195 | * Updates the object, ready for the next draw request
196 | *
197 | * @param msDuration
198 | *
199 | * @return playable
200 | */
201 | this.update = function( msDuration ){
202 | //Set the default movement to wealking, if it hasn't already been set
203 | if ( this.getMovement() == '' )
204 | {
205 | this.setMovement('walk');
206 | }
207 |
208 | //If the player is falling then set that sprite up. Other movement
209 | //types are dealt with outside this object
210 | if ( this.getVelocity().y > 0 )
211 | {
212 | this.setMovement('fall');
213 | }
214 |
215 | //Calculate the distance the playable has moved since the last frame
216 | var distanceX = (this.getVelocity().x * (msDuration/1000));
217 | var distanceY = (this.getVelocity().y * (msDuration/1000));
218 |
219 | //Move the playable the calculated distance
220 | this.rect.moveIp( distanceX, distanceY );
221 |
222 | return this;
223 | }
224 | }
225 |
226 | //Extend the playable object so that the parent is the sprite
227 | gamejs.utils.objects.extend(playable, gamejs.sprite.Sprite);
228 |
--------------------------------------------------------------------------------
/js/lib/player.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a player. Players have control over multiple playables, but only
3 | * one playable at a time.
4 | *
5 | * @author David North
6 | */
7 | include_once(['lib/playable.js']);
8 | function player()
9 | {
10 | //The maximum X velocity a player can trvel (heading right)
11 | const MAX_X_VELOCITY = 200;
12 |
13 | //The minimum X velocity a player can trvel (heading left)
14 | const MIN_X_VELOCITY = -200;
15 |
16 | //The minimum Y velocity a player can trvel (heading up)
17 | const MIN_Y_VELOCITY = -350;
18 |
19 | /**
20 | * @var array An array of playables at the players disposal
21 | */
22 | var _playables = new gamejs.sprite.Group();
23 |
24 | /**
25 | * @var float How fast the player is currently walking (minus
26 | * figure represents left, positive right)
27 | */
28 | var _walkVelocity = 0;
29 |
30 | /**
31 | * @var int The number of clones created
32 | */
33 | var _numClones = 0;
34 |
35 | //Start the player with a single playable
36 | _playables.add( new playable() );
37 |
38 | /**
39 | * @var int The current index of the playable currently under the
40 | * players control
41 | */
42 | var _currentIndex = 0;
43 |
44 | var _selectedImg = gamejs.image.load('img/selected.png');
45 |
46 | /**
47 | * Handles player input.
48 | *
49 | * @param world world The world this event came from
50 | * @param gamejs.Event event The event that fired
51 | */
52 | this.handleInput = function(event){
53 | //If a key has been pressed then check it to see if an
54 | //action needs taking place
55 | if ( event.type === gamejs.event.KEY_DOWN )
56 | {
57 | switch( event.key )
58 | {
59 | //The space, w, or up key denotes a jump. The player is not allowed
60 | //to jump if they are already falling or jumping
61 | case gamejs.event.K_SPACE:
62 | case gamejs.event.K_w:
63 | case gamejs.event.K_UP:
64 | if ( this.getCurrentPlayable().getMovement() == 'walk' )
65 | {
66 | this.setVelocity( this.getVelocity().x, MIN_Y_VELOCITY );
67 | this.getCurrentPlayable().setMovement('jump');
68 | }
69 | break;
70 |
71 | //The A key, or left arrow starts to move the player left
72 | case gamejs.event.K_a:
73 | case gamejs.event.K_LEFT:
74 | _walkVelocity = MIN_X_VELOCITY;
75 | break;
76 |
77 | //The D key, or right arrow starts to move the player right
78 | case gamejs.event.K_d:
79 | case gamejs.event.K_RIGHT:
80 | _walkVelocity = MAX_X_VELOCITY;
81 | break;
82 |
83 | //The C key clones a playable, so that the player can use
84 | //that instead
85 | case gamejs.event.K_c:
86 | if ( _numClones < 100 )
87 | {
88 | this.clone();
89 | _numClones++;
90 | }
91 | break;
92 |
93 | //The Tab key switches between the playables that the
94 | //player can control
95 | case gamejs.event.K_TAB:
96 | _walkVelocity = 0;
97 | this.moveToNext();
98 | }
99 | }
100 | else if ( event.type === gamejs.event.KEY_UP )
101 | {
102 | switch( event.key )
103 | {
104 | //Letting go of direction events will stop the player dead
105 | case gamejs.event.K_a:
106 | case gamejs.event.K_LEFT:
107 | case gamejs.event.K_d:
108 | case gamejs.event.K_RIGHT:
109 | _walkVelocity = 0;
110 | break;
111 | }
112 | }
113 | }
114 |
115 | /**
116 | * Returns the number of clonmes that the player has created
117 | *
118 | * @return int
119 | */
120 | this.getNumClones = function(){
121 | return _numClones;
122 | }
123 |
124 | /**
125 | * Sets the velocity of the current playable
126 | *
127 | * @param float x The X velocity
128 | * @param float y The Y velocity
129 | *
130 | * @return player
131 | */
132 | this.setVelocity = function( x, y ){
133 | var playable = this.getCurrentPlayable();
134 | playable.setVelocity( x, y );
135 |
136 | return this;
137 | }
138 |
139 | /**
140 | * Returns an object taht contains the X and Y velocity of the current
141 | * playable
142 | *
143 | * @return object
144 | */
145 | this.getVelocity = function(){
146 | return this.getCurrentPlayable().getVelocity();
147 | }
148 |
149 | /**
150 | * Updates all of the playable objects
151 | *
152 | * @param int msDuration
153 | *
154 | * @return player
155 | */
156 | this.update = function( msDuration ){
157 | if ( _walkVelocity )
158 | {
159 | this.setVelocity( _walkVelocity, this.getVelocity().y );
160 | }
161 |
162 | _playables.update( msDuration );
163 | return this;
164 | }
165 |
166 | /**
167 | * Draws all of the playable objects to the surface
168 | *
169 | * @param object mainSurface
170 | *
171 | * @return player
172 | */
173 | this.draw = function( mainSurface ){
174 | _playables.draw( mainSurface );
175 |
176 | var x = this.getCurrentPlayable().getX();
177 | var y = this.getCurrentPlayable().getY();
178 |
179 | x += (this.getCurrentPlayable().rect.width / 2);
180 | x -= (_selectedImg.getSize()[0] / 2);
181 |
182 | y -= (_selectedImg.getSize()[1] + 2);
183 |
184 | var rect = new gamejs.Rect([x, y])
185 | mainSurface.blit(_selectedImg, rect);
186 | return this;
187 | }
188 |
189 | /**
190 | * Moves to the next available playable for control. If the last playable
191 | * is selected then the first element is sed instead
192 | *
193 | * @return player
194 | */
195 | this.moveToNext = function(){
196 | if ( (_currentIndex + 1) == this.getPlayables().sprites().length )
197 | {
198 | _currentIndex = 0;
199 | }
200 | else
201 | {
202 | _currentIndex++
203 | }
204 |
205 | return this;
206 | }
207 |
208 | /**
209 | * Gets all of the playables available to the player
210 | *
211 | * @return array
212 | */
213 | this.getPlayables = function(){
214 | return _playables;
215 | }
216 |
217 | /**
218 | * Gets the current playable object that the player is in control of
219 | *
220 | * @return playable
221 | */
222 | this.getCurrentPlayable = function(){
223 | return this.getPlayables().sprites()[_currentIndex];
224 | }
225 |
226 | /**
227 | * Clones the current playable and adds it to the playables at the players
228 | * disposal
229 | *
230 | * @return player
231 | */
232 | this.clone = function(){
233 | //Get the template for the clone, and create a clone to the left
234 | //of the current playable
235 | var template = this.getCurrentPlayable();
236 | var clone = new playable();
237 | var newX = ( template.getX() - (template.rect.width * 2) );
238 |
239 | clone.setPosition(newX, template.getY() - template.image.getSize()[1]);
240 |
241 | _playables.add( clone );
242 |
243 | return this;
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/js/lib/scorecard.js:
--------------------------------------------------------------------------------
1 |
2 | function scorecard()
3 | {
4 | var _clonePar = 0;
5 |
6 | var _timePar = 0;
7 |
8 | var _clones = 0;
9 |
10 | var _timeTaken = 0;
11 |
12 | var _lastLevel = false;
13 |
14 | if ( !($('#game_scorecard').length) )
15 | {
16 | $('#gameWindow').append('');
17 | $('#game_scorecard_bg').append('');
18 | $('#game_scorecard').append('Results
');
19 | $('#game_scorecard').append('Number of Clones0
');
20 | $('#game_scorecard').append('Time0s
');
21 | $('#game_scorecard').append('Score
');
22 | $('#game_scorecard').append('Main Menu');
23 | $('#game_scorecard').append('Reset Level');
24 | $('#game_scorecard').append('Next Level');
25 | }
26 |
27 | this.setParForClones = function( par ){
28 | _clonePar = par;
29 | return this;
30 | }
31 |
32 | this.setParForTime = function( par ){
33 | _timePar = par;
34 | return this;
35 | }
36 |
37 | this.setTimeTaken = function( time ){
38 | _timeTaken = time;
39 | return this;
40 | }
41 |
42 | this.setClonesUsed = function( clones ){
43 | _clones = clones;
44 | return this;
45 | }
46 |
47 | this.setLastLevel = function(last){
48 | _lastLevel = last;
49 | return this;
50 | }
51 |
52 | this.show = function(){
53 | $('#game_scorecard .clones').text(_clones);
54 | $('#game_scorecard .time').text(_calcTime());
55 |
56 | var score = 0;
57 |
58 | if ( _timeTaken <= (_timePar * 2) )
59 | {
60 | score++;
61 | }
62 |
63 | if ( _clones <= _clonePar )
64 | {
65 | score++;
66 | }
67 |
68 | if ( _timeTaken <= _timePar )
69 | {
70 | score++;
71 | }
72 |
73 | $('#game_scorecard .score .star').remove();
74 |
75 | for ( var i = 1; i <= 3; i++ )
76 | {
77 | var star = $('');
78 | if ( score >= i )
79 | {
80 | star.addClass('enabled');
81 | }
82 |
83 | $('#game_scorecard .score').append(star);
84 | }
85 |
86 | if ( _lastLevel )
87 | {
88 | $('.nextLevel').on('click', function( event ){
89 | $('#game_scorecard_bg').hide();
90 | $('#gameEnd').fadeIn();
91 |
92 | $('body').on('keydown',function(event2){
93 | switch ( event2.keyCode )
94 | {
95 | //Enter and e will all move to the next level
96 | case 13:
97 | case 69:
98 | event2.preventDefault();
99 | event2.stopImmediatePropagation();
100 |
101 | $('#game_scorecard .mainMenu').click();
102 | $('#gameEnd').fadeOut();
103 | $(this).off();
104 | }
105 |
106 | return false;
107 | });
108 |
109 | $(this).off();
110 | event.preventDefault();
111 | event.stopImmediatePropagation();
112 | });
113 | }
114 |
115 | $('#game_scorecard_bg').fadeIn();
116 | }
117 |
118 | this.hide = function(){
119 | $('#game_scorecard_bg').fadeOut();
120 | }
121 |
122 | var _calcTime = function(){
123 | var seconds = parseInt(_timeTaken);
124 | var minutes = 0;
125 | var hours = 0;
126 | var days = 0;
127 | var ret = '';
128 |
129 | if ( seconds > 60 )
130 | {
131 | minutes = Math.floor( seconds / 60 );
132 | seconds = (seconds % 60);
133 |
134 | if ( minutes >= 60 )
135 | {
136 | hours = Math.floor( minutes / 60 );
137 | minutes = (minutes % 60);
138 |
139 | if ( hours >= 24 )
140 | {
141 | days = Math.floor( hours / 24 );
142 | hours = (hours % 24);
143 | }
144 | }
145 | }
146 |
147 | if ( days )
148 | {
149 | ret = days+'d '+hours+'h';
150 | }
151 | else if ( hours )
152 | {
153 | ret = hours+'h '+minutes+'m';
154 | }
155 | else if ( minutes )
156 | {
157 | ret = minutes+'m '+seconds+'s';
158 | }
159 | else
160 | {
161 | ret = seconds+'s';
162 | }
163 |
164 | return ret;
165 | }
166 |
167 | }
--------------------------------------------------------------------------------
/js/lib/startMenu.js:
--------------------------------------------------------------------------------
1 |
2 | include_once(['lib/world.js', 'lib/menu.js','lib/menuItem.js'])
3 | function startMenu()
4 | {
5 | var _menu = new menu();
6 | _menu.setPosition(260, 500)
7 | _menu.addItem( new menuItem('img/new-game.png') );
8 |
9 | var _bg = gamejs.image.load('img/splash-screen.png');
10 | var _world = null;
11 |
12 | var _setupLevel = function( mainSurface ){
13 | //Initiate the world amd set the level to a reset
14 | _world = new world();
15 | var lvlNum = 0;
16 | var self = this;
17 |
18 | var nextLevel = function(event){
19 | if ( typeof(event) !== 'undefined' )
20 | {
21 | event.preventDefault();
22 | }
23 |
24 | if ( $('#game_scorecard .nextLevel').hasClass('disabled') )
25 | {
26 | return false;
27 | }
28 |
29 | lvlNum++;
30 |
31 | resetLevel();
32 | return false;
33 | };
34 |
35 | var resetLevel = function(event){
36 | if ( typeof(event) !== 'undefined' )
37 | {
38 | event.preventDefault();
39 | }
40 |
41 | $('#game_scorecard_bg').hide();
42 |
43 | _world.init(lvlNum, mainSurface);
44 | return false;
45 | }
46 |
47 | var backToMenu = function(event){
48 | if ( typeof(event) !== 'undefined' )
49 | {
50 | event.preventDefault();
51 | }
52 |
53 | _world = null;
54 | $('#game_scorecard_bg').remove();
55 | return false;
56 | }
57 |
58 | $('.nextLevel').die();
59 | $('.resetLevel').die();
60 | $('.mainMenu').die();
61 |
62 | $('.nextLevel').live('click', nextLevel);
63 | $('.resetLevel').live('click', resetLevel);
64 | $('.mainMenu').live('click', backToMenu);
65 |
66 | //Initialise the first level
67 | nextLevel();
68 | }
69 |
70 | this.handleInput = function( mainSurface ){
71 | if ( _world === null )
72 | {
73 | gamejs.event.get().forEach(function(event){
74 | if ( event.type === gamejs.event.KEY_DOWN )
75 | {
76 | if ( event.key == gamejs.event.K_ENTER )
77 | {
78 | _setupLevel( mainSurface );
79 | }
80 | }
81 | });
82 | }
83 | else
84 | {
85 | _world.handleInput();
86 | }
87 | }
88 |
89 | this.update = function( msDuration ){
90 | if ( _world !== null )
91 | {
92 | _world.update( msDuration );
93 | }
94 | }
95 |
96 | this.draw = function( surface ){
97 | if ( _world === null )
98 | {
99 | surface.blit(_bg);
100 | _menu.draw( surface );
101 | }
102 | else
103 | {
104 | _world.draw( surface );
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/js/lib/tooltip.js:
--------------------------------------------------------------------------------
1 |
2 | function tooltip()
3 | {
4 | //Load the variables required by gamejs.sprite.Sprite
5 | tooltip.superConstructor.apply(this, [0, 0]);
6 | this.image = gamejs.image.load('img/blank.png');
7 | this.rect = new gamejs.Rect([0, 0]);
8 |
9 | if ( !($('#game_tooltip').length) )
10 | {
11 | $('#gameWindow').append('');
12 | }
13 |
14 | var _text = '';
15 |
16 | this.setDimensions = function(width, height){
17 | this.rect.width = width;
18 | this.rect.height = height;
19 | }
20 |
21 | this.setPosition = function(x, y){
22 | this.rect.x = x;
23 | this.rect.y = y;
24 | }
25 |
26 | this.setText = function( text ){
27 | _text = text;
28 | }
29 |
30 | this.show = function(){
31 | $('#game_tooltip').html(_text);
32 | $('#game_tooltip').fadeIn();
33 |
34 | if ( null != tooltip.timer )
35 | {
36 | clearInterval(tooltip.timer);
37 | tooltip.timer = null;
38 | }
39 |
40 | tooltip.timer = setTimeout(this.hide, 5000)
41 | }
42 |
43 | this.hide = function(animate){
44 | if ( typeof(animate) == 'undefined' )
45 | {
46 | animate = true;
47 | }
48 |
49 | if ( animate )
50 | {
51 | $('#game_tooltip').fadeOut();
52 | }
53 | else
54 | {
55 | $('#game_tooltip').hide();
56 | }
57 |
58 |
59 | tooltip.timer = null;
60 | }
61 |
62 | this.handleCollision = function( playable, isCurrentPlayer){
63 | if ( isCurrentPlayer )
64 | {
65 | this.show();
66 | }
67 | }
68 |
69 | this.draw = function(){
70 | return null;
71 | }
72 | }
73 |
74 | //Set a static variable, so that we can keep track of when to hide tooltips
75 | tooltip.timer = null;
76 |
77 | //Extend the playable object so that the parent is the sprite
78 | gamejs.utils.objects.extend(tooltip, gamejs.sprite.Sprite);
--------------------------------------------------------------------------------
/js/lib/utils.js:
--------------------------------------------------------------------------------
1 | function include_once(includes)
2 | {
3 | if ( typeof($('head').data('included')) == 'undefined' )
4 | {
5 | $('head').data('included', []);
6 | }
7 |
8 | for ( var i = 0; i < includes.length; i++ )
9 | {
10 | if ( $.inArray(includes[i], $('head').data('included')) === -1 )
11 | {
12 | $('head').append(
13 | ''
14 | );
15 |
16 | $('head').data('included').push(includes[i]);
17 | }
18 | }
19 | }
20 |
21 | function playerCollides(playable, rect, drag)
22 | {
23 | if ( typeof(drag) == 'undefined' )
24 | {
25 | drag = 50;
26 | }
27 |
28 | //Define the top edge (left to right, along the top of the
29 | //colliding block)
30 | var topEdge = [ [rect.left, rect.top], [rect.right, rect.top] ];
31 |
32 | //Define the top edge (left to right, along the bottom of the
33 | //colliding this)
34 | var bottomEdge = [ [rect.left, rect.bottom], [rect.right, rect.bottom] ];
35 |
36 | //Define the left edge (top to bottom, along the left of the
37 | //colliding this)
38 | var leftEdge = [ [rect.left, rect.top], [rect.left, rect.bottom] ];
39 |
40 | //Define the right edge (top to bottom, along the right of the
41 | //colliding this)
42 | var rightEdge = [ [rect.right, rect.top], [rect.right, rect.bottom] ];
43 |
44 | //Check the top and bottom colliision points. If a collision is
45 | //detected then set the velocity on the Y axis to zero and move
46 | //the playable so that it is no longer colliding
47 | if ( playable.rect.collideLine(topEdge[0], topEdge[1]) )
48 | {
49 | var newX = playable.getVelocity().x;
50 | if (newX < 0 )
51 | {
52 | newX += drag;
53 | }
54 | else if ( newX > 0 )
55 | {
56 | newX -= drag;
57 | }
58 | playable.setVelocity( newX, 0 );
59 | playable.rect.bottom = (rect.top - 0.01);
60 | playable.setMovement('walk');
61 | }
62 | else if ( playable.rect.collideLine(bottomEdge[0], bottomEdge[1]) )
63 | {
64 | playable.setVelocity( playable.getVelocity().x, 0 );
65 | playable.rect.top = (rect.bottom + 0.01);
66 | }
67 |
68 | //Check the left and right colliision points. If a collision is
69 | //detected then set the velocity on the X axis to zero and move
70 | //the playable so that it is no longer colliding
71 | if ( playable.rect.collideLine(leftEdge[0], leftEdge[1]) )
72 | {
73 | playable.setVelocity( 0, playable.getVelocity().y );
74 | playable.rect.right = (rect.left - 0.01);
75 |
76 | }
77 | else if ( playable.rect.collideLine(rightEdge[0], rightEdge[1]) )
78 | {
79 | playable.setVelocity( 0, playable.getVelocity().y );
80 | playable.rect.left = (rect.right + 0.01);
81 | }
82 | }
--------------------------------------------------------------------------------
/js/lib/world.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents the game world, containing the game logic and sprites that
3 | * require rendering
4 | *
5 | * @author David North
6 | */
7 | include_once(['lib/camera.js','lib/player.js', 'lib/scorecard.js',
8 | 'lib/lever.js', 'lib/door.js', 'lib/gates/andGate.js',
9 | 'lib/gates/orGate.js', 'lib/gates/notGate.js', 'lib/block.js',
10 | 'lib/goal.js', 'lib/tooltip.js', 'lib/platform.js']);
11 | function world()
12 | {
13 | //The maximum X velocity a player can trvel (heading right)
14 | const MAX_X_VELOCITY = 200;
15 |
16 | //The maximum Y velocity a player can trvel (heading down)
17 | const MAX_Y_VELOCITY = 400;
18 |
19 | //The minimum X velocity a player can trvel (heading left)
20 | const MIN_X_VELOCITY = -250;
21 |
22 | //The minimum Y velocity a player can trvel (heading up)
23 | const MIN_Y_VELOCITY = -350;
24 |
25 | /**
26 | * @var boolean Whether the world has fully loaded
27 | */
28 | var _hasLoaded;
29 |
30 | var _levelComplete;
31 |
32 | var _gameTime;
33 |
34 | var _scorecard;
35 |
36 | /**
37 | * @var camera The camera to use
38 | */
39 | var _camera;
40 |
41 | /**
42 | * @var gamejs.sprite.Sprite Represents the bounding box (and background)
43 | * of a level
44 | */
45 | var _levelSprite;
46 |
47 | var _levelRect;
48 |
49 | /**
50 | * @var player Represents the player
51 | */
52 | var _p;
53 |
54 | /**
55 | * @var gamejs.sprite.Group Represents all objects a player can
56 | * interact with
57 | */
58 | var _objects;
59 |
60 | var _goals;
61 |
62 | _levelSpriteCollission = function(playable){
63 | var rightEdge = [
64 | [this.rect.right, this.rect.top],
65 | [this.rect.right, this.rect.bottom]
66 | ];
67 |
68 | var leftEdge = [
69 | [this.rect.left, this.rect.top],
70 | [this.rect.left, this.rect.bottom]
71 | ];
72 |
73 | var topEdge = [
74 | [this.rect.left, this.rect.top],
75 | [this.rect.right, this.rect.top]
76 | ];
77 |
78 | var bottomEdge = [
79 | [this.rect.left, this.rect.bottom],
80 | [this.rect.right, this.rect.bottom]
81 | ];
82 |
83 | //Test the left and right edges, setting as appropriate
84 | if ( playable.rect.collideLine(rightEdge[0], rightEdge[1]) )
85 | {
86 | playable.rect.right = this.rect.right;
87 | }
88 | else if ( playable.rect.collideLine(leftEdge[0], leftEdge[1]) )
89 | {
90 | playable.rect.left = this.rect.left;
91 | }
92 |
93 | //Test the top and bottom edges, setting as appropriate
94 | if ( playable.rect.collideLine( topEdge[0], topEdge[1]) )
95 | {
96 | playable.rect.top = this.rect.top;
97 | }
98 | else if ( playable.rect.collideLine(bottomEdge[0], bottomEdge[1]) )
99 | {
100 | playable.rect.bottom = this.rect.bottom;
101 | }
102 | }
103 |
104 | /**
105 | * Main initiation method. Must be called before using the object
106 | *
107 | * @param object mainSurface the surface that everything will be drawn to
108 | *
109 | * @return world
110 | */
111 | this.init = function( levelNum, mainSurface )
112 | {
113 | this.clear();
114 |
115 | _levelSprite.image = gamejs.image.load('img/bg.png');
116 |
117 | var _size = _levelSprite.image.getSize();
118 | _levelSprite.rect = new gamejs.Rect([0, 0], [_size[0], _size[1]]);
119 | _levelRect = new gamejs.Rect([0, 0], [_size[0], _size[1]]);
120 |
121 | _objects.add(_levelSprite);
122 |
123 | _camera.setWidth( mainSurface.getSize()[0] );
124 | _camera.setHeight( mainSurface.getSize()[1] );
125 |
126 | $.ajax({
127 | "url": "js/lib/levels/level_"+levelNum+".js",
128 | "dataType": "json",
129 | "success": function(data){
130 | _loadLevel(data);
131 | _camera.focusOn(_p.getCurrentPlayable().rect, true);
132 | _hasLoaded = true;
133 | },
134 | "error": function(jqXHR, textStatus, errorThrown){
135 | throw errorThrown;
136 | }
137 | });
138 |
139 | return this;
140 | }
141 |
142 | this.clear = function(){
143 | if ( _objects instanceof gamejs.sprite.Group )
144 | {
145 | while( _objects.sprites().length )
146 | {
147 | var sprite = _objects.sprites()[0];
148 |
149 | if ( typeof(sprite.hide) == 'function' )
150 | {
151 | sprite.hide();
152 | }
153 |
154 | sprite.kill();
155 | sprite.remove(_objects);
156 | };
157 | }
158 |
159 | if ( _scorecard instanceof scorecard )
160 | {
161 | _scorecard.hide();
162 | }
163 |
164 | _hasLoaded = false;
165 | _levelComplete = false;
166 | _gameTime = 0;
167 | _scorecard = new scorecard();
168 | _camera = new camera( this );
169 | _levelSprite = new gamejs.sprite.Sprite();
170 | _levelRect = new gamejs.Rect([0,0]);
171 | _p = new player();
172 | _objects = new gamejs.sprite.Group();
173 | _goals = [];
174 |
175 | _levelSprite.handleCollision = _levelSpriteCollission;
176 | }
177 |
178 |
179 | this.getBoundingRect = function()
180 | {
181 | return _levelRect;
182 | }
183 |
184 | this.getObjects = function()
185 | {
186 | return [ _p.getPlayables(), _objects ]
187 | }
188 |
189 | this.getPlayer = function()
190 | {
191 | return _p;
192 | }
193 |
194 | /**
195 | * Handles user input and modifies the world objects accordingly
196 | *
197 | * @return world
198 | */
199 | this.handleInput = function()
200 | {
201 | if ( _hasLoaded && !_camera.isAnimating())
202 | {
203 | var self = this;
204 |
205 | //Loop through each game event (key presses mouse movements etc)
206 | gamejs.event.get().forEach(function(event){
207 | if ( !_levelComplete )
208 | {
209 | var _currentPlayer = _p.getCurrentPlayable();
210 |
211 | _p.handleInput(event);
212 |
213 | if ( _p.getCurrentPlayable() != _currentPlayer )
214 | {
215 | _camera.focusOn(
216 | _p.getCurrentPlayable().rect, true, true
217 | );
218 | }
219 |
220 | _objects.forEach(function(obj){
221 | if ( typeof(obj.handleInput) == 'function' )
222 | {
223 | obj.handleInput(self, event);
224 | }
225 | });
226 | }
227 | });
228 | }
229 |
230 | return this;
231 | }
232 |
233 | /**
234 | * Updates all objects within the world
235 | *
236 | * @param int msDuration The amount of time since the last update
237 | *
238 | * @return world
239 | */
240 | this.update = function( msDuration )
241 | {
242 | if ( _hasLoaded && !_levelComplete )
243 | {
244 | _gameTime += msDuration;
245 |
246 | //Apply the gravitational pull of the world
247 | _applyGravity( msDuration );
248 |
249 | //Apply updates to the player and any objects in the world
250 | _p.update( msDuration );
251 | _objects.update( msDuration );
252 |
253 | var colliders =
254 | gamejs.sprite.groupCollide(_p.getPlayables(), _objects);
255 |
256 | for( var i = 0; i < colliders.length; i++ )
257 | {
258 | if ( typeof(colliders[i].b.handleCollision) == 'function' )
259 | {
260 | var isCurrentPlayer = false;
261 | if ( colliders[i].a === _p.getCurrentPlayable() )
262 | {
263 | isCurrentPlayer = true;
264 | }
265 |
266 | colliders[i].b.handleCollision(
267 | colliders[i].a, isCurrentPlayer
268 | );
269 | }
270 | }
271 |
272 | //Modify the camera position
273 | _camera.update ( msDuration );
274 |
275 | _levelComplete = true;
276 | for( var i = 0; i < _goals.length; i++ )
277 | {
278 | if ( !_goals[i].isActive() )
279 | {
280 | _levelComplete = false;
281 | break;
282 | }
283 | }
284 |
285 | if ( _levelComplete )
286 | {
287 | for( var i = 0; i < _objects.sprites().length; i++ )
288 | {
289 | var obj = _objects.sprites()[i];
290 | if ( obj instanceof tooltip )
291 | {
292 | obj.hide(false);
293 | }
294 | }
295 |
296 | _scorecard.setTimeTaken(_gameTime / 1000);
297 | _scorecard.setClonesUsed(_p.getNumClones());
298 | _scorecard.show();
299 | }
300 | }
301 |
302 | return this;
303 | }
304 |
305 | /**
306 | * Draws all objects within the world
307 | *
308 | * @return this
309 | */
310 | this.draw = function ( mainSurface )
311 | {
312 | if ( _hasLoaded )
313 | {
314 | //Draw the level, collidables and non collidables, as these need
315 | //to be behind the player
316 | _objects.draw( mainSurface );
317 |
318 | //Draw the player at the forefront of the level
319 | _p.draw( mainSurface );
320 | }
321 |
322 | return this;
323 | }
324 |
325 | /**
326 | * Loads the level data from an array, filling the worls with collidables
327 | * and ensuring it's playable
328 | */
329 | var _loadLevel = function( data, input, output )
330 | {
331 | var _hasPlayer = false;
332 | for ( var i = 0; i < data.length; i ++)
333 | {
334 | if ( data[i]['type'] == 'stats' )
335 | {
336 | _scorecard.setParForClones(data[i]['clonePar']);
337 | _scorecard.setParForTime(data[i]['timePar']);
338 |
339 | if ( typeof(data[i]['lastLevel']) == 'boolean' )
340 | {
341 | _scorecard.setLastLevel(data[i]['lastLevel']);
342 | }
343 | else
344 | {
345 | _scorecard.setLastLevel(false);
346 | }
347 | }
348 | else
349 | {
350 | var xAmount = 1;
351 | var yAmount = 1;
352 |
353 | if ( typeof(data[i]['repeat-x']) != 'undefined')
354 | {
355 | xAmount = data[i]['repeat-x'];
356 | }
357 |
358 | if ( typeof(data[i]['repeat-y']) != 'undefined')
359 | {
360 | yAmount = data[i]['repeat-y'];
361 | }
362 |
363 | for ( var x = 0; x < xAmount; x++ )
364 | {
365 | for ( var y = 0; y < yAmount; y++ )
366 | {
367 | _addObjectToWorld(data[i], x, y, input, output);
368 | }
369 | }
370 | }
371 | }
372 | }
373 |
374 | var _addObjectToWorld = function( data, x, y, input, output ){
375 | var obj = null;
376 |
377 | if ( 'tooltip' == data['type'] )
378 | {
379 | obj = new tooltip();
380 | obj.setText(data['text']);
381 | _objects.add( obj );
382 | }
383 | else
384 | {
385 | switch ( data['type'] )
386 | {
387 | case 'block':
388 | case 'wall':
389 | data['type'] = 'block';
390 | case 'andGate':
391 | case 'orGate':
392 | case 'notGate':
393 | case 'lever':
394 | case 'door':
395 | case 'goal':
396 | case 'platform':
397 | var type = data['type'];
398 | obj = new window[type]();
399 | _objects.add( obj );
400 |
401 | if ( 'goal' === type )
402 | {
403 | _goals.push( obj );
404 | }
405 | break;
406 |
407 | case 'player':
408 | obj = _p.getCurrentPlayable();
409 | }
410 | }
411 |
412 | if ( null != obj )
413 | {
414 | if ( obj instanceof io )
415 | {
416 | if ( typeof(data['outputs']) !== 'undefined' )
417 | {
418 | _loadLevel(data['outputs'], obj);
419 | }
420 |
421 | if ( typeof(data['inputs']) !== 'undefined' )
422 | {
423 | _loadLevel(data['inputs'], null, obj)
424 | }
425 |
426 | if ( typeof(input) !== 'undefined' && input != null )
427 | {
428 | obj.addInput(input);
429 | }
430 |
431 | if ( typeof(output) !== 'undefined' && output != null )
432 | {
433 | obj.addOutput(output);
434 | }
435 | }
436 |
437 | if ( typeof(obj.setDimensions) != 'undefined' )
438 | {
439 | if ( typeof(data['width']) != 'undefined'
440 | && typeof(data['height']) != 'undefined' )
441 | {
442 | obj.setDimensions(data['width'], data['height']);
443 | }
444 | }
445 |
446 | var width = obj.rect.width;
447 | var height = obj.rect.height;
448 |
449 | var xPos = (data['x'] + (width * x));
450 | var yPos = (data['y'] + (height * y));
451 |
452 | obj.setPosition( xPos, yPos );
453 | }
454 | }
455 |
456 | /**
457 | * Applies the gravitational pull of the world on all playables
458 | */
459 | var _applyGravity = function( msDuration )
460 | {
461 | //Loop through each player and increase the Y velocity downward.
462 | //If the player is jumping, this has the affect of slowing the
463 | //player down. Otherwise the player is falling.
464 | _p.getPlayables().forEach(function(obj){
465 | var newVelocityY = obj.getVelocity().y
466 | newVelocityY += Math.round(0.3 * msDuration);
467 |
468 | obj.setVelocity( obj.getVelocity().x, newVelocityY )
469 |
470 | //the velocity cannot exceed the maximums, so ensuer that the player
471 | //is not falling too fast
472 | _sanatiseVelocity(obj);
473 | });
474 | }
475 |
476 | /**
477 | * Ensures that the player is not travelling too fast ion any direction
478 | */
479 | var _sanatiseVelocity = function(obj)
480 | {
481 | //Get the current Velocity
482 | var velocity = obj.getVelocity();
483 |
484 | //Make sure that the X velocity is not too slow or fast
485 | velocity.x = Math.max( MIN_X_VELOCITY, velocity.x );
486 | velocity.x = Math.min( MAX_X_VELOCITY, velocity.x );
487 |
488 | //Make sure that the Y velocity is not too slow or fast
489 | velocity.y = Math.max( MIN_Y_VELOCITY, velocity.y );
490 | velocity.y = Math.min( MAX_Y_VELOCITY, velocity.y );
491 |
492 | //Update the players velocity
493 | obj.setVelocity( velocity.x, velocity.y );
494 | }
495 | }
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | gamejs = require('gamejs');
2 | font = require('gamejs/font');
3 |
4 | //Preload all the required images
5 | gamejs.preload([
6 | 'img/splash-screen.png','img/new-game.png', 'img/bg.png','img/player.png',
7 | 'img/blank.png','img/switch.png','img/door.png','img/goal.png',
8 | 'img/platform-left.png','img/platform-right.png',
9 | 'img/platform-middle.png', 'img/selected.png',
10 | 'img/scorecard-background.png','img/game-window.png',
11 | 'img/reset.png', 'img/star-off.png', 'img/star-on.png',
12 | 'img/menu-button.png', 'img/next-button.png', 'img/end.png'
13 | ]);
14 |
15 | gamejs.ready(function() {
16 |
17 | var display = gamejs.display.setMode([800, 600]);
18 |
19 | //Ensure that all required files are included
20 | include_once([
21 | 'lib/startMenu.js'
22 | ]);
23 |
24 | var mainSurface = gamejs.display.getSurface();
25 | var mainWindow = new startMenu();
26 | var self = this;
27 |
28 | // msDuration = time since last tick() call
29 | var tick = function(msDuration){
30 | mainSurface.fill("#FFFFFF");
31 |
32 | //Handle user input
33 | mainWindow.handleInput( mainSurface );
34 |
35 | //Update the worlds objects
36 | mainWindow.update( msDuration );
37 |
38 | //Draw the new objects
39 | mainWindow.draw( mainSurface );
40 | };
41 |
42 | //Set up listeners for the body when a scorecard is present
43 | $('body').keydown(function(event){
44 | //Only check key presses if the scorcard is shown
45 | if ( $('#game_scorecard').is(":visible") )
46 | {
47 | switch ( event.keyCode )
48 | {
49 | //Enter and e will all move to the next level
50 | case 13:
51 | case 69:
52 | $('#game_scorecard .nextLevel').click();
53 | event.preventDefault();
54 | break;
55 | //Escape will quit to the menu
56 | case 27:
57 | $('#game_scorecard .mainMenu').click();
58 | event.preventDefault();
59 | break;
60 | //r will reset the level
61 | case 82:
62 | $('#game_scorecard .resetLevel').click();
63 | event.preventDefault();
64 | break;
65 | }
66 | }
67 | });
68 |
69 | //Remove the loading bar
70 | $('#preload').remove();
71 | $('#gameWindow').show();
72 |
73 | //Set up the tick function, run at 60fps
74 | gamejs.time.fpsCallback(tick, self, 60);
75 | });
76 |
--------------------------------------------------------------------------------