1 /**
  2  * The Render Engine
  3  * WiimoteInputComponent
  4  *
  5  * @fileoverview An extension of the keyboard input component which handles the
  6  *               Nintendo Wii remote.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1555 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.components.input.Wiimote",
 37     "requires":[
 38         "R.components.input.Keyboard",
 39         "R.math.Math2D",
 40         "R.math.Point2D",
 41         "R.math.Vector2D",
 42         "R.math.Rectangle2D"
 43     ]
 44 });
 45 
 46 /**
 47  * @class A component which responds to the Wiimote (in the Opera browser)
 48  * is notifies its host object by calling a number of methods.  The host object
 49  * should implement any of the following methods to receive the corresponding events.
 50  * <br/>
 51  * The next few events are fired on the host object, if they exist, when
 52  * the corresponding button is pressed and released.  All methods take three
 53  * arguments: the controller number, a boolean indicating <tt>true</tt>
 54  * if the button has been pressed or <tt>false</tt> when released, and
 55  * the event object that caused the method to be invoked.
 56  * <br/>
 57  * <ul>
 58  * <li><tt>onWiimoteLeft()</tt> - Direction pad left</li>
 59  * <li><tt>onWiimoteRight()</tt> - Direction pad right</li>
 60  * <li><tt>onWiimoteUp()</tt> - Direction pad up</li>
 61  * <li><tt>onWiimoteDown()</tt> - Direction pad down</li>
 62  * <li><tt>onWiimotePlus()</tt> - Plus button pressed/released</li>
 63  * <li><tt>onWiimoteMinus()</tt> - Minus button pressed/released</li>
 64  * <li><tt>onWiimoteButton1()</tt> - Button 1 pressed/released</li>
 65  * <li><tt>onWiimoteButton2()</tt> - Button 2 pressed/released</li>
 66  * <li><tt>onWiimoteButtonA()</tt> - Button A pressed/released</li>
 67  * <li><tt>onWiimoteButtonB()</tt> - Button B pressed/released</li>
 68  * <li><tt>onWiimoteButtonC()</tt> - Button C pressed/released</li>
 69  * <li><tt>onWiimoteButtonZ()</tt> - Button Z pressed/released</li>
 70  * </ul>
 71  * <br/><br/>
 72  * The following events are status events and take different
 73  * arguments:
 74  * <ul>
 75  * <li><tt>onWiimoteEnabled()</tt> - Enabled/disabled status (controller, state)</li>
 76  * <li><tt>onWiimoteValidity()</tt> - Validity of data transfer (controller, validity)</li>
 77  * <li><tt>onWiimoteDistance()</tt> - Distance from screen in meters (controller, dist)</li>
 78  * <li><tt>onWiimotePosition()</tt> - X/Y position (controller, x, y)</li>
 79  * <li><tt>onWiimoteRoll()</tt> - X-axis roll in radians (controller, roll)</li>
 80  * <li><tt>onWiimoteOffscreen()</tt> - Triggered <i>instead of</i> onWiimotePosition if the
 81  *                          remote isn't pointing at the screen.</li>
 82  * </ul>
 83  *
 84  * @param name {String} The name of the component
 85  * @param [priority=0.1] {Number} The priority of the component
 86  * @extends R.components.input.Keyboard
 87  * @constructor
 88  * @description Create a Wii remote input component.
 89  */
 90 R.components.input.Wiimote = function () {
 91     "use strict";
 92     return R.components.input.Keyboard.extend(/** @scope R.components.input.Wiimote.prototype */{
 93 
 94         enabledRemotes:null,
 95 
 96         remoteValid:null,
 97 
 98         hasMethods:null,
 99 
100         /**
101          * @private
102          */
103         constructor:function (name, priority) {
104             this.base(name, priority || 0.1);
105             this.enabledRemotes = [false, false, false, false];
106             this.remoteValid = [0, 0, 0, 0];
107 
108             var ctx = R.Engine.getDefaultContext();
109             var self = this;
110 
111             // Add the event handlers
112             ctx.addEvent(this, "mousedown", function (evt) {
113                 self._mouseDownListener(evt);
114             });
115 
116             ctx.addEvent(this, "mouseup", function (evt) {
117                 self._mouseUpListener(evt);
118             });
119 
120             if (!R.browser.Wii) {
121                 // In the absense of the WiiMote, we'll use the mouse as the pointer
122                 ctx.addEvent(this, "mousemove", function (evt) {
123                     self._mouseMoveListener(evt);
124                 });
125             }
126 
127             this.hasMethods = [false, false, false, false, false, false,
128                 false, false, false, false, false, false,
129                 false, false, false, false, false, false];
130         },
131 
132         /**
133          * Releases the component back into the object pool. See {@link R.engine.PooledObject#release}
134          * for more information.
135          */
136         release:function () {
137             this.base();
138             this.enabledRemotes = null;
139             this.remoteValid = null;
140             this.hasMethods = null;
141         },
142 
143         /**
144          * Destroy this instance and remove all references.
145          */
146         destroy:function () {
147             var ctx = R.Engine.getDefaultContext();
148 
149             // Clean up event handlers
150             ctx.removeEvent(this, "mousedown");
151             ctx.removeEvent(this, "mouseup");
152 
153             if (!R.browser.Wii) {
154                 // In the absence of the WiiMote, remove the mouse move handler
155                 ctx.removeEvent(this, "mousemove");
156             }
157             this.base();
158         },
159 
160         /**
161          * Deprecated in favor of {@link #setGameObject}
162          * @deprecated
163          */
164         setHostObject:function (hostObj) {
165             this.setGameObject(hostObj);
166         },
167 
168         /**
169          * Establishes the link between this component and its game object.
170          * When you assign components to a game object, it will call this method
171          * so that each component can refer to its game object, the same way
172          * a game object can refer to a component with {@link R.engine.GameObject#getComponent}.
173          *
174          * @param gameObject {R.engine.GameObject} The object which hosts this component
175          */
176         setGameObject:function (gameObject) {
177             this.base(gameObject);
178             this.hasMethods = [gameObject.onWiimoteLeft != undefined,
179                 gameObject.onWiimoteRight != undefined,
180                 gameObject.onWiimoteUp != undefined,
181                 gameObject.onWiimoteDown != undefined,
182                 gameObject.onWiimotePlus != undefined,
183                 gameObject.onWiimoteMinus != undefined,
184                 gameObject.onWiimoteButton1 != undefined,
185                 gameObject.onWiimoteButton2 != undefined,
186                 gameObject.onWiimoteButtonA != undefined,
187                 gameObject.onWiimoteButtonB != undefined,
188                 gameObject.onWiimoteButtonC != undefined,
189                 gameObject.onWiimoteButtonZ != undefined,
190                 gameObject.onWiimoteEnabled != undefined,
191                 gameObject.onWiimoteDistance != undefined,
192                 gameObject.onWiimoteValidity != undefined,
193                 gameObject.onWiimoteOffscreen != undefined,
194                 gameObject.onWiimotePosition != undefined,
195                 gameObject.onWiimoteRoll != undefined];
196         },
197 
198 
199         /** @private */
200         _mouseDownListener:function (evt) {
201             this._wmButtonA(evt, 0, true);
202         },
203 
204         /** @private */
205         _mouseUpListener:function (evt) {
206             this._wmButtonA(evt, 0, false);
207         },
208 
209         /** @private */
210         _mouseMoveListener:function (evt) {
211             this._wmPosition(0, evt.pageX, evt.pageY, evt.screenX, evt.screenY);
212         },
213 
214         /** @private */
215         _keyDownListener:function (event) {
216             // This is for handling the Primary Wiimote
217             switch (event.keyCode) {
218                 case R.components.input.Wiimote.KEYCODE_LEFT:
219                     this._wmLeft(event, 0, true);
220                     break;
221                 case R.components.input.Wiimote.KEYCODE_RIGHT:
222                     this._wmRight(event, 0, true);
223                     break;
224                 case R.components.input.Wiimote.KEYCODE_UP:
225                     this._wmUp(event, 0, true);
226                     break;
227                 case R.components.input.Wiimote.KEYCODE_DOWN:
228                     this._wmDown(event, 0, true);
229                     break;
230                 case R.components.input.Wiimote.KEYCODE_PLUS:
231                     this._wmPlus(event, 0, true);
232                     break;
233                 case R.components.input.Wiimote.KEYCODE_MINUS:
234                     this._wmMinus(event, 0, true);
235                     break;
236                 case R.components.input.Wiimote.KEYCODE_1:
237                     this._wmButton1(event, 0, true);
238                     break;
239                 case R.components.input.Wiimote.KEYCODE_2:
240                     this._wmButton2(event, 0, true);
241                     break;
242                 case R.components.input.Wiimote.KEYCODE_A:
243                     this._wmButtonA(event, 0, true);
244                     break;
245                 case R.components.input.Wiimote.KEYCODE_B:
246                     this._wmButtonB(event, 0, true);
247                     break;
248                 case R.components.input.Wiimote.KEYCODE_C:
249                     return this._wmButtonC(event, 0, true);
250                     break;
251                 case R.components.input.Wiimote.KEYCODE_Z:
252                     this._wmButtonZ(event, 0, true);
253                     break;
254             }
255 
256             // Pass along for straight keyboard handling
257             this.base(event);
258         },
259 
260         /** @private */
261         _keyUpListener:function (event) {
262             // This is for handling the Primary Wiimote
263             switch (event.keyCode) {
264                 case R.components.input.Wiimote.KEYCODE_LEFT:
265                     this._wmLeft(event, 0, false);
266                     break;
267                 case R.components.input.Wiimote.KEYCODE_RIGHT:
268                     this._wmRight(event, 0, false);
269                     break;
270                 case R.components.input.Wiimote.KEYCODE_UP:
271                     this._wmUp(event, 0, false);
272                     break;
273                 case R.components.input.Wiimote.KEYCODE_DOWN:
274                     this._wmDown(event, 0, false);
275                     break;
276                 case R.components.input.Wiimote.KEYCODE_PLUS:
277                     this._wmPlus(event, 0, false);
278                     break;
279                 case R.components.input.Wiimote.KEYCODE_MINUS:
280                     this._wmMinus(event, 0, false);
281                     break;
282                 case R.components.input.Wiimote.KEYCODE_1:
283                     this._wmButton1(event, 0, false);
284                     break;
285                 case R.components.input.Wiimote.KEYCODE_2:
286                     this._wmButton2(event, 0, false);
287                     break;
288                 case R.components.input.Wiimote.KEYCODE_A:
289                     this._wmButtonA(event, 0, false);
290                     break;
291                 case R.components.input.Wiimote.KEYCODE_B:
292                     this._wmButtonB(event, 0, false);
293                     break;
294                 case R.components.input.Wiimote.KEYCODE_C:
295                     this._wmButtonC(event, 0, false);
296                     break;
297                 case R.components.input.Wiimote.KEYCODE_Z:
298                     this._wmButtonZ(event, 0, false);
299                     break;
300             }
301 
302             // Pass along for straight keyboard handling
303             this.base(event);
304         },
305 
306         /**
307          * This will do the polling of the Wiimote and fire events when
308          * statuses change.
309          *
310          * @private
311          */
312         execute:function (renderContext, time, dt) {
313             if (!R.browser.Wii) {
314                 // If this isn't Opera for Wii, don't do anything
315                 return;
316             }
317 
318             // Run through the available Wiimotes
319             var op = R.browser.WiiMote;
320             for (var w = 0; w < 4; w++) {
321 
322                 var remote = op.update(w); // This fixes a dependency problem
323                 // Cannot perform this check on the primary remote,
324                 // that's why this object extends the keyboard input component...
325                 if (remote.isEnabled) {
326 
327                     if (!this.enabledRemotes[w]) {
328                         // Let the host know that a Wiimote became enabled
329                         this._wmEnabled(w, true);
330                     }
331 
332                     if (!remote.isBrowsing) {
333                         var evt = { primary:false };
334 
335                         // Simple bitmask check to handle states and fire methods
336                         this._wmLeft(evt, w, remote.hold & 1);
337                         this._wmRight(evt, w, remote.hold & 2);
338                         this._wmDown(evt, w, remote.hold & 4);
339                         this._wmUp(evt, w, remote.hold & 8);
340                         this._wmPlus(evt, w, remote.hold & 16);
341                         this._wmButton2(evt, w, remote.hold & 256);
342                         this._wmButton1(evt, w, remote.hold & 512);
343                         this._wmButtonA(evt, w, remote.hold & 2048);
344                         this._wmMinus(evt, w, remote.hold & 4096);
345                         this._wmButtonZ(evt, w, remote.hold & 8192);
346                         this._wmButtonC(evt, w, remote.hold & 16384);
347                     }
348 
349                     this._wmButtonB(evt, w, remote.hold & 1024);
350 
351                     // Set validity of remote data
352                     this._wmValidity(w, remote.dpdValidity);
353 
354                     // Set distance to screen
355                     this._wmDistance(w, remote.dpdDistance);
356 
357                     // Set position and roll
358                     this._wmPosition(w, remote.dpdScreenX, remote.dpdScreenY, remote.dpdX, remote.dpdY);
359                     this._wmRoll(w, remote.dpdRollX, remote.dpdRollY, Math.atan2(remote.dpdRollY, remote.dpdRollX));
360                 } else {
361                     if (this.enabledRemotes[w]) {
362                         // Let the host know that a Wiimote became disabled
363                         this._wmEnabled(w, false);
364                     }
365                 }
366             }
367         },
368 
369         /** @private */
370         _wmLeft:function (evt, controllerNum, pressed) {
371             if (this.hasMethods[0]) {
372                 this.getGameObject().onWiimoteLeft(controllerNum, pressed, evt);
373             }
374         },
375 
376         /** @private */
377         _wmRight:function (evt, controllerNum, pressed) {
378             if (this.hasMethods[1]) {
379                 this.getGameObject().onWiimoteRight(controllerNum, pressed, evt);
380             }
381         },
382 
383         /** @private */
384         _wmUp:function (evt, controllerNum, pressed) {
385             if (this.hasMethods[2]) {
386                 this.getGameObject().onWiimoteUp(controllerNum, pressed, evt);
387             }
388         },
389 
390         /** @private */
391         _wmDown:function (evt, controllerNum, pressed) {
392             if (this.hasMethods[3]) {
393                 this.getGameObject().onWiimoteDown(controllerNum, pressed, evt);
394             }
395         },
396 
397         /** @private */
398         _wmPlus:function (evt, controllerNum, pressed) {
399             if (this.hasMethods[4]) {
400                 this.getGameObject().onWiimotePlus(controllerNum, pressed, evt);
401             }
402         },
403 
404         /** @private */
405         _wmMinus:function (evt, controllerNum, pressed) {
406             if (this.hasMethods[5]) {
407                 this.getGameObject().onWiimoteMinus(controllerNum, pressed, evt);
408             }
409         },
410 
411         /** @private */
412         _wmButton1:function (evt, controllerNum, pressed) {
413             if (this.hasMethods[6]) {
414                 this.getGameObject().onWiimoteButton1(controllerNum, pressed, evt);
415             }
416         },
417 
418         /** @private */
419         _wmButton2:function (evt, controllerNum, pressed) {
420             if (this.hasMethods[7]) {
421                 this.getGameObject().onWiimoteButton2(controllerNum, pressed, evt);
422             }
423         },
424 
425         /** @private */
426         _wmButtonA:function (evt, controllerNum, pressed) {
427             if (this.hasMethods[8]) {
428                 this.getGameObject().onWiimoteButtonA(controllerNum, pressed, evt);
429             }
430         },
431 
432         /** @private */
433         _wmButtonB:function (evt, controllerNum, pressed) {
434             if (this.hasMethods[9]) {
435                 this.getGameObject().onWiimoteButtonB(controllerNum, pressed, evt);
436             }
437         },
438 
439         /** @private */
440         _wmButtonC:function (evt, controllerNum, pressed) {
441             if (this.hasMethods[10]) {
442                 this.getGameObject().onWiimoteButtonC(controllerNum, pressed, evt);
443             }
444         },
445 
446         /** @private */
447         _wmButtonZ:function (evt, controllerNum, pressed) {
448             if (this.hasMethods[11]) {
449                 this.getGameObject().onWiimoteButtonZ(controllerNum, pressed, evt);
450             }
451         },
452 
453         /** @private */
454         _wmEnabled:function (controllerNum, state) {
455             // Store the Wiimote enabled state
456             this.enabledRemotes[controllerNum] = state;
457             if (this.hasMethods[12]) {
458                 this.getGameObject().onWiimoteEnabled(controllerNum, state);
459             }
460         },
461 
462         /** @private */
463         _wmDistance:function (c, d) {
464             if (this.hasMethods[13]) {
465                 this.getGameObject().onWiimoteDistance(c, d);
466             }
467         },
468 
469         /** @private */
470         _wmValidity:function (c, v) {
471             if (this.remoteValid[c] != v) {
472                 this.remoteValid[c] = v;
473                 if (this.hasMethods[14]) {
474                     this.getGameObject().onWiimoteValidity(c, v);
475                 }
476             }
477         },
478 
479         /** @private */
480         _wmPosition:function (c, sx, sy, x, y) {
481             if (this.hasMethods[15]) {
482                 this.getGameObject().onWiimoteOffscreen(c, (!sx || !sy));
483             }
484 
485             if ((sx && sy) && this.hasMethods[16]) {
486                 this.getGameObject().onWiimotePosition(c, sx, sy, x, y);
487             }
488         },
489 
490         /** @private */
491         _wmRoll:function (c, x, y, z) {
492             if (this.hasMethods[17]) {
493                 // Pitch, yaw, roll?
494                 this.getGameObject().onWiimoteRoll(c, x, y, z);
495             }
496         }
497     }, /** @scope R.components.input.Wiimote.prototype */{
498 
499         /**
500          * Get the class name of this object
501          *
502          * @return {String} "R.components.input.Wiimote"
503          */
504         getClassName:function () {
505             return "R.components.input.Wiimote";
506         },
507 
508         /**
509          * For second argument to <tt>onWiimoteValidity()</tt>: the data is good
510          * @type {Number}
511          */
512         DATA_GOOD:2,
513 
514         /**
515          * For second argument to <tt>onWiimoteValidity()</tt>: the data is poor
516          * @type {Number}
517          */
518         DATA_POOR:1,
519 
520         /**
521          * For second argument to <tt>onWiimoteValidity()</tt>: the Wiimote isn't pointing at the screen
522          * @type {Number}
523          */
524         DATA_INVALID:0,
525 
526         /**
527          * For second argument to <tt>onWiimoteValidity()</tt>: the data is very poor (unreliable)
528          * @type {Number}
529          */
530         DATA_VERY_POOR:-1,
531 
532         /**
533          * For second argument to <tt>onWiimoteValidity()</tt>: the data is extremely poor (garbage)
534          * @type {Number}
535          */
536         DATA_EXTREMELY_POOR:-2,
537 
538         /**
539          * Keycode for button "A"
540          * @type {Number}
541          */
542         KEYCODE_A:13,
543 
544         /**
545          * Keycode for button "B"
546          * @type {Number}
547          */
548         KEYCODE_B:32, // 171
549 
550         /**
551          * Keycode for button "C"
552          * @type {Number}
553          */
554         KEYCODE_C:67, // 201
555 
556         /**
557          * Keycode for button "Z"
558          * @type {Number}
559          */
560         KEYCODE_Z:90, // 200
561 
562         /**
563          * Keycode for button "1"
564          * @type {Number}
565          */
566         KEYCODE_1:173,
567 
568         /**
569          * Keycode for button "2"
570          * @type {Number}
571          */
572         KEYCODE_2:173,
573 
574         /**
575          * Keycode for button "-"
576          * @type {Number}
577          */
578         KEYCODE_MINUS:170,
579 
580         /**
581          * Keycode for button "+"
582          * @type {Number}
583          */
584         KEYCODE_PLUS:174,
585 
586         /**
587          * Keycode for dpad left
588          * @type {Number}
589          */
590         KEYCODE_LEFT:178,
591 
592         /**
593          * Keycode for dpad right
594          * @type {Number}
595          */
596         KEYCODE_RIGHT:177,
597 
598         /**
599          * Keycode for dpad up
600          * @type {Number}
601          */
602         KEYCODE_UP:175,
603 
604         /**
605          * Keycode for dpad down
606          * @type {Number}
607          */
608         KEYCODE_DOWN:176
609     });
610 };
611