Vojtech Szocs has uploaded a new change for review. Change subject: Introducing space-shooter plugin ......................................................................
Introducing space-shooter plugin Welcome, space traveler! This plugin is part of UI Plugins Crash Course: http://www.ovirt.org/Tutorial/UIPlugins/CrashCourse This plugin contains Alien Invasion, sample HTML5 game developed by Pascal Rettig and released under both GPL and MIT license: https://github.com/cykod/AlienInvasion Refer to Crash Course wiki for step-by-step tutorial. Check out README file for details on installation. Change-Id: I347b18ca57b7b41e4cdf42e528cf5a7aff60c583 Signed-off-by: Vojtech Szocs <[email protected]> --- A space-shooter-plugin/.gitignore A space-shooter-plugin/README A space-shooter-plugin/space-shooter-resources/dc-score.html A space-shooter-plugin/space-shooter-resources/game/GPL-LICENSE.txt A space-shooter-plugin/space-shooter-resources/game/MIT-LICENSE.txt A space-shooter-plugin/space-shooter-resources/game/README.md A space-shooter-plugin/space-shooter-resources/game/base.css A space-shooter-plugin/space-shooter-resources/game/engine.js A space-shooter-plugin/space-shooter-resources/game/game.js A space-shooter-plugin/space-shooter-resources/game/game.manifest A space-shooter-plugin/space-shooter-resources/game/images/sprites.png A space-shooter-plugin/space-shooter-resources/game/index.html A space-shooter-plugin/space-shooter-resources/modernizr.custom.js A space-shooter-plugin/space-shooter-resources/plugin.html A space-shooter-plugin/space-shooter.json 15 files changed, 1,490 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/samples-uiplugins refs/changes/13/23813/1 diff --git a/space-shooter-plugin/.gitignore b/space-shooter-plugin/.gitignore new file mode 100644 index 0000000..517b035 --- /dev/null +++ b/space-shooter-plugin/.gitignore @@ -0,0 +1,5 @@ +# space-shooter project ignore list + +# IDE files +.idea +*.iml diff --git a/space-shooter-plugin/README b/space-shooter-plugin/README new file mode 100644 index 0000000..09c6558 --- /dev/null +++ b/space-shooter-plugin/README @@ -0,0 +1,34 @@ +oVirt Space Shooter +------------------- + +Welcome, space traveler! + +This plugin is part of UI Plugins Crash Course: + + http://www.ovirt.org/Tutorial/UIPlugins/CrashCourse + +This plugin contains Alien Invasion, sample HTML5 game +developed by Pascal Rettig and released under both GPL +and MIT license: + + https://github.com/cykod/AlienInvasion + +Refer to Crash Course wiki for step-by-step tutorial. + + +INSTALLING + +Configure oVirt Engine HTTP(S) origin(s): + + $ ENGINE_HTTP='http://engine-domain-example:8080' + $ ENGINE_HTTPS='https://engine-domain-example:8443' + $ PLUGIN_USER_CONFIG='{ "config": { "allowedOrigins": ["$ENGINE_HTTP","$ENGINE_HTTPS"] } }' + $ echo $PLUGIN_USER_CONFIG > $ENGINE_ETC/ui-plugins/space-shooter-config.json + +Alternatively, simply edit $PLUGIN_HOME/space-shooter.json file directly. + +Expose plugin metadata to oVirt Engine + $ ln -s $PLUGIN_HOME/space-shooter.json $ENGINE_USR/ui-plugins/space-shooter.json + +Expose plugin static files to oVirt Engine + $ ln -s $PLUGIN_HOME/space-shooter-resources $ENGINE_USR/ui-plugins/space-shooter-resources diff --git a/space-shooter-plugin/space-shooter-resources/dc-score.html b/space-shooter-plugin/space-shooter-resources/dc-score.html new file mode 100644 index 0000000..dcea2eb --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/dc-score.html @@ -0,0 +1,33 @@ +<!doctype html> +<!-- + oVirt Space Shooter: Data Center 'Score' sub tab + displays current user score for given Data Center entity +--> +<html> +<head> + <meta charset="utf-8"> +</head> +<body> + +<p> + Score for Data Center <span id='dataCenterName'>?</span>: + <span id='score'>?</span> (<span id='rank'>?</span>) +</p> + +<script type='text/javascript'> + + // Ask the plugin for current user score + parent.postMessage('GetDataCenterScore', '*'); + + // The plugin should call this function in response to GetDataCenterScore message + var update = function(dataCenterName, score, rank, rankColor) { + document.getElementById('dataCenterName').innerHTML = '<em>' + dataCenterName + '</em>'; + document.getElementById('score').innerHTML = '<b>' + score + '</b>'; + document.getElementById('rank').innerHTML = '<b>' + rank + '</b>'; + document.getElementById('rank').style.color = rankColor; + }; + +</script> + +</body> +</html> diff --git a/space-shooter-plugin/space-shooter-resources/game/GPL-LICENSE.txt b/space-shooter-plugin/space-shooter-resources/game/GPL-LICENSE.txt new file mode 100644 index 0000000..11dddd0 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/GPL-LICENSE.txt @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/space-shooter-plugin/space-shooter-resources/game/MIT-LICENSE.txt b/space-shooter-plugin/space-shooter-resources/game/MIT-LICENSE.txt new file mode 100644 index 0000000..72997a4 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2011 Cykod LLC, http://coderdeck.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/space-shooter-plugin/space-shooter-resources/game/README.md b/space-shooter-plugin/space-shooter-resources/game/README.md new file mode 100644 index 0000000..b023030 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/README.md @@ -0,0 +1,26 @@ +Alien Invasion +============== +This is the sample game that is built in the first three Chapter of +mobile HTML5 Game Development. + +It is released under both the GPL and MIT license to do with what you will. + +Playable Version: +http://cykod.github.com/AlienInvasion/ + +Bit.ly link for mobile: +http://bit.ly/html5-invasion + + +If you make an interesting for fork or enhancement of the game, let me know and it'll get +linked to here. This original repo will stay matching the code in the book. + +For more [HTML5 Game Development](http://www.html5gamedevelopment.org) resources, see: + +* [HTML5 Game Demos](http://www.html5gamedevelopment.org/html5-demos) +* [HTML5 GameDev Tutorials](http://www.html5gamedevelopment.org/html5-game-tutorials) +* [HTML5 Game Development News](http://www.html5gamedevelopment.org/html5-news) +* [HTML5 Game Engines](http://www.html5gamedevelopment.org/html5-engines) + + + diff --git a/space-shooter-plugin/space-shooter-resources/game/base.css b/space-shooter-plugin/space-shooter-resources/game/base.css new file mode 100644 index 0000000..779e2a8 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/base.css @@ -0,0 +1,62 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + + +/* Center the container */ +#container { + /*padding-top:50px;*/ + margin:0 auto; + /*width:480px;*/ +} + +/* Give canvas a background */ +canvas { + background-color:black; + +} diff --git a/space-shooter-plugin/space-shooter-resources/game/engine.js b/space-shooter-plugin/space-shooter-resources/game/engine.js new file mode 100644 index 0000000..deff7d3 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/engine.js @@ -0,0 +1,452 @@ +(function() { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = + window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + + if (!window.cancelAnimationFrame) + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; +}()); + + +var Game = new function() { + var boards = []; + + // Game Initialization + this.initialize = function(canvasElementId,sprite_data,callback) { + this.canvas = document.getElementById(canvasElementId); + + this.playerOffset = 10; + this.canvasMultiplier= 1; + this.setupMobile(); + + this.width = this.canvas.width; + this.height= this.canvas.height; + + this.ctx = this.canvas.getContext && this.canvas.getContext('2d'); + if(!this.ctx) { return alert("Please upgrade your browser to play"); } + + this.setupInput(); + + this.loop(); + + if(this.mobile) { + this.setBoard(4,new TouchControls()); + } + + SpriteSheet.load(sprite_data,callback); + }; + + + // Handle Input + var KEY_CODES = { 37:'left', 39:'right', 32 :'fire' }; + this.keys = {}; + + this.setupInput = function() { + window.addEventListener('keydown',function(e) { + if(KEY_CODES[e.keyCode]) { + Game.keys[KEY_CODES[e.keyCode]] = true; + e.preventDefault(); + } + },false); + + window.addEventListener('keyup',function(e) { + if(KEY_CODES[e.keyCode]) { + Game.keys[KEY_CODES[e.keyCode]] = false; + e.preventDefault(); + } + },false); + }; + + + var lastTime = new Date().getTime(); + var maxTime = 1/30; + // Game Loop + this.loop = function() { + var curTime = new Date().getTime(); + requestAnimationFrame(Game.loop); + var dt = (curTime - lastTime)/1000; + if(dt > maxTime) { dt = maxTime; } + + for(var i=0,len = boards.length;i<len;i++) { + if(boards[i]) { + boards[i].step(dt); + boards[i].draw(Game.ctx); + } + } + lastTime = curTime; + }; + + // Change an active game board + this.setBoard = function(num,board) { boards[num] = board; }; + + + this.setupMobile = function() { + var container = document.getElementById("container"), + hasTouch = !!('ontouchstart' in window), + w = window.innerWidth, h = window.innerHeight; + + if(hasTouch) { this.mobile = true; } + + if(screen.width >= 1280 || !hasTouch) { return false; } + + if(w > h) { + alert("Please rotate the device and then click OK"); + w = window.innerWidth; h = window.innerHeight; + } + + container.style.height = h*2 + "px"; + window.scrollTo(0,1); + + h = window.innerHeight + 2; + container.style.height = h + "px"; + container.style.width = w + "px"; + container.style.padding = 0; + + if(h >= this.canvas.height * 1.75 || w >= this.canvas.height * 1.75) { + this.canvasMultiplier = 2; + this.canvas.width = w / 2; + this.canvas.height = h / 2; + this.canvas.style.width = w + "px"; + this.canvas.style.height = h + "px"; + } else { + this.canvas.width = w; + this.canvas.height = h; + } + + this.canvas.style.position='absolute'; + this.canvas.style.left="0px"; + this.canvas.style.top="0px"; + + }; + +}; + + +var SpriteSheet = new function() { + this.map = { }; + + this.load = function(spriteData,callback) { + this.map = spriteData; + this.image = new Image(); + this.image.onload = callback; + this.image.src = 'images/sprites.png'; + }; + + this.draw = function(ctx,sprite,x,y,frame) { + var s = this.map[sprite]; + if(!frame) frame = 0; + ctx.drawImage(this.image, + s.sx + frame * s.w, + s.sy, + s.w, s.h, + Math.floor(x), Math.floor(y), + s.w, s.h); + }; + + return this; +}; + +var TitleScreen = function TitleScreen(title,subtitle,callback) { + var up = false; + this.step = function(dt) { + if(!Game.keys['fire']) up = true; + if(up && Game.keys['fire'] && callback) callback(); + }; + + this.draw = function(ctx) { + ctx.fillStyle = "#FFFFFF"; + + ctx.font = "bold 40px bangers"; + var measure = ctx.measureText(title); + ctx.fillText(title,Game.width/2 - measure.width/2,Game.height/2); + + ctx.font = "bold 20px bangers"; + var measure2 = ctx.measureText(subtitle); + ctx.fillText(subtitle,Game.width/2 - measure2.width/2,Game.height/2 + 40); + }; +}; + + +var GameBoard = function() { + var board = this; + + // The current list of objects + this.objects = []; + this.cnt = {}; + + // Add a new object to the object list + this.add = function(obj) { + obj.board=this; + this.objects.push(obj); + this.cnt[obj.type] = (this.cnt[obj.type] || 0) + 1; + return obj; + }; + + // Mark an object for removal + this.remove = function(obj) { + var idx = this.removed.indexOf(obj); + if(idx == -1) { + this.removed.push(obj); + return true; + } else { + return false; + } + }; + + // Reset the list of removed objects + this.resetRemoved = function() { this.removed = []; }; + + // Removed an objects marked for removal from the list + this.finalizeRemoved = function() { + for(var i=0,len=this.removed.length;i<len;i++) { + var idx = this.objects.indexOf(this.removed[i]); + if(idx != -1) { + this.cnt[this.removed[i].type]--; + this.objects.splice(idx,1); + } + } + }; + + // Call the same method on all current objects + this.iterate = function(funcName) { + var args = Array.prototype.slice.call(arguments,1); + for(var i=0,len=this.objects.length;i<len;i++) { + var obj = this.objects[i]; + obj[funcName].apply(obj,args); + } + }; + + // Find the first object for which func is true + this.detect = function(func) { + for(var i = 0,val=null, len=this.objects.length; i < len; i++) { + if(func.call(this.objects[i])) return this.objects[i]; + } + return false; + }; + + // Call step on all objects and them delete + // any object that have been marked for removal + this.step = function(dt) { + this.resetRemoved(); + this.iterate('step',dt); + this.finalizeRemoved(); + }; + + // Draw all the objects + this.draw= function(ctx) { + this.iterate('draw',ctx); + }; + + // Check for a collision between the + // bounding rects of two objects + this.overlap = function(o1,o2) { + return !((o1.y+o1.h-1<o2.y) || (o1.y>o2.y+o2.h-1) || + (o1.x+o1.w-1<o2.x) || (o1.x>o2.x+o2.w-1)); + }; + + // Find the first object that collides with obj + // match against an optional type + this.collide = function(obj,type) { + return this.detect(function() { + if(obj != this) { + var col = (!type || this.type & type) && board.overlap(obj,this); + return col ? this : false; + } + }); + }; + + +}; + +var Sprite = function() { }; + +Sprite.prototype.setup = function(sprite,props) { + this.sprite = sprite; + this.merge(props); + this.frame = this.frame || 0; + this.w = SpriteSheet.map[sprite].w; + this.h = SpriteSheet.map[sprite].h; +}; + +Sprite.prototype.merge = function(props) { + if(props) { + for (var prop in props) { + this[prop] = props[prop]; + } + } +}; + +Sprite.prototype.draw = function(ctx) { + SpriteSheet.draw(ctx,this.sprite,this.x,this.y,this.frame); +}; + +Sprite.prototype.hit = function(damage) { + this.board.remove(this); +}; + + +var Level = function(levelData,callback) { + this.levelData = []; + for(var i =0; i<levelData.length; i++) { + this.levelData.push(Object.create(levelData[i])); + } + this.t = 0; + this.callback = callback; +}; + +Level.prototype.step = function(dt) { + var idx = 0, remove = [], curShip = null; + + // Update the current time offset + this.t += dt * 1000; + + // Start, End, Gap, Type, Override + // [ 0, 4000, 500, 'step', { x: 100 } ] + while((curShip = this.levelData[idx]) && + (curShip[0] < this.t + 2000)) { + // Check if we've passed the end time + if(this.t > curShip[1]) { + remove.push(curShip); + } else if(curShip[0] < this.t) { + // Get the enemy definition blueprint + var enemy = enemies[curShip[3]], + override = curShip[4]; + + // Add a new enemy with the blueprint and override + this.board.add(new Enemy(enemy,override)); + + // Increment the start time by the gap + curShip[0] += curShip[2]; + } + idx++; + } + + // Remove any objects from the levelData that have passed + for(var i=0,len=remove.length;i<len;i++) { + var remIdx = this.levelData.indexOf(remove[i]); + if(remIdx != -1) this.levelData.splice(remIdx,1); + } + + // If there are no more enemies on the board or in + // levelData, this level is done + if(this.levelData.length === 0 && this.board.cnt[OBJECT_ENEMY] === 0) { + if(this.callback) this.callback(); + } + +}; + +Level.prototype.draw = function(ctx) { }; + + +var TouchControls = function() { + + var gutterWidth = 10; + var unitWidth = Game.width/5; + var blockWidth = unitWidth-gutterWidth; + + this.drawSquare = function(ctx,x,y,txt,on) { + ctx.globalAlpha = on ? 0.9 : 0.6; + ctx.fillStyle = "#CCC"; + ctx.fillRect(x,y,blockWidth,blockWidth); + + ctx.fillStyle = "#FFF"; + ctx.globalAlpha = 1.0; + ctx.font = "bold " + (3*unitWidth/4) + "px arial"; + + var txtSize = ctx.measureText(txt); + + ctx.fillText(txt, + x+blockWidth/2-txtSize.width/2, + y+3*blockWidth/4+5); + }; + + this.draw = function(ctx) { + ctx.save(); + + var yLoc = Game.height - unitWidth; + this.drawSquare(ctx,gutterWidth,yLoc,"\u25C0", Game.keys['left']); + this.drawSquare(ctx,unitWidth + gutterWidth,yLoc,"\u25B6", Game.keys['right']); + this.drawSquare(ctx,4*unitWidth,yLoc,"A",Game.keys['fire']); + + ctx.restore(); + }; + + this.step = function(dt) { }; + + this.trackTouch = function(e) { + var touch, x; + + e.preventDefault(); + Game.keys['left'] = false; + Game.keys['right'] = false; + for(var i=0;i<e.targetTouches.length;i++) { + touch = e.targetTouches[i]; + x = touch.pageX / Game.canvasMultiplier - Game.canvas.offsetLeft; + if(x < unitWidth) { + Game.keys['left'] = true; + } + if(x > unitWidth && x < 2*unitWidth) { + Game.keys['right'] = true; + } + } + + if(e.type == 'touchstart' || e.type == 'touchend') { + for(i=0;i<e.changedTouches.length;i++) { + touch = e.changedTouches[i]; + x = touch.pageX / Game.canvasMultiplier - Game.canvas.offsetLeft; + if(x > 4 * unitWidth) { + Game.keys['fire'] = (e.type == 'touchstart'); + } + } + } + }; + + Game.canvas.addEventListener('touchstart',this.trackTouch,true); + Game.canvas.addEventListener('touchmove',this.trackTouch,true); + Game.canvas.addEventListener('touchend',this.trackTouch,true); + + // For Android + Game.canvas.addEventListener('dblclick',function(e) { e.preventDefault(); },true); + Game.canvas.addEventListener('click',function(e) { e.preventDefault(); },true); + + Game.playerOffset = unitWidth + 20; +}; + + +var GamePoints = function() { + Game.points = 0; + + var pointsLength = 8; + + this.draw = function(ctx) { + ctx.save(); + ctx.font = "bold 18px arial"; + ctx.fillStyle= "#FFFFFF"; + + var txt = "" + Game.points; + var i = pointsLength - txt.length, zeros = ""; + while(i-- > 0) { zeros += "0"; } + + ctx.fillText(zeros + txt,10,20); + ctx.restore(); + + }; + + this.step = function(dt) { }; +}; diff --git a/space-shooter-plugin/space-shooter-resources/game/game.js b/space-shooter-plugin/space-shooter-resources/game/game.js new file mode 100644 index 0000000..7317f35 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/game.js @@ -0,0 +1,305 @@ +var sprites = { + ship: { sx: 0, sy: 0, w: 37, h: 42, frames: 1 }, + missile: { sx: 0, sy: 30, w: 2, h: 10, frames: 1 }, + enemy_purple: { sx: 37, sy: 0, w: 42, h: 43, frames: 1 }, + enemy_bee: { sx: 79, sy: 0, w: 37, h: 43, frames: 1 }, + enemy_ship: { sx: 116, sy: 0, w: 42, h: 43, frames: 1 }, + enemy_circle: { sx: 158, sy: 0, w: 32, h: 33, frames: 1 }, + explosion: { sx: 0, sy: 64, w: 64, h: 64, frames: 12 }, + enemy_missile: { sx: 9, sy: 42, w: 3, h: 20, frame: 1, } +}; + +var enemies = { + straight: { x: 0, y: -50, sprite: 'enemy_ship', health: 10, + E: 100 }, + ltr: { x: 0, y: -100, sprite: 'enemy_purple', health: 10, + B: 75, C: 1, E: 100, missiles: 2 }, + circle: { x: 250, y: -50, sprite: 'enemy_circle', health: 10, + A: 0, B: -100, C: 1, E: 20, F: 100, G: 1, H: Math.PI/2 }, + wiggle: { x: 100, y: -50, sprite: 'enemy_bee', health: 20, + B: 50, C: 4, E: 100, firePercentage: 0.001, missiles: 2 }, + step: { x: 0, y: -50, sprite: 'enemy_circle', health: 10, + B: 150, C: 1.2, E: 75 } +}; + +var OBJECT_PLAYER = 1, + OBJECT_PLAYER_PROJECTILE = 2, + OBJECT_ENEMY = 4, + OBJECT_ENEMY_PROJECTILE = 8, + OBJECT_POWERUP = 16; + +var startGame = function() { + var ua = navigator.userAgent.toLowerCase(); + + // Only 1 row of stars + if(ua.match(/android/)) { + Game.setBoard(0,new Starfield(50,0.6,100,true)); + } else { + Game.setBoard(0,new Starfield(20,0.4,100,true)); + Game.setBoard(1,new Starfield(50,0.6,100)); + Game.setBoard(2,new Starfield(100,1.0,50)); + } + Game.setBoard(3,new TitleScreen("Alien Invasion", + "Press fire to start playing", + playGame)); +}; + +var level1 = [ + // Start, End, Gap, Type, Override + [ 0, 4000, 500, 'step' ], + [ 6000, 13000, 800, 'ltr' ], + [ 10000, 16000, 400, 'circle' ], + [ 17800, 20000, 500, 'straight', { x: 50 } ], + [ 18200, 20000, 500, 'straight', { x: 90 } ], + [ 18200, 20000, 500, 'straight', { x: 10 } ], + [ 22000, 25000, 400, 'wiggle', { x: 150 }], + [ 22000, 25000, 400, 'wiggle', { x: 100 }] +]; + + + +var playGame = function() { + var board = new GameBoard(); + board.add(new PlayerShip()); + board.add(new Level(level1,winGame)); + Game.setBoard(3,board); + Game.setBoard(5,new GamePoints(0)); +}; + +var winGame = function() { + Game.setBoard(3,new TitleScreen("You win!", + "Press fire to play again", + playGame)); + parent.postMessage('GameWin', '*'); +}; + +var loseGame = function() { + Game.setBoard(3,new TitleScreen("You lose!", + "Press fire to play again", + playGame)); +}; + +var Starfield = function(speed,opacity,numStars,clear) { + + // Set up the offscreen canvas + var stars = document.createElement("canvas"); + stars.width = Game.width; + stars.height = Game.height; + var starCtx = stars.getContext("2d"); + + var offset = 0; + + // If the clear option is set, + // make the background black instead of transparent + if(clear) { + starCtx.fillStyle = "#000"; + starCtx.fillRect(0,0,stars.width,stars.height); + } + + // Now draw a bunch of random 2 pixel + // rectangles onto the offscreen canvas + starCtx.fillStyle = "#FFF"; + starCtx.globalAlpha = opacity; + for(var i=0;i<numStars;i++) { + starCtx.fillRect(Math.floor(Math.random()*stars.width), + Math.floor(Math.random()*stars.height), + 2, + 2); + } + + // This method is called every frame + // to draw the starfield onto the canvas + this.draw = function(ctx) { + var intOffset = Math.floor(offset); + var remaining = stars.height - intOffset; + + // Draw the top half of the starfield + if(intOffset > 0) { + ctx.drawImage(stars, + 0, remaining, + stars.width, intOffset, + 0, 0, + stars.width, intOffset); + } + + // Draw the bottom half of the starfield + if(remaining > 0) { + ctx.drawImage(stars, + 0, 0, + stars.width, remaining, + 0, intOffset, + stars.width, remaining); + } + }; + + // This method is called to update + // the starfield + this.step = function(dt) { + offset += dt * speed; + offset = offset % stars.height; + }; +}; + +var PlayerShip = function() { + this.setup('ship', { vx: 0, reloadTime: 0.25, maxVel: 200 }); + + this.reload = this.reloadTime; + this.x = Game.width/2 - this.w / 2; + this.y = Game.height - Game.playerOffset - this.h; + + this.step = function(dt) { + if(Game.keys['left']) { this.vx = -this.maxVel; } + else if(Game.keys['right']) { this.vx = this.maxVel; } + else { this.vx = 0; } + + this.x += this.vx * dt; + + if(this.x < 0) { this.x = 0; } + else if(this.x > Game.width - this.w) { + this.x = Game.width - this.w; + } + + this.reload-=dt; + if(Game.keys['fire'] && this.reload < 0) { + Game.keys['fire'] = false; + this.reload = this.reloadTime; + + this.board.add(new PlayerMissile(this.x,this.y+this.h/2)); + this.board.add(new PlayerMissile(this.x+this.w,this.y+this.h/2)); + } + }; +}; + +PlayerShip.prototype = new Sprite(); +PlayerShip.prototype.type = OBJECT_PLAYER; + +PlayerShip.prototype.hit = function(damage) { + if(this.board.remove(this)) { + loseGame(); + } +}; + + +var PlayerMissile = function(x,y) { + this.setup('missile',{ vy: -700, damage: 10 }); + this.x = x - this.w/2; + this.y = y - this.h; +}; + +PlayerMissile.prototype = new Sprite(); +PlayerMissile.prototype.type = OBJECT_PLAYER_PROJECTILE; + +PlayerMissile.prototype.step = function(dt) { + this.y += this.vy * dt; + var collision = this.board.collide(this,OBJECT_ENEMY); + if(collision) { + collision.hit(this.damage); + this.board.remove(this); + } else if(this.y < -this.h) { + this.board.remove(this); + } +}; + + +var Enemy = function(blueprint,override) { + this.merge(this.baseParameters); + this.setup(blueprint.sprite,blueprint); + this.merge(override); +}; + +Enemy.prototype = new Sprite(); +Enemy.prototype.type = OBJECT_ENEMY; + +Enemy.prototype.baseParameters = { A: 0, B: 0, C: 0, D: 0, + E: 0, F: 0, G: 0, H: 0, + t: 0, reloadTime: 0.75, + reload: 0 }; + +Enemy.prototype.step = function(dt) { + this.t += dt; + + this.vx = this.A + this.B * Math.sin(this.C * this.t + this.D); + this.vy = this.E + this.F * Math.sin(this.G * this.t + this.H); + + this.x += this.vx * dt; + this.y += this.vy * dt; + + var collision = this.board.collide(this,OBJECT_PLAYER); + if(collision) { + collision.hit(this.damage); + this.board.remove(this); + } + + if(Math.random() < 0.01 && this.reload <= 0) { + this.reload = this.reloadTime; + if(this.missiles == 2) { + this.board.add(new EnemyMissile(this.x+this.w-2,this.y+this.h)); + this.board.add(new EnemyMissile(this.x+2,this.y+this.h)); + } else { + this.board.add(new EnemyMissile(this.x+this.w/2,this.y+this.h)); + } + + } + this.reload-=dt; + + if(this.y > Game.height || + this.x < -this.w || + this.x > Game.width) { + this.board.remove(this); + } +}; + +Enemy.prototype.hit = function(damage) { + this.health -= damage; + if(this.health <=0) { + if(this.board.remove(this)) { + Game.points += this.points || 100; + this.board.add(new Explosion(this.x + this.w/2, + this.y + this.h/2)); + } + } +}; + +var EnemyMissile = function(x,y) { + this.setup('enemy_missile',{ vy: 200, damage: 10 }); + this.x = x - this.w/2; + this.y = y; +}; + +EnemyMissile.prototype = new Sprite(); +EnemyMissile.prototype.type = OBJECT_ENEMY_PROJECTILE; + +EnemyMissile.prototype.step = function(dt) { + this.y += this.vy * dt; + var collision = this.board.collide(this,OBJECT_PLAYER) + if(collision) { + collision.hit(this.damage); + this.board.remove(this); + } else if(this.y > Game.height) { + this.board.remove(this); + } +}; + + + +var Explosion = function(centerX,centerY) { + this.setup('explosion', { frame: 0 }); + this.x = centerX - this.w/2; + this.y = centerY - this.h/2; +}; + +Explosion.prototype = new Sprite(); + +Explosion.prototype.step = function(dt) { + this.frame++; + if(this.frame >= 12) { + this.board.remove(this); + } +}; + +window.addEventListener("load", function() { + parent.postMessage('GameInit', '*'); + Game.initialize("game",sprites,startGame); +}); + + diff --git a/space-shooter-plugin/space-shooter-resources/game/game.manifest b/space-shooter-plugin/space-shooter-resources/game/game.manifest new file mode 100644 index 0000000..78aa44c --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/game.manifest @@ -0,0 +1,14 @@ +CACHE MANIFEST + +# v1.02 +CACHE: +index.html +engine.js +game.js +images/sprites.png +base.css + +NETWORK: +http://fonts.googleapis.com/ +http://www.google-analytics.com/ +http://themes.googleusercontent.com/ diff --git a/space-shooter-plugin/space-shooter-resources/game/images/sprites.png b/space-shooter-plugin/space-shooter-resources/game/images/sprites.png new file mode 100644 index 0000000..78c9175 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/images/sprites.png Binary files differ diff --git a/space-shooter-plugin/space-shooter-resources/game/index.html b/space-shooter-plugin/space-shooter-resources/game/index.html new file mode 100644 index 0000000..c1f8362 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/game/index.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html lang="en"> +<head> + <meta charset="UTF-8"/> + <title>Alien Invasion</title> + <link rel="stylesheet" href="base.css" type="text/css" /> + <link href='http://fonts.googleapis.com/css?family=Bangers' rel='stylesheet' type='text/css'> + <meta name="viewport" content="width=device-width, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"/> + <meta name="apple-mobile-web-app-capable" content="yes"> + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> +</head> +<body> + <div id='container'> + <canvas id='game' width='320' height='480'></canvas> + </div> + <script src='engine.js'></script> + <script src='game.js'></script> +</body> +</html> + + diff --git a/space-shooter-plugin/space-shooter-resources/modernizr.custom.js b/space-shooter-plugin/space-shooter-resources/modernizr.custom.js new file mode 100644 index 0000000..1b40fa6 --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/modernizr.custom.js @@ -0,0 +1,4 @@ +/* Modernizr 2.6.2 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-canvas-canvastext-localstorage + */ +;window.Modernizr=function(a,b,c){function t(a){i.cssText=a}function u(a,b){return t(prefixes.join(a+";")+(b||""))}function v(a,b){return typeof a===b}function w(a,b){return!!~(""+a).indexOf(b)}function x(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:v(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=b.documentElement,g="modernizr",h=b.createElement(g),i=h.style,j,k={}.toString,l={},m={},n={},o=[],p=o.slice,q,r={}.hasOwnProperty,s;!v(r,"undefined")&&!v(r.call,"undefined")?s=function(a,b){return r.call(a,b)}:s=function(a,b){return b in a&&v(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=p.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(p.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(p.call(arguments)))};return e}),l.canvas=functi! on(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},l.canvastext=function(){return!!e.canvas&&!!v(b.createElement("canvas").getContext("2d").fillText,"function")},l.localstorage=function(){try{return localStorage.setItem(g,g),localStorage.removeItem(g),!0}catch(a){return!1}};for(var y in l)s(l,y)&&(q=y.toLowerCase(),e[q]=l[y](),o.push((e[q]?"":"no-")+q));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)s(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof enableClasses!="undefined"&&enableClasses&&(f.className+=" "+(b?"":"no-")+a),e[a]=b}return e},t(""),h=j=null,e._version=d,e}(this,this.document); \ No newline at end of file diff --git a/space-shooter-plugin/space-shooter-resources/plugin.html b/space-shooter-plugin/space-shooter-resources/plugin.html new file mode 100644 index 0000000..e16dacc --- /dev/null +++ b/space-shooter-plugin/space-shooter-resources/plugin.html @@ -0,0 +1,217 @@ +<!doctype html> +<!-- + oVirt Space Shooter: plugin host page + contains plugin code that interacts with plugin API +--> +<html> +<head> + <meta charset="utf-8"> + <!-- Use Modernizr for detecting necessary HTML5 features like Canvas and Local Storage --> + <script src='modernizr.custom.js'></script> +</head> +<body> + +<script type='text/javascript'> + + // Get API object for 'space-shooter' plugin + // Note: using 'parent' due to plugin code being evaluated within an iframe context + var api = parent.pluginApi('space-shooter'); + + // Get runtime plugin configuration, i.e. custom configuration (if any) + // merged on top of default configuration (if any) + var config = api.configObject(); + + // Customize API options that affect specific features of plugin API + api.options({ + // Configure source origin(s), i.e. protocol://domain:port + // from which HTML5 message events will be accepted + allowedMessageOrigins: config.allowedOrigins + }); + + var victory = false; // Has player won at least one game in the current game dialog? + var dataCenter = null; // Data Center entity associated with the current game dialog + + var gameContentWindow = null; // Reference to game Window object + var subTabWindow = null; // Reference to 'Score' sub tab Window object + + var dialogToken = 'space-shooter-dialog'; + var subTabToken = 'space-shooter-dc-score'; + + var dialogUrl = 'plugin/space-shooter/game/index.html'; + var subTabUrl = 'plugin/space-shooter/dc-score.html'; + + var init = function() { + // Add new action button to Data Center main tab + api.addMainTabActionButton('DataCenter', 'Protect DataCenter from Alien Invasion', + // Extra options for addMainTabActionButton function + { + isEnabled: function() { + // Enable button only when selecting single Data Center + return arguments.length == 1; + }, + onClick: function() { + // Reset victory flag and open new game dialog + victory = false; + openDialog(); + }, + location: 'OnlyFromContext' // Button available only from context menu + } + ); + + // Add new 'Score' sub tab under Data Center main tab + api.addSubTab('DataCenter', 'Space Shooter Score', subTabToken, subTabUrl); + }; + + var openDialog = function() { + // Show modal dialog with actual game content + api.showDialog(dataCenter.name + ' under attack', dialogToken, dialogUrl, + '340px', '560px', + // Extra options for showDialog function + { + closeIconVisible: false, // Hide dialog's close icon + closeOnEscKey: false, // Prevent user from closing dialog with Escape key + resizeEnabled: true, // Allow dialog resizing + buttons: + [ + // Button for those who cannot beat the game (it can happen!) + { + label: 'Cheat', + onClick: cheatGame + }, + // Button to close the dialog + { + label: 'Get me outta here', + onClick: closeDialog + } + ] + }); + }; + + var closeDialog = function() { + if (victory) { + api.closeDialog(dialogToken); + } else { + alert('Not so fast! Destroy the aliens at least once.'); + } + }; + + var cheatGame = function() { + if (gameContentWindow != null) { + // Invoke function on game Window object + gameContentWindow.winGame(); + } + }; + + var getKey = function() { + // Calculate unique key (string) for storing and retrieving game score for + // given user + Data Center combination, i.e. admin@internal_dc123_DCScore + return dataCenter != null ? api.loginUserName() + "_" + dataCenter.id + "_DCScore" : null; + }; + + var getScore = function() { + // Return current score as string, or 0 if there is no score yet + return localStorage.getItem(getKey()) || '0'; + }; + + var incScore = function() { + // Increment current score by 1, store the result as string + localStorage.setItem(getKey(), (parseInt(getScore(), 10) + 1) + ''); + }; + + var updateSubTab = function() { + if (subTabWindow == null || dataCenter == null) { + return; + } + + // Get the current user score + var score = getScore(); + var intScore = parseInt(score, 10); + + // Calculate rank based on user score + var rank, rankColor; + switch (true) { + case (intScore >= 10): + rank = 'Laser Master'; + rankColor = 'green'; + break; + case (intScore >= 3): + rank = 'Veteran'; + rankColor = 'orange'; + break; + case (intScore >= 1): + rank = 'Survivor'; + rankColor = 'red'; + break; + default: + rank = 'Newbie'; + rankColor = 'gray'; + break; + } + + // Update 'Score' sub tab + subTabWindow.update(dataCenter.name, score, rank, rankColor); + }; + + var browserRocks = function() { + // Detect necessary HTML5 features via Modernizr + return Modernizr.canvas && Modernizr.canvastext && Modernizr.localstorage; + }; + + // Register event handler functions for later invocation by UI plugin infrastructure + api.register({ + + // Called by the infrastructure as part of plugin initialization, + // will be called just once during the lifetime of a plugin + UiInit: function() { + if (browserRocks()) { + init(); + } + }, + + // Called when item selection changes in Data Center main tab, + // useful for keeping track of currently selected Data Center entity + DataCenterSelectionChange: function() { + if (arguments.length == 1) { + // Single entity was selected, remember it and make 'Score' sub tab visible + dataCenter = arguments[0]; + api.setTabAccessible(subTabToken, true); + } else { + // Otherwise, forget the entity and hide 'Score' sub tab + dataCenter = null; + api.setTabAccessible(subTabToken, false); + } + }, + + // Called when another Window (i.e. custom UI contributed by a plugin) + // sends message to WebAdmin Window via HTML5 postMessage API, + // ideal for cross-window communication that works across different origins + MessageReceived: function(data, sourceWindow) { + // If we get here, we already passed allowed source origin check + switch (data) { + // Game content just got loaded + case 'GameInit': + gameContentWindow = sourceWindow; + break; + // User just won the game + case 'GameWin': + victory = true; + incScore(); + updateSubTab(); + break; + // 'Score' sub tab asks for current user score + case 'GetDataCenterScore': + subTabWindow = sourceWindow; + updateSubTab(); + break; + } + } + + }); + + // Tell plugin infrastructure that we are good to go, expect UiInit callback + api.ready(); + +</script> + +</body> +</html> diff --git a/space-shooter-plugin/space-shooter.json b/space-shooter-plugin/space-shooter.json new file mode 100644 index 0000000..36f6741 --- /dev/null +++ b/space-shooter-plugin/space-shooter.json @@ -0,0 +1,19 @@ +{ + + // Unique name of the plugin + "name": "space-shooter", + + // URL of plugin host page that bootstraps plugin code + // This URL maps to $ENGINE_USR/ui-plugins/space-shooter-resources/plugin.html + "url": "plugin/space-shooter/plugin.html", + + // Path to plugin resource files relative to descriptor + // This path maps to $ENGINE_USR/ui-plugins/space-shooter-resources + "resourcePath": "space-shooter-resources", + + // Default configuration associated with the plugin + "config": { + "allowedOrigins": ["http://127.0.0.1:8080"] + } + +} -- To view, visit http://gerrit.ovirt.org/23813 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I347b18ca57b7b41e4cdf42e528cf5a7aff60c583 Gerrit-PatchSet: 1 Gerrit-Project: samples-uiplugins Gerrit-Branch: master Gerrit-Owner: Vojtech Szocs <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
